/*
* compress.cpp (C) 2006-2008, Aurélien Croc (AP²C)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Id: compress.cpp 301 2012-02-29 14:11:09Z tillkamppeter $
*
*/
#include "compress.h"
#include <math.h>
#include <string.h>
#include "page.h"
#include "band.h"
#include "errlog.h"
#include "request.h"
#include "bandplane.h"
#include "algo0x0d.h"
#include "algo0x0e.h"
#include "algo0x11.h"
#include "algo0x13.h"
#include "algo0x15.h"
static bool _isEmptyBand(unsigned char* band, unsigned long size)
{
unsigned long max, mod;
max = size / sizeof(unsigned long);
mod = size % sizeof(unsigned long);
for (unsigned long i=0; i < max; i++) {
if (((unsigned long*)band)[i])
return false;
}
for (unsigned long i=0; i < mod; i++)
if (band[size-i-1])
return false;
return true;
}
static bool _compressBandedPage(const Request& request, Page* page)
{
unsigned long index=0, pageHeight, pageWidth, lineWidthInB, bandHeight;
unsigned long bandSize, hardMarginX, hardMarginXInB, hardMarginY;
unsigned char *planes[4], *band;
unsigned long bandNumber=0;
unsigned char colors;
colors = page->colorsNr();
hardMarginX = ((unsigned long)ceil(page->convertToXResolution(request.
printer()->hardMarginX())) + 7) & ~7;
hardMarginY = ceil(page->convertToYResolution(request.printer()->
hardMarginY()));
hardMarginXInB = hardMarginX / 8;
pageWidth = page->width();
pageHeight = page->height() - hardMarginY;
page->setWidth(pageWidth);
page->setHeight(pageHeight);
lineWidthInB = (pageWidth + 7) / 8;
bandHeight = request.printer()->bandHeight();
if (page->xResolution() == 300 && page->yResolution() == 300)
bandHeight /= 2;
bandSize = lineWidthInB * bandHeight;
index = hardMarginY * lineWidthInB;
band = new unsigned char[bandSize];
for (unsigned int i=0; i < colors; i++)
planes[i] = page->planeBuffer(i);
/*
* 1. On vérifier si la bande n'est pas blanche
* |-> Si bande blanche, on passe
* '-> Sinon, on compresse
* 2. On rajoute les informations de bande (numéro de bande et de
* couleur).
* 3. On enregistre la bande dans la page.
* 4. On détruit les buffers de plans dans la page.
*/
while (pageHeight) {
unsigned long localHeight = bandHeight;
Band *current = NULL;
bool theEnd = false;
// Special things to do for the last band
if (pageHeight < bandHeight) {
theEnd = true;
localHeight = pageHeight;
memset(band, 0, bandSize);
}
for (unsigned int i=0; i < colors; i++) {
Algorithm *algo = NULL;
BandPlane *plane;
switch (page->compression()) {
case 0x0D:
algo = new Algo0x0D;
break;
case 0x0E:
algo = new Algo0x0E;
break;
case 0x11:
algo = new Algo0x11;
break;
default:
ERRORMSG(_("Unknown compression algorithm. Aborted"));
return false;
}
// Copy the data into the band depending on the algorithm options
if (algo->reverseLineColumn()) {
for (unsigned int y=0; y < localHeight; y++) {
for (unsigned int x=0; x < lineWidthInB - hardMarginXInB; x++) {
band[x * bandHeight + y] = planes[i][index + x +
hardMarginXInB + y * lineWidthInB];
}
for (unsigned int x=lineWidthInB - hardMarginXInB; x < lineWidthInB; x++)
band[x * bandHeight + y] = 0;
}
} else {
for (unsigned int y=0; y < localHeight; y++) {
for (unsigned int x=0; x < lineWidthInB - hardMarginXInB; x++) {
band[x + y * lineWidthInB] = planes[i][index + x +
hardMarginXInB + y * lineWidthInB];
}
for (unsigned int x=lineWidthInB - hardMarginXInB; x < lineWidthInB; x++)
band[x + y * lineWidthInB] = 0;
}
}
// Does the band is empty?
if (_isEmptyBand(band, bandSize)) {
delete algo;
continue;
}
// Check if bytes have to be reversed
if (algo->inverseByte())
for (unsigned int j=0; j < bandSize; j++)
band[j] = ~band[j];
// Call the compression method
plane = algo->compress(request, band, pageWidth, bandHeight);
/*
* If algorithm 0xd did not create a plane, it means that the
* complementary algorithm 0xE need to be used
*/
if (!plane && page->compression() == 0x0D) {
delete algo;
algo = new Algo0x0E;
/* Bytes has to be reversed first, as algo0xd didn't do that. */
for (unsigned int j = 0; j < bandSize; j++)
band[j] = ~band[j];
/* Do the encoding with algo0xe. */
plane = algo->compress(request, band, pageWidth, bandHeight);
}
if (plane) {
plane->setColorNr(i + 1);
if (!current)
current = new Band(bandNumber, pageWidth, bandHeight);
current->registerPlane(plane);
}
delete algo;
}
if (current)
page->registerBand(current);
bandNumber++;
index += bandSize;
pageHeight = theEnd ? 0 : pageHeight - bandHeight;
}
page->flushPlanes();
delete[] band;
return true;
}
#ifndef DISABLE_JBIG
static bool _compressBandedJBIGPage(const Request& request, Page* page)
{
unsigned long index=0, pageHeight, lineWidthInB, bandHeight = 128;
unsigned long bufferWidth, bandSize, hardMarginXInB=13, hardMarginY=100;
unsigned char *planes[4], *band[4];
unsigned long bandNumber=0, xLimitInB, bufferWidthInB;
Algo0x15 *algo = new Algo0x15;
/* Image trimming are done from hardware margins defined in the ppd. */
hardMarginXInB = ((unsigned long)ceil(page->convertToXResolution(request.
printer()->hardMarginX())) + 7) / 8;
hardMarginY = ceil(page->convertToYResolution(request.printer()->
hardMarginY()));
bandHeight = request.printer()->bandHeight();
lineWidthInB = ((page->width()) + 7) / 8;
// Compute an updated page height, clipped on top.
pageHeight = page->height() - hardMarginY;
// Compute the buffer width that is nearest multiple of 256 to the original.
bufferWidth = page->width() & ~255;
if ((bufferWidth + 128) < page->width())
bufferWidth += 256;
// Update the page heigth.
page->setHeight(pageHeight);
// Update the page width.
page->setWidth(bufferWidth);
bufferWidthInB = (bufferWidth + 7) / 8;
bandSize = bufferWidthInB * bandHeight;
index = hardMarginY * lineWidthInB;
for (unsigned int i=0; i < page->colorsNr(); i++) {
band[i] = new unsigned char[bandSize];
planes[i] = page->planeBuffer(i);
}
/*
Here, limit the width of the copied image to the buffer, as the buffer
width varies and can lead to 6 practical cases, the following is a
condensed code.
*/
if (hardMarginXInB + bufferWidthInB < lineWidthInB - hardMarginXInB)
xLimitInB = hardMarginXInB + bufferWidthInB;
else
xLimitInB = lineWidthInB - hardMarginXInB;
bool theEnd = false;
unsigned long indexSizeIncrement = bandHeight * lineWidthInB;
unsigned long localHeight = bandHeight;
while (pageHeight) {
Band *current = NULL;
bool cmyPlanesHasData = false;
// Special things to do for the last band
if (pageHeight < bandHeight) {
theEnd = true;
localHeight = pageHeight;
for (unsigned int i=0; i < page->colorsNr(); i++)
memset(band[i], 0, bandSize);
}
for (unsigned int i=0; i < page->colorsNr(); i++) {
for (unsigned int y=0; y < localHeight; y++) {
for (unsigned int x=hardMarginXInB; x < xLimitInB; x++)
band[i][x - hardMarginXInB + y * bufferWidthInB] =
planes[i][index + x + y * lineWidthInB];
for (unsigned int x=lineWidthInB - 2 * hardMarginXInB;
x < bufferWidthInB; x++)
band[i][x + y * bufferWidthInB] = 0;
}
}
// Are the CMY planes completely empty in the band?
for (unsigned int i=0; i < page->colorsNr() - 1; i++)
if (!_isEmptyBand(band[i], bandSize)) {
cmyPlanesHasData = true;
break;
};
// Compress the entire band.
if (cmyPlanesHasData) {
for (unsigned int i=0; i < page->colorsNr(); i++) {
BandPlane *plane = algo->compress(request, band[i],
bufferWidth, bandHeight);
if (plane) {
plane->setColorNr((1 == page->colorsNr()) ? 4:i + 1);
if (!current)
current = new Band(bandNumber, bufferWidth, bandHeight);
current->registerPlane(plane);
}
}
} else if (!_isEmptyBand(band[page->colorsNr() - 1], bandSize)) {
// Compress only the K band.
BandPlane *plane = algo->compress(request,
band[page->colorsNr() - 1],
bufferWidth, bandHeight);
if (plane) {
plane->setColorNr(4);
if (!current)
current = new Band(bandNumber, bufferWidth, bandHeight);
current->registerPlane(plane);
}
}
if (current)
page->registerBand(current);
bandNumber++;
index += indexSizeIncrement;
pageHeight = theEnd ? 0 : pageHeight - bandHeight;
}
if (page->bandsNr() > 0)
page->setBIH(algo->getBIHdata());
page->flushPlanes();
for (unsigned int i=0; i < page->colorsNr(); i++)
delete[] band[i];
delete algo;
return true;
}
static bool _compressWholePage(const Request& request, Page* page)
{
unsigned long hardMarginX, hardMarginXInB, hardMarginY, lineWidthInB;
unsigned long pageWidth, bandHeight, planeHeight, pageHeight, index;
unsigned long bandNumber=0;
unsigned char *buffer;
Band *current = NULL;
Algo0x13 algo[4];
hardMarginX = ((unsigned long)ceil(page->convertToXResolution(request.
printer()->hardMarginX())) + 7) & ~7;
hardMarginY = ceil(page->convertToYResolution(request.printer()->
hardMarginY()));
hardMarginXInB = hardMarginX / 8;
pageWidth = page->width() - hardMarginX * 2;
pageHeight = page->height() - hardMarginY * 2;
page->setWidth(pageWidth);
page->setHeight(pageHeight);
lineWidthInB = (pageWidth + 7) / 8;
bandHeight = request.printer()->bandHeight();
// Alignment of the page height on band height
planeHeight = ((pageHeight + bandHeight - 1) / bandHeight) * bandHeight;
buffer = new unsigned char[lineWidthInB * planeHeight];
do {
current = NULL;
for (unsigned int i=0; i < page->colorsNr(); i++) {
unsigned char *curPlane = page->planeBuffer(i);
BandPlane *plane;
index = hardMarginY * (lineWidthInB + 2 * hardMarginXInB) +
hardMarginXInB;
for (unsigned int y=0; y < pageHeight; y++,
index += 2 * hardMarginXInB) {
for (unsigned int x=0; x < lineWidthInB; x++, index++) {
buffer[x + y * lineWidthInB] = curPlane[index];
}
}
memset(buffer + pageHeight * lineWidthInB, 0,
(planeHeight - pageHeight) * lineWidthInB);
// Call the compression method
plane = algo[i].compress(request, buffer, page->width(),
planeHeight);
if (plane) {
plane->setColorNr(i + 1);
if (!current)
current = new Band(bandNumber, page->width(),
request.printer()->bandHeight());
current->registerPlane(plane);
}
}
if (current)
page->registerBand(current);
bandNumber++;
} while (current);
page->flushPlanes();
delete[] buffer;
return true;
}
#endif /* DISABLE_JBIG */
bool compressPage(const Request& request, Page* page)
{
switch(page->compression()) {
case 0x0D:
case 0x0E:
case 0x11:
return _compressBandedPage(request, page);
case 0x13:
#ifndef DISABLE_JBIG
return _compressWholePage(request, page);
#else
ERRORMSG(_("J-BIG compression algorithm has been disabled during "
"the compilation. Please recompile SpliX and enable the "
"J-BIG compression algorithm."));
break;
#endif /* DISABLE_JBIG */
case 0x15:
#ifndef DISABLE_JBIG
return _compressBandedJBIGPage(request, page);
#else
ERRORMSG(_("J-BIG compression algorithm has been disabled during "
"the compilation. Please recompile SpliX and enable the "
"J-BIG compression algorithm."));
break;
#endif /* DISABLE_JBIG */
default:
ERRORMSG(_("Compression algorithm 0x%lX does not exist"),
page->compression());
}
return false;
}
/* vim: set expandtab tabstop=4 shiftwidth=4 smarttab tw=80 cin enc=utf8: */