/*
* algo0x0e.cpp SpliX is Copyright 2006-2008 by Aurélien Croc
* This file is a SpliX derivative work
*
* 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.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Id: algo0x0e.cpp 245 2008-11-24 23:00:43Z ap2c $
*
* --
* This code is written by Leonardo Hamada
*/
#include "algo0x0e.h"
#include <new>
#include <string.h>
#include "errlog.h"
#include "request.h"
#include "printer.h"
#include "bandplane.h"
#define getData(x) data[(x)]
#define setOutputData(y,z) output[(y)]=(z)
#define codecR(R, V, n) \
addReplicativeRun(output, outputSize, R, V)
#define codecL(X, L, B, n) \
addLiteralSequence(output, outputSize, data, X, L, B)
/*
* Constructor
*/
Algo0x0E::Algo0x0E()
{
}
Algo0x0E::~Algo0x0E()
{
}
inline void Algo0x0E::addLiteralSequence(
unsigned char * output,
unsigned long & outputSize,
unsigned char * data,
unsigned long position,
unsigned long length,
unsigned long blanks )
{
/* Set control value for the literal chunk length. */
unsigned long tmp = length + blanks - 1;
unsigned long w;
output[ outputSize++ ] = 0x80 | (unsigned char)(tmp >> 8);
output[ outputSize++ ] = (unsigned char)tmp;
/* Copy literal data chunk. */
for(w=0;w<length;w++){
output[outputSize++] = data[position + w];
}
/* Pad with required blanks. */
for(w=0;w<blanks;w++){
data[outputSize++] = 0xff;
}
}
inline void Algo0x0E::addReplicativeRun(
unsigned char * output,
unsigned long & outputSize,
unsigned long runs,
unsigned char value )
{
/* Verify run numbers to choose appropriate control header. */
if ( 65 >= runs ) {
output[ outputSize++ ] = 0x7f & (unsigned char)(1L - runs);
} else {
unsigned long t = 0xffff - runs + 2;
output[ outputSize++ ] = (unsigned char)(t >> 8);
output[ outputSize++ ] = (unsigned char)t;
}
/* Set value to be replicated. */
output[ outputSize++ ] = value;
}
/* Check if segment at 'e' position and forward can be encoded as
consecutive runs. 'L' limits the width of the seek. */
unsigned long Algo0x0E::verifyGain(unsigned long e,
unsigned long L,
unsigned char * data)
{
unsigned long g=0, u=1;
while(e+u<L){
gain_seek:
if(getData(e+u-1)==getData(e+u)){
if(++u==4){
break;
}
}else{
break;
};
}
if(u>=2){
g+=(u<=65)?(u-2):(u-3);
if(g<2){
e+=u;
if(e+1<L){
u=1;
goto gain_seek;
}
}
}
return g;
}
unsigned long Algo0x0E::encodeReplications(unsigned long q,
unsigned long L,
unsigned char * data,
unsigned char * output,
unsigned long & outputSize)
{
unsigned long r;
runs_enc: r=1;
while(q+r<L){
if(getData(q+r-1)==getData(q+r)){
r++;
}else{
break;
}
}
if(r>=2){
codecR(r,getData(q),0);
q+=r;
goto runs_enc;
}
return q;
}
unsigned long Algo0x0E::locateBackwardReplications(unsigned long L,
unsigned char * data)
{
/* This must be signed.*/
long int i=L-1, r;
seek_literal2: r=1;
while(i-r>=0){
if(data[i-r+1]==data[i-r]){
r++;
}else{
break;
}
}
if(r>1){
i=i-r;
goto seek_literal2;
}
return i+1;
}
BandPlane * Algo0x0E::compress(const Request & request, unsigned char *data,
unsigned long width, unsigned long height)
{
/* Basic parameters validation. */
if ( !data || !height || !width ) {
ERRORMSG(_("Invalid given data for compression: 0xe"));
return NULL;
}
/* We will interpret the band heigth of 128 pixels as 600 DPI printing
request. Likewise, height of 64 pixels as 300 DPI printing. */
if ( ! ( 128 == height || 64 == height ) ) {
ERRORMSG(_("Invalid band height for compression: 0xe"));
return NULL;
}
/* The row-bytes of the bitmap. */
const unsigned long rowBytes = ( width + 7 ) / 8;
/* This is the allowed raw data size per scan-line. */
const unsigned long maxWorkRb = ( 0x40 == height ) ?
0x09A0 / 8 : 0x1360 / 8;
/* If rowBytes is larger than allowed size, print will be cropped. */
const unsigned long workRb = ( rowBytes > maxWorkRb ) ?
maxWorkRb : rowBytes;
unsigned char * output = NULL;
try {
/* Estimate a buffer size equal to 2-byte control header overhead +
maxWorkRb, times the bitmap height + up to 3-byte padding at end. */
output = new unsigned char[ ( 2 + maxWorkRb ) * height + 3 ];
} catch( std::bad_alloc & ){
/* Catch error if buffer creation fails. */
ERRORMSG(_("Could not allocate work buffer for encoding: 0xe"));
return NULL;
}
/* Keep track of encoded data size. */
unsigned long outputSize = 0;
/* Main encoding loop for each scan-line.
Top to bottom scan-line processing. */
while(true){
/*
i: index into data.
F: last replication encodable marker.
E: resized WorkRb per scan-line.
B: blank paddings.
*/
unsigned long i, F, E, B;
/* Adjust this working scan-line size
up to where there is no blank bytes on the right end. */
for(E=workRb;(E>0)&&(getData(E-1)==0xff);E--)
/* Empty statement. */
;
/* Determine the number of padding blank bytes to the right
end of the scan-line relative to constant max. width. */
B=maxWorkRb-E;
/* If scan-line is not blank and the number of blank padding is
not 1, seek the beginning position of the last scan-line segment,
when it can be encoded as contiguous replication runs. */
F=((E>0)&&(B!=1))?locateBackwardReplications(E,data):E;
/* Try to encode the first segment as replication runs. */
i=(F>0)?encodeReplications(0,F,data,output,outputSize):0;
/* Continue to encode the rest of data as replication or literal
segment chunks as appropriate. */
/* l: length of cumulative literal segment.*/
unsigned long l=0;
while(i+l+1<F){
if(getData(i+l+1)!=getData(i+l)){
l++;
}else{
if(verifyGain(i+l,F,data)>=2){
codecL(i,l,0,1);
i=encodeReplications(i+l,F,data,output,outputSize);
l=0;
}else{
l++;
}
}
}
/* Encode last remaining segments and white paddings. */
if(F<E){
/* Encode previous literal segment that remains unencoded. */
if(i<F){
codecL(i,F-i,0,2);
}
/* Encode the last non blank replication runs. */
i=encodeReplications(F,E,data,output,outputSize);
/* Ends a scan-line with required white paddings */
if(B>=2){
codecR(B,0xff,3);
}
}else{
/* When the end of non blank data of a scan-line does not
end with replication runs, encode with literal sequence. */
if(B>=2){
/* Encode previous literal segment that remains unencoded. */
if(i<E){
codecL(i,E-i,0,4);
}
/* Ends a scan-line with required white paddings. */
codecR(B,0xff,5);
}else if((B>0)||(i<E)){
/* Ends a scan-line encoding with a
literal sequence with blanks if any. */
codecL(i,E-i,B,6);
}
}
if( --height>0 ){
/* Proceed to the next scan-line. */
data = & data[ rowBytes ];
}else{
break;
}
}
/* Zero value byte padding for data size alignment to 4-bytes bounds. */
unsigned long zerosPad = 4 - ( outputSize % 4 );
/* Check if it is already aligned. */
if ( 4 > zerosPad ){
while ( zerosPad-- ){
output[ outputSize++ ] = 0;
}
}
/* Prepare to return data encoded by algorithm 0xe. */
BandPlane * plane = new BandPlane();
/* Regsiter data and its size. */
plane->setData( output, outputSize );
plane->setEndian( BandPlane::Dependant );
/* Set this band encoding type. */
plane->setCompression( 0xe );
DEBUGMSG(_("Finished band encoding: type=0xe, size=%lu"), outputSize);
/* Bye-bye. */
return plane;
}