//C- -*- C++ -*- //C- ------------------------------------------------------------------- //C- DjVuLibre-3.5 //C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. //C- Copyright (c) 2001 AT&T //C- //C- This software is subject to, and may be distributed under, the //C- GNU General Public License, either Version 2 of the license, //C- or (at your option) any later version. The license should have //C- accompanied the software or you may obtain a copy of the license //C- from the Free Software Foundation at http://www.fsf.org . //C- //C- This program is distributed in the hope that it will be useful, //C- but WITHOUT ANY WARRANTY; without even the implied warranty of //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //C- GNU General Public License for more details. //C- //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from //C- Lizardtech Software. Lizardtech Software has authorized us to //C- replace the original DjVu(r) Reference Library notice by the following //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu): //C- //C- ------------------------------------------------------------------ //C- | DjVu (r) Reference Library (v. 3.5) //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. //C- | The DjVu Reference Library is protected by U.S. Pat. No. //C- | 6,058,214 and patents pending. //C- | //C- | This software is subject to, and may be distributed under, the //C- | GNU General Public License, either Version 2 of the license, //C- | or (at your option) any later version. The license should have //C- | accompanied the software or you may obtain a copy of the license //C- | from the Free Software Foundation at http://www.fsf.org . //C- | //C- | The computer code originally released by LizardTech under this //C- | license and unmodified by other parties is deemed "the LIZARDTECH //C- | ORIGINAL CODE." Subject to any third party intellectual property //C- | claims, LizardTech grants recipient a worldwide, royalty-free, //C- | non-exclusive license to make, use, sell, or otherwise dispose of //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU //C- | General Public License. This grant only confers the right to //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to //C- | the extent such infringement is reasonably necessary to enable //C- | recipient to make, have made, practice, sell, or otherwise dispose //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to //C- | any greater extent that may be necessary to utilize further //C- | modifications or combinations. //C- | //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. //C- +------------------------------------------------------------------ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if NEED_GNUG_PRAGMAS # pragma implementation #endif // - Author: Leon Bottou, 08/1998 // From: Leon Bottou, 1/31/2002 // Lizardtech has split this file into a decoder and an encoder. // Only superficial changes. The meat is mine. #define IW44IMAGE_IMPLIMENTATION /* */ #include "IW44Image.h" #include "ZPCodec.h" #include "GBitmap.h" #include "GPixmap.h" #include "IFFByteStream.h" #include "GRect.h" #include #include #include #include #include "MMX.h" #undef IWTRANSFORM_TIMER #ifdef IWTRANSFORM_TIMER #include "GOS.h" #endif #include #include #include #ifndef NEED_DECODER_ONLY #ifdef HAVE_NAMESPACES namespace DJVU { # ifdef NOT_DEFINED // Just to fool emacs c++ mode } #endif #endif #define IWALLOCSIZE 4080 #define IWCODEC_MAJOR 1 #define IWCODEC_MINOR 2 #define DECIBEL_PRUNE 5.0 ////////////////////////////////////////////////////// // WAVELET DECOMPOSITION CONSTANTS ////////////////////////////////////////////////////// // Parameters for IW44 wavelet. // - iw_norm: norm of all wavelets (for db estimation) // - iw_shift: scale applied before decomposition static const float iw_norm[16] = { 2.627989e+03F, 1.832893e+02F, 1.832959e+02F, 5.114690e+01F, 4.583344e+01F, 4.583462e+01F, 1.279225e+01F, 1.149671e+01F, 1.149712e+01F, 3.218888e+00F, 2.999281e+00F, 2.999476e+00F, 8.733161e-01F, 1.074451e+00F, 1.074511e+00F, 4.289318e-01F }; static const int iw_shift = 6; static const int iw_round = (1<<(iw_shift-1)); static const struct { int start; int size; } bandbuckets[] = { // Code first bucket and number of buckets in each band { 0, 1 }, // -- band zero contains all lores info { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 4 }, { 8, 4 }, { 12,4 }, { 16,16 }, { 32,16 }, { 48,16 }, }; /** IW44 encoded gray-level image. This class provided functions for managing a gray level image represented as a collection of IW44 wavelet coefficients. The coefficients are stored in a memory efficient data structure. Member function \Ref{get_bitmap} renders an arbitrary segment of the image into a \Ref{GBitmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy constructor and the copy operator are declared as private members. It is therefore not possible to make multiple copies of instances of this class. */ class IWBitmap::Encode : public IWBitmap { public: /// Destructor virtual ~Encode(void); /** Null constructor. Constructs an empty IWBitmap object. This object does not contain anything meaningful. You must call function \Ref{init}, \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet coefficient data structure. */ Encode(void); /** Initializes an IWBitmap with image #bm#. This constructor performs the wavelet decomposition of image #bm# and records the corresponding wavelet coefficient. Argument #mask# is an optional bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). */ void init(const GBitmap &bm, const GP mask=0); // CODER /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls how much data is generated. The chunk data is written to ByteStream #bs# with no IFF header. Successive calls to #encode_chunk# encode successive chunks. You must call #close_codec# after encoding the last chunk of a file. */ virtual int encode_chunk(GP gbs, const IWEncoderParms &parms); /** Writes a gray level image into DjVu IW44 file. This function creates a composite chunk (identifier #FORM:BM44#) composed of #nchunks# chunks (identifier #BM44#). Data for each chunk is generated with #encode_chunk# using the corresponding parameters in array #parms#. */ virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); /** Resets the encoder/decoder state. The first call to #decode_chunk# or #encode_chunk# initializes the coder for encoding or decoding. Function #close_codec# must be called after processing the last chunk in order to reset the coder and release the associated memory. */ virtual void close_codec(void); protected: Codec::Encode *ycodec_enc; }; /** IW44 encoded color image. This class provided functions for managing a color image represented as a collection of IW44 wavelet coefficients. The coefficients are stored in a memory efficient data structure. Member function \Ref{get_pixmap} renders an arbitrary segment of the image into a \Ref{GPixmap}. Member functions \Ref{decode_iff} and \Ref{encode_iff} read and write DjVu IW44 files (see \Ref{IW44Image.h}). Both the copy constructor and the copy operator are declared as private members. It is therefore not possible to make multiple copies of instances of this class. */ class IWPixmap::Encode : public IWPixmap { public: enum CRCBMode { CRCBnone=IW44Image::CRCBnone, CRCBhalf=IW44Image::CRCBhalf, CRCBnormal=IW44Image::CRCBnormal, CRCBfull=IW44Image::CRCBfull }; /// Destructor virtual ~Encode(void); /** Null constructor. Constructs an empty IWPixmap object. This object does not contain anything meaningful. You must call function \Ref{init}, \Ref{decode_iff} or \Ref{decode_chunk} to populate the wavelet coefficient data structure. */ Encode(void); /** Initializes an IWPixmap with color image #bm#. This constructor performs the wavelet decomposition of image #bm# and records the corresponding wavelet coefficient. Argument #mask# is an optional bilevel image specifying the masked pixels (see \Ref{IW44Image.h}). Argument #crcbmode# specifies how the chrominance information should be encoded (see \Ref{CRCBMode}). */ void init(const GPixmap &bm, const GP mask=0, CRCBMode crcbmode=CRCBnormal); // CODER /** Encodes one data chunk into ByteStream #bs#. Parameter #parms# controls how much data is generated. The chunk data is written to ByteStream #bs# with no IFF header. Successive calls to #encode_chunk# encode successive chunks. You must call #close_codec# after encoding the last chunk of a file. */ virtual int encode_chunk(GP gbs, const IWEncoderParms &parms); /** Writes a color image into a DjVu IW44 file. This function creates a composite chunk (identifier #FORM:PM44#) composed of #nchunks# chunks (identifier #PM44#). Data for each chunk is generated with #encode_chunk# using the corresponding parameters in array #parms#. */ virtual void encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms); /** Resets the encoder/decoder state. The first call to #decode_chunk# or #encode_chunk# initializes the coder for encoding or decoding. Function #close_codec# must be called after processing the last chunk in order to reset the coder and release the associated memory. */ virtual void close_codec(void); protected: Codec::Encode *ycodec_enc, *cbcodec_enc, *crcodec_enc; }; class IW44Image::Map::Encode : public IW44Image::Map // DJVU_CLASS { public: Encode(const int w, const int h) : Map(w,h) {} // creation (from image) void create(const signed char *img8, int imgrowsize, const signed char *msk8=0, int mskrowsize=0); // slash resolution void slashres(int res); }; class IW44Image::Codec::Encode : public IW44Image::Codec { public: Encode(IW44Image::Map &map); // Coding virtual int code_slice(ZPCodec &zp); float estimate_decibel(float frac); // Data void encode_buckets(ZPCodec &zp, int bit, int band, IW44Image::Block &blk, IW44Image::Block &eblk, int fbucket, int nbucket); int encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk); IW44Image::Map emap; }; IW44Image::Codec::Encode::Encode(IW44Image::Map &map) : Codec(map), emap(map.iw,map.ih) {} ////////////////////////////////////////////////////// /** IW44Image::Transform::Encode */ class IW44Image::Transform::Encode : IW44Image::Transform { public: // WAVELET TRANSFORM /** Forward transform. */ static void forward(short *p, int w, int h, int rowsize, int begin, int end); // COLOR TRANSFORM /** Extracts Y */ static void RGB_to_Y(const GPixel *p, int w, int h, int rowsize, signed char *out, int outrowsize); /** Extracts Cb */ static void RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, signed char *out, int outrowsize); /** Extracts Cr */ static void RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, signed char *out, int outrowsize); }; ////////////////////////////////////////////////////// // MMX IMPLEMENTATION HELPERS ////////////////////////////////////////////////////// // Note: // MMX implementation for vertical transforms only. // Speedup is basically related to faster memory transfer // The IW44 transform is not CPU bound, it is memory bound. #ifdef MMX static const short w9[] = {9,9,9,9}; static const short w1[] = {1,1,1,1}; static const int d8[] = {8,8}; static const int d16[] = {16,16}; static inline void mmx_fv_1 ( short* &q, short* e, int s, int s3 ) { while (q>4); q++; } while (q+3>5); q ++; } while (q+3=3 && y+30) mmx_fv_1(q, e, s, s3); #endif while (q>4); q += scale; } } else if (y>1); q += scale; q1 += scale; } } } // 2-Update { short *q = p-s3; short *e = q+w; if (y>=6 && y0) mmx_fv_2(q, e, s, s3); #endif while (q>5); q += scale; } } else if (y>=3) { // Special cases short *q1 = (y-2=6) { while (q>5); q += scale; if (q1) q1 += scale; if (q3) q3 += scale; } } else if (y>=4) { while (q>5); q += scale; if (q1) q1 += scale; if (q3) q3 += scale; } } else { while (q>5); q += scale; if (q1) q1 += scale; if (q3) q3 += scale; } } } } y += 2; p += s+s; } } static void filter_fh(short *p, int w, int h, int rowsize, int scale) { int y = 0; int s = scale; int s3 = s+s+s; rowsize *= scale; while (y>1); q[0] = b3; q += s+s; } while (q+s3 < e) { // Generic case a0=a1; a1=a2; a2=a3; a3=q[s3]; b0=b1; b1=b2; b2=b3; b3 = q[0] - ((((a1+a2)<<3)+(a1+a2)-a0-a3+8) >> 4); q[0] = b3; q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); q += s+s; } while (q < e) { // Special case: w-3 <= x < w a1=a2; a2=a3; b0=b1; b1=b2; b2=b3; b3 = q[0] - ((a1+a2+1)>>1); q[0] = b3; q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); q += s+s; } while (q-s3 < e) { // Special case w <= x < w+3 b0=b1; b1=b2; b2=b3; b3=0; if (q-s3 >= p) q[-s3] = q[-s3] + ((((b1+b2)<<3)+(b1+b2)-b0-b3+16) >> 5); q += s+s; } y += scale; p += rowsize; } } ////////////////////////////////////////////////////// // WAVELET TRANSFORM ////////////////////////////////////////////////////// //---------------------------------------------------- // Function for applying bidimensional IW44 between // scale intervals begin(inclusive) and end(exclusive) void IW44Image::Transform::Encode::forward(short *p, int w, int h, int rowsize, int begin, int end) { // PREPARATION filter_begin(w,h); // LOOP ON SCALES for (int scale=begin; scaler] + gmul[p2->g] + bmul[p2->b] + 32768; *out2 = (y>>16) - 128; } } } #ifdef min #undef min #endif static inline int min(const int x,const int y) {return (xy)?x:y;} /* Extracts Cb */ void IW44Image::Transform::Encode::RGB_to_Cb(const GPixel *p, int w, int h, int rowsize, signed char *out, int outrowsize) { int rmul[256], gmul[256], bmul[256]; for (int k=0; k<256; k++) { rmul[k] = (int)(k*0x10000*rgb_to_ycc[2][0]); gmul[k] = (int)(k*0x10000*rgb_to_ycc[2][1]); bmul[k] = (int)(k*0x10000*rgb_to_ycc[2][2]); } for (int i=0; ir] + gmul[p2->g] + bmul[p2->b] + 32768; *out2 = max(-128, min(127, c>>16)); } } } /* Extracts Cr */ void IW44Image::Transform::Encode::RGB_to_Cr(const GPixel *p, int w, int h, int rowsize, signed char *out, int outrowsize) { int rmul[256], gmul[256], bmul[256]; for (int k=0; k<256; k++) { rmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][0]); gmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][1]); bmul[k] = (int)((k*0x10000)*rgb_to_ycc[1][2]); } for (int i=0; ir] + gmul[p2->g] + bmul[p2->b] + 32768; *out2 = max(-128, min(127, c>>16)); } } } ////////////////////////////////////////////////////// // MASKING DECOMPOSITION ////////////////////////////////////////////////////// //---------------------------------------------------- // Function for applying bidimensional IW44 between // scale intervals begin(inclusive) and end(exclusive) // with a MASK bitmap static void interpolate_mask(short *data16, int w, int h, int rowsize, const signed char *mask8, int mskrowsize) { int i,j; // count masked bits short *count; GPBuffer gcount(count,w*h); short *cp = count; for (i=0; i gsdata(sdata,w*h); short *p = sdata; short *q = data16; for (i=0; ih) { istart -= scale; cpp -= w*scale; qq -= w*scale; } int jstart = j; if (jstart+split>w) jstart -= scale; // compute gray level for (ii=istart; ii0) { npix += cpp[jj]; gray += cpp[jj] * qq[jj]; } else if (ii>=i && jj>=j) { gotz = 1; } } // process result if (npix == 0) { // continue to next resolution again = 1; cp[j] = 0; } else { gray = gray / npix; // check whether initial image require fix if (gotz) { cpp = cp; qq = p; for (ii=i; ii>2; q[j] = gray; } } // double resolution split = scale; scale = scale+scale; } } static void forward_mask(short *data16, int w, int h, int rowsize, int begin, int end, const signed char *mask8, int mskrowsize ) { int i,j; signed char *m; short *p; short *d; // Allocate buffers short *sdata; GPBuffer gsdata(sdata,w*h); signed char *smask; GPBuffer gsmask(smask,w*h); // Copy mask m = smask; for (i=0; i=w || m[j+scale])) m[j] = 1; else m[j] = 0; m = m1 + w*scale; } } // Free buffers } void IW44Image::Map::Encode::create(const signed char *img8, int imgrowsize, const signed char *msk8, int mskrowsize ) { int i, j; // Progress DJVU_PROGRESS_TASK(transf,"create iw44 map",3); // Allocate decomposition buffer short *data16; GPBuffer gdata16(data16,bw*bh); // Copy pixels short *p = data16; const signed char *row = img8; for (i=0; iread_liftblock(liftblock, this); block++; } // next row of blocks p += 32*bw; } } void IW44Image::Map::Encode::slashres(int res) { int minbucket = 1; if (res < 2) return; else if (res < 4) minbucket=16; else if (res < 8) minbucket=4; for (int blockno=0; blockno=thres || (int)(pcoeff[i])<=-thres) cstatetmp = NEW|UNK; cstate[i] = cstatetmp; bstatetmp |= cstatetmp; } } else { for (int i=0; i<16; i++) { int cstatetmp = UNK; if (epcoeff[i]) cstatetmp = ACTIVE; else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) cstatetmp = NEW|UNK; cstate[i] = cstatetmp; bstatetmp |= cstatetmp; } } bucketstate[buckno] = bstatetmp; bbstate |= bstatetmp; } } else { // Band zero ( fbucket==0 implies band==zero and nbucket==1 ) const short *pcoeff = blk.data(0, &map); const short *epcoeff = eblk.data(0, &emap); char *cstate = coeffstate; for (int i=0; i<16; i++) { int thres = quant_lo[i]; int cstatetmp = cstate[i]; if (cstatetmp != ZERO) { cstatetmp = UNK; if (epcoeff[i]) cstatetmp = ACTIVE; else if ((int)(pcoeff[i])>=thres || (int)(pcoeff[i])<=-thres) cstatetmp = NEW|UNK; } cstate[i] = cstatetmp; bbstate |= cstatetmp; } bucketstate[0] = bbstate; } return bbstate; } // encode_buckets // -- code a sequence of buckets in a given block void IW44Image::Codec::Encode::encode_buckets(ZPCodec &zp, int bit, int band, IW44Image::Block &blk, IW44Image::Block &eblk, int fbucket, int nbucket) { // compute state of all coefficients in all buckets int bbstate = encode_prepare(band, fbucket, nbucket, blk, eblk); // code root bit if ((nbucket<16) || (bbstate&ACTIVE)) { bbstate |= NEW; } else if (bbstate & UNK) { zp.encoder( (bbstate&NEW) ? 1 : 0 , ctxRoot); #ifdef TRACE DjVuPrintMessage("bbstate[bit=%d,band=%d] = %d\n", bit, band, bbstate); #endif } // code bucket bits if (bbstate & NEW) for (int buckno=0; buckno0) { int k = (fbucket+buckno)<<2; const short *b = eblk.data(k>>4); if (b) { k = k & 0xf; if (b[k]) ctx += 1; if (b[k+1]) ctx += 1; if (b[k+2]) ctx += 1; if (ctx<3 && b[k+3]) ctx += 1; } } #endif #ifndef NOCTX_BUCKET_ACTIVE if (bbstate & ACTIVE) ctx |= 4; #endif // Code zp.encoder( (bucketstate[buckno]&NEW) ? 1 : 0, ctxBucket[band][ctx] ); #ifdef TRACE DjVuPrintMessage(" bucketstate[bit=%d,band=%d,buck=%d] = %d\n", bit, band, buckno, bucketstate[buckno] & ~ZERO); #endif } } // code new active coefficient (with their sign) if (bbstate & NEW) { int thres = quant_hi[band]; char *cstate = coeffstate; for (int buckno=0; buckno=maxgotcha) ctx = maxgotcha; else ctx = gotcha; #endif #ifndef NOCTX_ACTIVE if (bucketstate[buckno] & ACTIVE) ctx |= 8; #endif // Code zp.encoder( (cstate[i]&NEW) ? 1 : 0, ctxStart[ctx] ); if (cstate[i] & NEW) { // Code sign zp.IWencoder( (pcoeff[i]<0) ? 1 : 0 ); // Set encoder state if (band==0) thres = quant_lo[i]; epcoeff[i] = thres + (thres>>1); } #ifndef NOCTX_EXPECT if (cstate[i] & NEW) gotcha = 0; else if (gotcha > 0) gotcha -= 1; #endif #ifdef TRACE DjVuPrintMessage(" coeffstate[bit=%d,band=%d,buck=%d,c=%d] = %d\n", bit, band, buckno, i, cstate[i]); #endif } } } } // code mantissa bits if (bbstate & ACTIVE) { int thres = quant_hi[band]; char *cstate = coeffstate; for (int buckno=0; buckno= ecoeff) pix = 1; // encode second or lesser mantissa bit if (ecoeff <= 3*thres) zp.encoder(pix, ctxMant); else zp.IWencoder(!!pix); // adjust epcoeff epcoeff[i] = ecoeff - (pix ? 0 : thres) + (thres>>1); } } } } // IW44Image::Codec::estimate_decibel // -- estimate encoding error (after code_slice) in decibels. float IW44Image::Codec::Encode::estimate_decibel(float frac) { int i,j; const float *q; // Fill norm arrays float norm_lo[16]; float norm_hi[10]; // -- lo coefficients q = iw_norm; for (i=j=0; i<4; j++) norm_lo[i++] = *q++; for (j=0; j<4; j++) norm_lo[i++] = *q; q += 1; for (j=0; j<4; j++) norm_lo[i++] = *q; q += 1; for (j=0; j<4; j++) norm_lo[i++] = *q; q += 1; // -- hi coefficients norm_hi[0] = 0; for (j=1; j<10; j++) norm_hi[j] = *q++; // Initialize mse array float *xmse; GPBuffer gxmse(xmse,map.nb); // Compute mse in each block for (int blockno=0; blocknom ? m : (p<0 ? 0 : p)); float pivot = 0; // Partition array while (n < p) { int l = n; int h = m; if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } pivot = xmse[(l+h)/2]; if (pivot < xmse[l]) { float tmp=pivot; pivot=xmse[l]; xmse[l]=tmp; } if (pivot > xmse[h]) { float tmp=pivot; pivot=xmse[h]; xmse[h]=tmp; } while (l < h) { if (xmse[l] > xmse[h]) { float tmp=xmse[l]; xmse[l]=xmse[h]; xmse[h]=tmp; } while (xmse[l]pivot) h--; } if (p>=l) n = l; else m = l-1; } // Compute average mse float mse = 0; for (i=p; i gbs) { gbs->write8(serial); gbs->write8(slices); } void IW44Image::SecondaryHeader::encode(GP gbs) { gbs->write8(major); gbs->write8(minor); } void IW44Image::TertiaryHeader::encode(GP gbs) { gbs->write8(xhi); gbs->write8(xlo); gbs->write8(yhi); gbs->write8(ylo); gbs->write8(crcbdelay); } GP IW44Image::create_encode(const ImageType itype) { switch(itype) { case COLOR: return new IWPixmap::Encode(); case GRAY: return new IWBitmap::Encode(); default: return 0; } } GP IW44Image::create_encode(const GBitmap &bm, const GP mask) { IWBitmap::Encode *bit=new IWBitmap::Encode(); GP retval=bit; bit->init(bm, mask); return retval; } IWBitmap::Encode::Encode(void) : IWBitmap(), ycodec_enc(0) {} IWBitmap::Encode::~Encode() { close_codec(); } void IWBitmap::Encode::init(const GBitmap &bm, const GP gmask) { // Free close_codec(); delete ymap; ymap = 0; // Init int i, j; int w = bm.columns(); int h = bm.rows(); int g = bm.get_grays()-1; signed char *buffer; GPBuffer gbuffer(buffer,w*h); // Prepare gray level conversion table signed char bconv[256]; for (i=0; i<256; i++) bconv[i] = max(0,min(255,i*255/g)) - 128; // Perform decomposition // Prepare mask information const signed char *msk8 = 0; int mskrowsize = 0; GBitmap *mask=gmask; if (gmask) { msk8 = (const signed char*)((*mask)[0]); mskrowsize = mask->rowsize(); } // Prepare a buffer of signed bytes for (i=0; icreate(buffer, w, msk8, mskrowsize); } void IWBitmap::Encode::close_codec(void) { delete ycodec_enc; ycodec_enc = 0; IWBitmap::close_codec(); } int IWBitmap::Encode::encode_chunk(GP gbs, const IWEncoderParms &parm) { // Check if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) G_THROW( ERR_MSG("IW44Image.need_stop") ); if (! ymap) G_THROW( ERR_MSG("IW44Image.empty_object") ); // Open codec if (!ycodec_enc) { cslice = cserial = cbytes = 0; ycodec_enc = new Codec::Encode(*ymap); } // Adjust cbytes cbytes += sizeof(struct IW44Image::PrimaryHeader); if (cserial == 0) cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); // Prepare zcoded slices int flag = 1; int nslices = 0; GP gmbs=ByteStream::create(); ByteStream &mbs=*gmbs; DJVU_PROGRESS_TASK(chunk,"encode chunk",parm.slices-cslice); { float estdb = -1.0; GP gzp=ZPCodec::create(gmbs, true, true); ZPCodec &zp=*gzp; while (flag) { if (parm.decibels>0 && estdb>=parm.decibels) break; if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) break; if (parm.slices>0 && nslices+cslice>=parm.slices) break; DJVU_PROGRESS_RUN(chunk, (1+nslices-cslice)|0xf); flag = ycodec_enc->code_slice(zp); if (flag && parm.decibels>0.0) if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) estdb = ycodec_enc->estimate_decibel(db_frac); nslices++; } } // Write primary header struct IW44Image::PrimaryHeader primary; primary.serial = cserial; primary.slices = nslices; primary.encode(gbs); // Write auxilliary headers if (cserial == 0) { struct IW44Image::SecondaryHeader secondary; secondary.major = IWCODEC_MAJOR + 0x80; secondary.minor = IWCODEC_MINOR; secondary.encode(gbs); struct IW44Image::TertiaryHeader tertiary; tertiary.xhi = (ymap->iw >> 8) & 0xff; tertiary.xlo = (ymap->iw >> 0) & 0xff; tertiary.yhi = (ymap->ih >> 8) & 0xff; tertiary.ylo = (ymap->ih >> 0) & 0xff; tertiary.crcbdelay = 0; tertiary.encode(gbs); } // Write slices mbs.seek(0); gbs->copy(mbs); // Return cbytes += mbs.tell(); cslice += nslices; cserial += 1; return flag; } void IWBitmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) { if (ycodec_enc) G_THROW( ERR_MSG("IW44Image.left_open1") ); int flag = 1; iff.put_chunk("FORM:BM44", 1); DJVU_PROGRESS_TASK(iff,"encode iff chunk",nchunks); for (int i=0; flag && i IW44Image::create_encode( const GPixmap &pm, const GP gmask, CRCBMode crcbmode) { IWPixmap::Encode *pix=new IWPixmap::Encode(); GP retval=pix; pix->init(pm, gmask,(IWPixmap::Encode::CRCBMode)crcbmode); return retval; } IWPixmap::Encode::Encode(void) : IWPixmap(), ycodec_enc(0), cbcodec_enc(0), crcodec_enc(0) {} IWPixmap::Encode::~Encode() { close_codec(); } void IWPixmap::Encode::init(const GPixmap &pm, const GP gmask, CRCBMode crcbmode) { /* Free */ close_codec(); delete ymap; delete cbmap; delete crmap; ymap = cbmap = crmap = 0; /* Create */ int w = pm.columns(); int h = pm.rows(); signed char *buffer; GPBuffer gbuffer(buffer,w*h); // Create maps Map::Encode *eymap = new Map::Encode(w,h); ymap = eymap; // Handle CRCB mode switch (crcbmode) { case CRCBnone: crcb_half=1; crcb_delay=-1; break; case CRCBhalf: crcb_half=1; crcb_delay=10; break; case CRCBnormal: crcb_half=0; crcb_delay=10; break; case CRCBfull: crcb_half=0; crcb_delay= 0; break; } // Prepare mask information const signed char *msk8 = 0; int mskrowsize = 0; GBitmap *mask=gmask; if (mask) { msk8 = (signed char const *)((*mask)[0]); mskrowsize = mask->rowsize(); } // Fill buffer with luminance information DJVU_PROGRESS_TASK(create,"initialize pixmap",3); DJVU_PROGRESS_RUN(create,(crcb_delay>=0 ? 1 : 3)); Transform::Encode::RGB_to_Y(pm[0], w, h, pm.rowsize(), buffer, w); if (crcb_delay < 0) { // Stupid inversion for gray images signed char *e = buffer + w*h; for (signed char *b=buffer; bcreate(buffer, w, msk8, mskrowsize); // Create chrominance maps if (crcb_delay >= 0) { Map::Encode *ecbmap = new Map::Encode(w,h); cbmap = ecbmap; Map::Encode *ecrmap = new Map::Encode(w,h); crmap = ecrmap; // Process CB information DJVU_PROGRESS_RUN(create,2); Transform::Encode::RGB_to_Cb(pm[0], w, h, pm.rowsize(), buffer, w); ecbmap->create(buffer, w, msk8, mskrowsize); // Process CR information DJVU_PROGRESS_RUN(create,3); Transform::Encode::RGB_to_Cr(pm[0], w, h, pm.rowsize(), buffer, w); ecrmap->create(buffer, w, msk8, mskrowsize); // Perform chrominance reduction (CRCBhalf) if (crcb_half) { ecbmap->slashres(2); ecrmap->slashres(2); } } } void IWPixmap::Encode::encode_iff(IFFByteStream &iff, int nchunks, const IWEncoderParms *parms) { if (ycodec_enc) G_THROW( ERR_MSG("IW44Image.left_open3") ); int flag = 1; iff.put_chunk("FORM:PM44", 1); DJVU_PROGRESS_TASK(iff,"encode pixmap chunk", nchunks); for (int i=0; flag && i gbs, const IWEncoderParms &parm) { // Check if (parm.slices==0 && parm.bytes==0 && parm.decibels==0) G_THROW( ERR_MSG("IW44Image.need_stop2") ); if (!ymap) G_THROW( ERR_MSG("IW44Image.empty_object2") ); // Open if (!ycodec_enc) { cslice = cserial = cbytes = 0; ycodec_enc = new Codec::Encode(*ymap); if (crmap && cbmap) { cbcodec_enc = new Codec::Encode(*cbmap); crcodec_enc = new Codec::Encode(*crmap); } } // Adjust cbytes cbytes += sizeof(struct IW44Image::PrimaryHeader); if (cserial == 0) cbytes += sizeof(struct IW44Image::SecondaryHeader) + sizeof(struct IW44Image::TertiaryHeader); // Prepare zcodec slices int flag = 1; int nslices = 0; GP gmbs=ByteStream::create(); ByteStream &mbs=*gmbs; DJVU_PROGRESS_TASK(chunk, "encode pixmap chunk", parm.slices-cslice); { float estdb = -1.0; GP gzp=ZPCodec::create(gmbs, true, true); ZPCodec &zp=*gzp; while (flag) { if (parm.decibels>0 && estdb>=parm.decibels) break; if (parm.bytes>0 && mbs.tell()+cbytes>=parm.bytes) break; if (parm.slices>0 && nslices+cslice>=parm.slices) break; DJVU_PROGRESS_RUN(chunk,(1+nslices-cslice)|0xf); flag = ycodec_enc->code_slice(zp); if (flag && parm.decibels>0) if (ycodec_enc->curband==0 || estdb>=parm.decibels-DECIBEL_PRUNE) estdb = ycodec_enc->estimate_decibel(db_frac); if (crcodec_enc && cbcodec_enc && cslice+nslices>=crcb_delay) { flag |= cbcodec_enc->code_slice(zp); flag |= crcodec_enc->code_slice(zp); } nslices++; } } // Write primary header struct IW44Image::PrimaryHeader primary; primary.serial = cserial; primary.slices = nslices; primary.encode(gbs); // Write secondary header if (cserial == 0) { struct IW44Image::SecondaryHeader secondary; secondary.major = IWCODEC_MAJOR; secondary.minor = IWCODEC_MINOR; if (! (crmap && cbmap)) secondary.major |= 0x80; secondary.encode(gbs); struct IW44Image::TertiaryHeader tertiary; tertiary.xhi = (ymap->iw >> 8) & 0xff; tertiary.xlo = (ymap->iw >> 0) & 0xff; tertiary.yhi = (ymap->ih >> 8) & 0xff; tertiary.ylo = (ymap->ih >> 0) & 0xff; tertiary.crcbdelay = (crcb_half ? 0x00 : 0x80); tertiary.crcbdelay |= (crcb_delay>=0 ? crcb_delay : 0x00); tertiary.encode(gbs); } // Write slices mbs.seek(0); gbs->copy(mbs); // Return cbytes += mbs.tell(); cslice += nslices; cserial += 1; return flag; } // code_slice // -- read/write a slice of datafile int IW44Image::Codec::Encode::code_slice(ZPCodec &zp) { // Check that code_slice can still run if (curbit < 0) return 0; // Perform coding if (! is_null_slice(curbit, curband)) { for (int blockno=0; blockno