diff options
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp')
-rw-r--r-- | kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp | 1797 |
1 files changed, 1797 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp new file mode 100644 index 00000000..c63eda7d --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/IW44EncodeCodec.cpp @@ -0,0 +1,1797 @@ +//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, Version 2. 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 +//C- distributed by Lizardtech Software. On July 19th 2002, Lizardtech +//C- Software authorized us to replace the original DjVu(r) Reference +//C- Library notice by the following text (see doc/lizard2002.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, Version 2. 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- +------------------------------------------------------------------ +// +// $Id: IW44EncodeCodec.cpp,v 1.11 2004/08/06 15:11:29 leonb Exp $ +// $Name: release_3_5_15 $ + +#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 <stdlib.h> +#include <string.h> +#include <math.h> +#include "MMX.h" +#undef IWTRANSFORM_TIMER +#ifdef IWTRANSFORM_TIMER +#include "GOS.h" +#endif + +#include <assert.h> +#include <string.h> +#include <math.h> + +#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<GBitmap> 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<ByteStream> 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<GBitmap> 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<ByteStream> 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<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d8,mm0); + MMXar( paddd, d8,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 4,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 4,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( psubw, mm0,mm7); // MM7=[ p3-x3, p2-x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +static inline void +mmx_fv_2 ( short* &q, short* e, int s, int s3 ) +{ + while (q<e && (((long)q)&0x7)) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q ++; + } + while (q+3<e) + { + MMXar( movq, q-s,mm0); // MM0=[ b3, b2, b1, b0 ] + MMXar( movq, q+s,mm2); // MM2=[ c3, c2, c1, c0 ] + MMXrr( movq, mm0,mm1); + MMXrr( punpcklwd, mm2,mm0); // MM0=[ c1, b1, c0, b0 ] + MMXrr( punpckhwd, mm2,mm1); // MM1=[ c3, b3, c2, b2 ] + MMXar( pmaddwd, w9,mm0); // MM0=[ (c1+b1)*9, (c0+b0)*9 ] + MMXar( pmaddwd, w9,mm1); // MM1=[ (c3+b3)*9, (c2+b2)*9 ] + MMXar( movq, q-s3,mm2); + MMXar( movq, q+s3,mm4); + MMXrr( movq, mm2,mm3); + MMXrr( punpcklwd, mm4,mm2); // MM2=[ d1, a1, d0, a0 ] + MMXrr( punpckhwd, mm4,mm3); // MM3=[ d3, a3, d2, a2 ] + MMXar( pmaddwd, w1,mm2); // MM2=[ (a1+d1)*1, (a0+d0)*1 ] + MMXar( pmaddwd, w1,mm3); // MM3=[ (a3+d3)*1, (a2+d2)*1 ] + MMXar( paddd, d16,mm0); + MMXar( paddd, d16,mm1); + MMXrr( psubd, mm2,mm0); // MM0=[ (c1+b1)*9-a1-d1+8, ... + MMXrr( psubd, mm3,mm1); // MM1=[ (c3+b3)*9-a3-d3+8, ... + MMXir( psrad, 5,mm0); + MMXar( movq, q,mm7); // MM7=[ p3,p2,p1,p0 ] + MMXir( psrad, 5,mm1); + MMXrr( packssdw, mm1,mm0); // MM0=[ x3,x2,x1,x0 ] + MMXrr( paddw, mm0,mm7); // MM7=[ p3+x3, p2+x2, ... ] + MMXra( movq, mm7,q); +#if defined(_MSC_VER) && defined(_DEBUG) + MMXemms; +#endif + q += 4; + } +} + +#endif /* MMX */ + +////////////////////////////////////////////////////// +// NEW FILTERS +////////////////////////////////////////////////////// + +static void +filter_fv(short *p, int w, int h, int rowsize, int scale) +{ + int y = 0; + int s = scale*rowsize; + int s3 = s+s+s; + h = ((h-1)/scale)+1; + y += 1; + p += s; + while (y-3 < h) + { + // 1-Delta + { + short *q = p; + short *e = q+w; + if (y>=3 && y+3<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_1(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q -= (((a<<3)+a-b+8)>>4); + q += scale; + } + } + else if (y<h) + { + // Special cases + short *q1 = (y+1<h ? q+s : q-s); + while (q<e) + { + int a = (int)q[-s] + (int)(*q1); + *q -= ((a+1)>>1); + q += scale; + q1 += scale; + } + } + } + // 2-Update + { + short *q = p-s3; + short *e = q+w; + if (y>=6 && y<h) + { + // Generic case +#ifdef MMX + if (scale==1 && MMXControl::mmxflag>0) + mmx_fv_2(q, e, s, s3); +#endif + while (q<e) + { + int a = (int)q[-s] + (int)q[s]; + int b = (int)q[-s3] + (int)q[s3]; + *q += (((a<<3)+a-b+16)>>5); + q += scale; + } + } + else if (y>=3) + { + // Special cases + short *q1 = (y-2<h ? q+s : 0); + short *q3 = (y<h ? q+s3 : 0); + if (y>=6) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (int)q[-s3] + (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else if (y>=4) + { + while (q<e) + { + int a = (int)q[-s] + (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>5); + q += scale; + if (q1) q1 += scale; + if (q3) q3 += scale; + } + } + else + { + while (q<e) + { + int a = (q1 ? (int)(*q1) : 0); + int b = (q3 ? (int)(*q3) : 0); + *q += (((a<<3)+a-b+16)>>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<h) + { + short *q = p+s; + short *e = p+w; + int a0=0, a1=0, a2=0, a3=0; + int b0=0, b1=0, b2=0, b3=0; + if (q < e) + { + // Special case: x=1 + a1 = a2 = a3 = q[-s]; + if (q+s<e) + a2 = q[s]; + if (q+s3<e) + a3 = q[s3]; + b3 = q[0] - ((a1+a2+1)>>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; scale<end; scale<<=1) + { +#ifdef IWTRANSFORM_TIMER + int tv,th; + th = tv = GOS::ticks(); +#endif + filter_fh(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks(); + tv = th - tv; +#endif + filter_fv(p, w, h, rowsize, scale); +#ifdef IWTRANSFORM_TIMER + th = GOS::ticks()-th; + DjVuPrintErrorUTF8("forw%d\tv=%dms h=%dms\n", scale,th,tv); +#endif + } + // TERMINATE + filter_end(); +} + +////////////////////////////////////////////////////// +// COLOR TRANSFORM +////////////////////////////////////////////////////// + +static const float +rgb_to_ycc[3][3] = +{ { 0.304348F, 0.608696F, 0.086956F }, + { 0.463768F, -0.405797F, -0.057971F }, + {-0.173913F, -0.347826F, 0.521739F } }; + + +/* Extracts Y */ +void +IW44Image::Transform::Encode::RGB_to_Y(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[0][0]); + gmul[k] = (int)(k*0x10000*rgb_to_ycc[0][1]); + bmul[k] = (int)(k*0x10000*rgb_to_ycc[0][2]); + } + for (int i=0; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int y = rmul[p2->r] + 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 (x<y)?x:y;} +#ifdef max +#undef max +#endif +static inline int max(const int x,const int y) {return (x>y)?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; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + 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; i<h; i++, p+=rowsize, out+=outrowsize) + { + const GPixel *p2 = p; + signed char *out2 = out; + for (int j=0; j<w; j++,p2++,out2++) + { + int c = rmul[p2->r] + 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<short> gcount(count,w*h); + short *cp = count; + for (i=0; i<h; i++, cp+=w, mask8+=mskrowsize) + for (j=0; j<w; j++) + cp[j] = (mask8[j] ? 0 : 0x1000); + // copy image + short *sdata; + GPBuffer<short> gsdata(sdata,w*h); + short *p = sdata; + short *q = data16; + for (i=0; i<h; i++, p+=w, q+=rowsize) + for (j=0; j<w; j++) + p[j] = q[j]; + // iterate over resolutions + int split = 1; + int scale = 2; + int again = 1; + while (again && scale<w && scale<h) + { + again = 0; + p = data16; + q = sdata; + cp = count; + // iterate over block + for (i=0; i<h; i+=scale, cp+=w*scale, q+=w*scale, p+=rowsize*scale) + for (j=0; j<w; j+=scale) + { + int ii, jj; + int gotz = 0; + int gray = 0; + int npix = 0; + short *cpp = cp; + short *qq = q; + // look around when square goes beyond border + int istart = i; + if (istart+split>h) + { + istart -= scale; + cpp -= w*scale; + qq -= w*scale; + } + int jstart = j; + if (jstart+split>w) + jstart -= scale; + // compute gray level + for (ii=istart; ii<i+scale && ii<h; ii+=split, cpp+=w*split, qq+=w*split) + for (jj=jstart; jj<j+scale && jj<w; jj+=split) + { + if (cpp[jj]>0) + { + 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<i+scale && ii<h; ii+=1, cpp+=w, qq+=rowsize) + for (jj=j; jj<j+scale && jj<w; jj+=1) + if (cpp[jj] == 0) + { + qq[jj] = gray; + cpp[jj] = 1; + } + } + // store average for next iteration + cp[j] = npix>>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<short> gsdata(sdata,w*h); + signed char *smask; + GPBuffer<signed char> gsmask(smask,w*h); + // Copy mask + m = smask; + for (i=0; i<h; i+=1, m+=w, mask8+=mskrowsize) + memcpy((void*)m, (void*)mask8, w); + // Loop over scale + for (int scale=begin; scale<end; scale<<=1) + { + // Copy data into sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + d[j] = p[j]; + p += rowsize * scale; + d += w * scale; + } + // Decompose + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Cancel masked coefficients + d = sdata; + m = smask; + for (i=0; i<h; i+=scale+scale) + { + for (j=scale; j<w; j+=scale+scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + if (i+scale < h) + { + for (j=0; j<w; j+=scale) + if (m[j]) + d[j] = 0; + d += w * scale; + m += w * scale; + } + } + // Reconstruct + IW44Image::Transform::Decode::backward(sdata, w, h, w, scale+scale, scale); + // Correct visible pixels + p = data16; + d = sdata; + m = smask; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + if (! m[j]) + d[j] = p[j]; + p += rowsize*scale; + m += w*scale; + d += w*scale; + } + // Decompose again (no need to iterate actually!) + IW44Image::Transform::Encode::forward(sdata, w, h, w, scale, scale+scale); + // Copy coefficients from sdata buffer + p = data16; + d = sdata; + for (i=0; i<h; i+=scale) + { + for (j=0; j<w; j+=scale) + p[j] = d[j]; + p += rowsize * scale; + d += w * scale; + } + // Compute new mask for next scale + m = smask; + signed char *m0 = m; + signed char *m1 = m; + for (i=0; i<h; i+=scale+scale) + { + m0 = m1; + if (i+scale < h) + m1 = m + w*scale; + for (j=0; j<w; j+=scale+scale) + if (m[j] && m0[j] && m1[j] && (j<=0 || m[j-scale]) && (j+scale>=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<short> gdata16(data16,bw*bh); + // Copy pixels + short *p = data16; + const signed char *row = img8; + for (i=0; i<ih; i++) + { + for (j=0; j<iw; j++) + *p++ = (int)(row[j]) << iw_shift; + row += imgrowsize; + for (j=iw; j<bw; j++) + *p++ = 0; + } + for (i=ih; i<bh; i++) + for (j=0; j<bw; j++) + *p++ = 0; + // Handle bitmask + if (msk8) + { + // Interpolate pixels below mask + DJVU_PROGRESS_RUN(transf, 1); + interpolate_mask(data16, iw, ih, bw, msk8, mskrowsize); + // Multiscale iterative masked decomposition + DJVU_PROGRESS_RUN(transf, 3); + forward_mask(data16, iw, ih, bw, 1, 32, msk8, mskrowsize); + } + else + { + // Perform traditional decomposition + DJVU_PROGRESS_RUN(transf, 3); + IW44Image::Transform::Encode::forward(data16, iw, ih, bw, 1, 32); + } + // Copy coefficient into blocks + p = data16; + IW44Image::Block *block = blocks; + for (i=0; i<bh; i+=32) + { + for (j=0; j<bw; j+=32) + { + short liftblock[1024]; + // transfer coefficients at (p+j) into aligned block + short *pp = p + j; + short *pl = liftblock; + for (int ii=0; ii<32; ii++, pp+=bw) + for (int jj=0; jj<32; jj++) + *pl++ = pp[jj]; + // transfer into IW44Image::Block (apply zigzag and scaling) + block->read_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<nb; blockno++) + for (int buckno=minbucket; buckno<64; buckno++) + blocks[blockno].zero(buckno); +} + +// encode_prepare +// -- compute the states prior to encoding the buckets +int +IW44Image::Codec::Encode::encode_prepare(int band, int fbucket, int nbucket, IW44Image::Block &blk, IW44Image::Block &eblk) +{ + int bbstate = 0; + // compute state of all coefficients in all buckets + if (band) + { + // Band other than zero + int thres = quant_hi[band]; + char *cstate = coeffstate; + for (int buckno=0; buckno<nbucket; buckno++, cstate+=16) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + int bstatetmp = 0; + if (! pcoeff) + { + bstatetmp = UNK; + // cstate[i] is not used and does not need initialization + } + else if (! epcoeff) + { + for (int i=0; i<16; i++) + { + int cstatetmp = UNK; + if ((int)(pcoeff[i])>=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; buckno<nbucket; buckno++) + { + // Code bucket bit + if (bucketstate[buckno] & UNK) + { + // Context + int ctx = 0; +#ifndef NOCTX_BUCKET_UPPER + if (band>0) + { + 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<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & NEW) + { + int i; +#ifndef NOCTX_EXPECT + int gotcha = 0; + const int maxgotcha = 7; + for (i=0; i<16; i++) + if (cstate[i] & UNK) + gotcha += 1; +#endif + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + // iterate within bucket + for (i=0; i<16; i++) + { + if (cstate[i] & UNK) + { + // Prepare context + int ctx = 0; +#ifndef NOCTX_EXPECT + if (gotcha>=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<nbucket; buckno++, cstate+=16) + if (bucketstate[buckno] & ACTIVE) + { + const short *pcoeff = blk.data(fbucket+buckno); + short *epcoeff = eblk.data(fbucket+buckno, &emap); + for (int i=0; i<16; i++) + if (cstate[i] & ACTIVE) + { + // get coefficient + int coeff = pcoeff[i]; + int ecoeff = epcoeff[i]; + if (coeff < 0) + coeff = -coeff; + // get band zero thresholds + if (band == 0) + thres = quant_lo[i]; + // compute mantissa bit + int pix = 0; + if (coeff >= 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<float> gxmse(xmse,map.nb); + // Compute mse in each block + for (int blockno=0; blockno<map.nb; blockno++) + { + float mse = 0; + // Iterate over bands + for (int bandno=0; bandno<10; bandno++) + { + int fbucket = bandbuckets[bandno].start; + int nbucket = bandbuckets[bandno].size; + IW44Image::Block &blk = map.blocks[blockno]; + IW44Image::Block &eblk = emap.blocks[blockno]; + float norm = norm_hi[bandno]; + for (int buckno=0; buckno<nbucket; buckno++) + { + const short *pcoeff = blk.data(fbucket+buckno); + const short *epcoeff = eblk.data(fbucket+buckno); + if (pcoeff) + { + if (epcoeff) + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]<0 ? -pcoeff[i] : pcoeff[i]); + delta = delta - epcoeff[i]; + mse = mse + norm * delta * delta; + } + } + else + { + for (i=0; i<16; i++) + { + if (bandno == 0) + norm = norm_lo[i]; + float delta = (float)(pcoeff[i]); + mse = mse + norm * delta * delta; + } + } + } + } + } + xmse[blockno] = mse / 1024; + } + // Compute partition point + int n = 0; + int m = map.nb - 1; + int p = (int)floor(m*(1.0-frac)+0.5); + p = (p>m ? 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 || (xmse[l]==pivot && l<h)) l++; + while (xmse[h]>pivot) h--; + } + if (p>=l) + n = l; + else + m = l-1; + } + // Compute average mse + float mse = 0; + for (i=p; i<map.nb; i++) + mse = mse + xmse[i]; + mse = mse / (map.nb - p); + // Return + float factor = 255 << iw_shift; + float decibel = (float)(10.0 * log ( factor * factor / mse ) / 2.302585125); + return decibel; +} + + + + +////////////////////////////////////////////////////// +// IW44IMAGE ENCODING ROUTINES +////////////////////////////////////////////////////// + + +void +IW44Image::PrimaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(serial); + gbs->write8(slices); +} + +void +IW44Image::SecondaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(major); + gbs->write8(minor); +} + +void +IW44Image::TertiaryHeader::encode(GP<ByteStream> gbs) +{ + gbs->write8(xhi); + gbs->write8(xlo); + gbs->write8(yhi); + gbs->write8(ylo); + gbs->write8(crcbdelay); +} + + + +GP<IW44Image> +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> +IW44Image::create_encode(const GBitmap &bm, const GP<GBitmap> mask) +{ + IWBitmap::Encode *bit=new IWBitmap::Encode(); + GP<IW44Image> 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<GBitmap> 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<signed char> 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; i<h; i++) + { + signed char *bufrow = buffer + i*w; + const unsigned char *bmrow = bm[i]; + for (j=0; j<w; j++) + bufrow[j] = bconv[bmrow[j]]; + } + // Create map + Map::Encode *eymap=new Map::Encode(w,h); + ymap = eymap; + eymap->create(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<ByteStream> 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<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk,"encode chunk",parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> 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<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("BM44"); + flag = encode_chunk(iff.get_bytestream(),parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +GP<IW44Image> +IW44Image::create_encode( + const GPixmap &pm, const GP<GBitmap> gmask, CRCBMode crcbmode) +{ + IWPixmap::Encode *pix=new IWPixmap::Encode(); + GP<IW44Image> 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<GBitmap> 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<signed char> 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; b<e; b++) + *b = 255 - *b; + } + // Create YMAP + eymap->create(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<nchunks; i++) + { + DJVU_PROGRESS_RUN(iff,i+1); + iff.put_chunk("PM44"); + flag = encode_chunk(iff.get_bytestream(), parms[i]); + iff.close_chunk(); + } + iff.close_chunk(); + close_codec(); +} + +void +IWPixmap::Encode::close_codec(void) +{ + delete ycodec_enc; + delete cbcodec_enc; + delete crcodec_enc; + ycodec_enc = crcodec_enc = cbcodec_enc = 0; + IWPixmap::close_codec(); +} + +int +IWPixmap::Encode::encode_chunk(GP<ByteStream> 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<ByteStream> gmbs=ByteStream::create(); + ByteStream &mbs=*gmbs; + DJVU_PROGRESS_TASK(chunk, "encode pixmap chunk", parm.slices-cslice); + { + float estdb = -1.0; + GP<ZPCodec> 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<map.nb; blockno++) + { + const int fbucket = bandbuckets[curband].start; + const int nbucket = bandbuckets[curband].size; + encode_buckets(zp, curbit, curband, + map.blocks[blockno], emap.blocks[blockno], + fbucket, nbucket); + } + } + return finish_code_slice(zp); +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif +#endif // NEED_DECODER_ONLY + |