You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegraphics/kviewshell/plugins/djvu/libdjvu/JB2Image.cpp

1428 lines
36 KiB

//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: JB2Image.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $
// $Name: release_3_5_15 $
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#if NEED_GNUG_PRAGMAS
# pragma implementation
#endif
// From: Leon Bottou, 1/31/2002
// Lizardtech has split the corresponding cpp file into a decoder and an encoder.
// Only superficial changes. The meat is mine.
#include "JB2Image.h"
#include "GThreads.h"
#include "GRect.h"
#include "GBitmap.h"
#include <string.h>
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
////////////////////////////////////////
//// CLASS JB2Codec::Decode: DECLARATION
////////////////////////////////////////
// This class is accessed via the decode
// functions of class JB2Image
//**** Class JB2Codec
// This class implements the JB2 decoder.
// Contains all contextual information for decoding a JB2Image.
class JB2Dict::JB2Codec::Decode : public JB2Dict::JB2Codec
{
public:
Decode(void);
void init(const GP<ByteStream> &gbs);
// virtual
void code(const GP<JB2Image> &jim);
void code(JB2Image *jim) {const GP<JB2Image> gjim(jim);code(gjim);}
void code(const GP<JB2Dict> &jim);
void code(JB2Dict *jim) {const GP<JB2Dict> gjim(jim);code(gjim);}
void set_dict_callback(JB2DecoderCallback *cb, void *arg);
protected:
int CodeNum(const int lo, const int hi, NumContext &ctx);
// virtual
bool CodeBit(const bool bit, BitContext &ctx);
void code_comment(GUTF8String &comment);
void code_record_type(int &rectype);
int code_match_index(int &index, JB2Dict &jim);
void code_inherited_tqshape_count(JB2Dict &jim);
void code_image_size(JB2Dict &jim);
void code_image_size(JB2Image &jim);
void code_absolute_location(JB2Blit *jblt, int rows, int columns);
void code_absolute_mark_size(GBitmap &bm, int border=0);
void code_relative_mark_size(GBitmap &bm, int cw, int ch, int border=0);
void code_bitmap_directly(GBitmap &bm,const int dw, int dy,
unsigned char *up2, unsigned char *up1, unsigned char *up0 );
void code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm,
const int xd2c, const int dw, int dy, int cy,
unsigned char *up1, unsigned char *up0, unsigned char *xup1,
unsigned char *xup0, unsigned char *xdn1 );
int get_diff(const int x_diff,NumContext &rel_loc);
private:
GP<ZPCodec> gzp;
JB2DecoderCallback *cbfunc;
void *cbarg;
};
////////////////////////////////////////
//// CLASS JB2DICT: IMPLEMENTATION
////////////////////////////////////////
JB2Dict::JB2Dict()
: inherited_tqshapes(0)
{
}
void
JB2Dict::init()
{
inherited_tqshapes = 0;
inherited_dict = 0;
tqshapes.empty();
}
JB2Shape &
JB2Dict::get_tqshape(const int tqshapeno)
{
JB2Shape *retval;
if(tqshapeno >= inherited_tqshapes)
{
retval=&tqshapes[tqshapeno - inherited_tqshapes];
}else if(inherited_dict)
{
retval=&(inherited_dict->get_tqshape(tqshapeno));
}else
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
return *retval;
}
const JB2Shape &
JB2Dict::get_tqshape(const int tqshapeno) const
{
const JB2Shape *retval;
if(tqshapeno >= inherited_tqshapes)
{
retval=&tqshapes[tqshapeno - inherited_tqshapes];
}else if(inherited_dict)
{
retval=&(inherited_dict->get_tqshape(tqshapeno));
}else
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
return *retval;
}
void
JB2Dict::set_inherited_dict(const GP<JB2Dict> &dict)
{
if (tqshapes.size() > 0)
G_THROW( ERR_MSG("JB2Image.cant_set") );
if (inherited_dict)
G_THROW( ERR_MSG("JB2Image.cant_change") );
inherited_dict = dict;
inherited_tqshapes = dict->get_tqshape_count();
// Make sure that inherited bitmaps are marked as shared
for (int i=0; i<inherited_tqshapes; i++)
{
JB2Shape &jshp = dict->get_tqshape(i);
if (jshp.bits) jshp.bits->share();
}
}
void
JB2Dict::compress()
{
for (int i=tqshapes.lbound(); i<=tqshapes.hbound(); i++)
tqshapes[i].bits->compress();
}
unsigned int
JB2Dict::get_memory_usage() const
{
unsigned int usage = sizeof(JB2Dict);
usage += sizeof(JB2Shape) * tqshapes.size();
for (int i=tqshapes.lbound(); i<=tqshapes.hbound(); i++)
if (tqshapes[i].bits)
usage += tqshapes[i].bits->get_memory_usage();
return usage;
}
int
JB2Dict::add_tqshape(const JB2Shape &tqshape)
{
if (tqshape.tqparent >= get_tqshape_count())
G_THROW( ERR_MSG("JB2Image.bad_parent_tqshape") );
int index = tqshapes.size();
tqshapes.touch(index);
tqshapes[index] = tqshape;
return index + inherited_tqshapes;
}
void
JB2Dict::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg)
{
init();
JB2Codec::Decode codec;
codec.init(gbs);
codec.set_dict_callback(cb,arg);
codec.code(this);
}
////////////////////////////////////////
//// CLASS JB2IMAGE: IMPLEMENTATION
////////////////////////////////////////
JB2Image::JB2Image(void)
: width(0), height(0), reproduce_old_bug(false)
{
}
void
JB2Image::init(void)
{
width = height = 0;
blits.empty();
JB2Dict::init();
}
unsigned int
JB2Image::get_memory_usage() const
{
unsigned int usage = JB2Dict::get_memory_usage();
usage += sizeof(JB2Image) - sizeof(JB2Dict);
usage += sizeof(JB2Blit) * blits.size();
return usage;
}
void
JB2Image::set_dimension(int awidth, int aheight)
{
width = awidth;
height = aheight;
}
int
JB2Image::add_blit(const JB2Blit &blit)
{
if (blit.tqshapeno >= (unsigned int)get_tqshape_count())
G_THROW( ERR_MSG("JB2Image.bad_tqshape") );
int index = blits.size();
blits.touch(index);
blits[index] = blit;
return index;
}
GP<GBitmap>
JB2Image::get_bitmap(int subsample, int align) const
{
if (width==0 || height==0)
G_THROW( ERR_MSG("JB2Image.cant_create") );
int swidth = (width + subsample - 1) / subsample;
int sheight = (height + subsample - 1) / subsample;
int border = ((swidth + align - 1) & ~(align - 1)) - swidth;
GP<GBitmap> bm = GBitmap::create(sheight, swidth, border);
bm->set_grays(1+subsample*subsample);
for (int blitno = 0; blitno < get_blit_count(); blitno++)
{
const JB2Blit *pblit = get_blit(blitno);
const JB2Shape &ptqshape = get_tqshape(pblit->tqshapeno);
if (ptqshape.bits)
bm->blit(ptqshape.bits, pblit->left, pblit->bottom, subsample);
}
return bm;
}
GP<GBitmap>
JB2Image::get_bitmap(const GRect &rect, int subsample, int align, int dispy) const
{
if (width==0 || height==0)
G_THROW( ERR_MSG("JB2Image.cant_create") );
int rxmin = rect.xmin * subsample;
int rymin = rect.ymin * subsample;
int swidth = rect.width();
int sheight = rect.height();
int border = ((swidth + align - 1) & ~(align - 1)) - swidth;
GP<GBitmap> bm = GBitmap::create(sheight, swidth, border);
bm->set_grays(1+subsample*subsample);
for (int blitno = 0; blitno < get_blit_count(); blitno++)
{
const JB2Blit *pblit = get_blit(blitno);
const JB2Shape &ptqshape = get_tqshape(pblit->tqshapeno);
if (ptqshape.bits)
bm->blit(ptqshape.bits, pblit->left-rxmin, pblit->bottom-rymin+dispy, subsample);
}
return bm;
}
void
JB2Image::decode(const GP<ByteStream> &gbs, JB2DecoderCallback *cb, void *arg)
{
init();
JB2Codec::Decode codec;
codec.init(gbs);
codec.set_dict_callback(cb,arg);
codec.code(this);
}
////////////////////////////////////////
//// CLASS JB2CODEC : IMPLEMENTATION
////////////////////////////////////////
#define START_OF_DATA (0)
#define NEW_MARK (1)
#define NEW_MARK_LIBRARY_ONLY (2)
#define NEW_MARK_IMAGE_ONLY (3)
#define MATCHED_REFINE (4)
#define MATCHED_REFINE_LIBRARY_ONLY (5)
#define MATCHED_REFINE_IMAGE_ONLY (6)
#define MATCHED_COPY (7)
#define NON_MARK_DATA (8)
#define RETQUIRED_DICT_OR_RESET (9)
#define PRESERVED_COMMENT (10)
#define END_OF_DATA (11)
// STATIC DATA MEMBERS
static const int BIGPOSITIVE = 262142;
static const int BIGNEGATIVE = -262143;
static const int CELLCHUNK = 20000;
static const int CELLEXTRA = 500;
// CONSTRUCTOR
JB2Dict::JB2Codec::Decode::Decode(void)
: JB2Dict::JB2Codec(0), cbfunc(0), cbarg(0) {}
void
JB2Dict::JB2Codec::Decode::init(const GP<ByteStream> &gbs)
{
gzp=ZPCodec::create(gbs,false,true);
}
JB2Dict::JB2Codec::JB2Codec(const bool xencoding)
: encoding(xencoding),
cur_ncell(0),
gbitcells(bitcells,CELLCHUNK+CELLEXTRA),
gleftcell(leftcell,CELLCHUNK+CELLEXTRA),
grightcell(rightcell,CELLCHUNK+CELLEXTRA),
refinementp(false),
gotstartrecordp(0),
dist_comment_byte(0),
dist_comment_length(0),
dist_record_type(0),
dist_match_index(0),
dist_refinement_flag(0),
abs_loc_x(0),
abs_loc_y(0),
abs_size_x(0),
abs_size_y(0),
image_size_dist(0),
inherited_tqshape_count_dist(0),
offset_type_dist(0),
rel_loc_x_current(0),
rel_loc_x_last(0),
rel_loc_y_current(0),
rel_loc_y_last(0),
rel_size_x(0),
rel_size_y(0)
{
memset(bitdist, 0, sizeof(bitdist));
memset(cbitdist, 0, sizeof(cbitdist));
// Initialize numcoder
bitcells[0] = 0; // dummy cell
leftcell[0] = rightcell[0] = 0;
cur_ncell = 1;
}
JB2Dict::JB2Codec::~JB2Codec() {}
void
JB2Dict::JB2Codec::reset_numcoder()
{
dist_comment_byte = 0;
dist_comment_length = 0;
dist_record_type = 0;
dist_match_index = 0;
abs_loc_x = 0;
abs_loc_y = 0;
abs_size_x = 0;
abs_size_y = 0;
image_size_dist = 0;
inherited_tqshape_count_dist = 0;
rel_loc_x_current = 0;
rel_loc_x_last = 0;
rel_loc_y_current = 0;
rel_loc_y_last = 0;
rel_size_x = 0;
rel_size_y = 0;
gbitcells.clear();
gleftcell.clear();
grightcell.clear();
cur_ncell = 1;
}
void
JB2Dict::JB2Codec::Decode::set_dict_callback(JB2DecoderCallback *cb, void *arg)
{
cbfunc = cb;
cbarg = arg;
}
// CODE NUMBERS
inline bool
JB2Dict::JB2Codec::Decode::CodeBit(const bool, BitContext &ctx)
{
return gzp->decoder(ctx)?true:false;
}
int
JB2Dict::JB2Codec::Decode::CodeNum(int low, int high, NumContext &ctx)
{
return JB2Codec::CodeNum(low,high,&ctx,0);
}
int
JB2Dict::JB2Codec::CodeNum(int low, int high, NumContext *pctx, int v)
{
bool negative=false;
int cutoff;
// Check
if (!pctx || ((int)*pctx >= cur_ncell))
G_THROW( ERR_MSG("JB2Image.bad_numcontext") );
// Start all phases
cutoff = 0;
for(int phase=1,range=0xffffffff;range != 1;)
{
if (! *pctx)
{
const int max_ncell=gbitcells;
if (cur_ncell >= max_ncell)
{
const int nmax_ncell = max_ncell+CELLCHUNK;
gbitcells.resize(nmax_ncell);
gleftcell.resize(nmax_ncell);
grightcell.resize(nmax_ncell);
}
*pctx = cur_ncell ++;
bitcells[*pctx] = 0;
leftcell[*pctx] = rightcell[*pctx] = 0;
}
// encode
const bool decision = encoding
? ((low < cutoff && high >= cutoff)
? CodeBit((v>=cutoff),bitcells[*pctx])
: (v >= cutoff))
: ((low>=cutoff)||((high>=cutoff)&&CodeBit(false,bitcells[*pctx])));
// context for new bit
pctx = decision?(&rightcell[*pctx]):(&leftcell[*pctx]);
// phase dependent part
switch (phase)
{
case 1:
negative = !decision;
if (negative)
{
if (encoding)
v = - v - 1;
const int temp = - low - 1;
low = - high - 1;
high = temp;
}
phase = 2; cutoff = 1;
break;
case 2:
if (!decision)
{
phase = 3;
range = (cutoff + 1) / 2;
if (range == 1)
cutoff = 0;
else
cutoff -= range / 2;
}
else
{
cutoff += cutoff + 1;
}
break;
case 3:
range /= 2;
if (range != 1)
{
if (!decision)
cutoff -= range / 2;
else
cutoff += range / 2;
}
else if (!decision)
{
cutoff --;
}
break;
}
}
return (negative)?(- cutoff - 1):cutoff;
}
// CODE COMMENTS
void
JB2Dict::JB2Codec::Decode::code_comment(GUTF8String &comment)
{
int size=CodeNum(0, BIGPOSITIVE, dist_comment_length);
comment.empty();
char *combuf = comment.getbuf(size);
for (int i=0; i<size; i++)
{
combuf[i]=CodeNum(0, 255, dist_comment_byte);
}
comment.getbuf();
}
// LIBRARY
void
JB2Dict::JB2Codec::init_library(JB2Dict &jim)
{
int ntqshape = jim.get_inherited_tqshape_count();
tqshape2lib.resize(0,ntqshape-1);
lib2tqshape.resize(0,ntqshape-1);
libinfo.resize(0,ntqshape-1);
for (int i=0; i<ntqshape; i++)
{
tqshape2lib[i] = i;
lib2tqshape[i] = i;
JB2Shape &jshp = jim.get_tqshape(i);
libinfo[i].compute_bounding_box(*(jshp.bits));
}
}
int
JB2Dict::JB2Codec::add_library(const int tqshapeno, JB2Shape &jshp)
{
const int libno = lib2tqshape.hbound() + 1;
lib2tqshape.touch(libno);
lib2tqshape[libno] = tqshapeno;
tqshape2lib.touch(tqshapeno);
tqshape2lib[tqshapeno] = libno;
libinfo.touch(libno);
libinfo[libno].compute_bounding_box(*(jshp.bits));
return libno;
}
// CODE SIMPLE VALUES
inline void
JB2Dict::JB2Codec::Decode::code_record_type(int &rectype)
{
rectype=CodeNum( START_OF_DATA, END_OF_DATA, dist_record_type);
}
int
JB2Dict::JB2Codec::Decode::code_match_index(int &index, JB2Dict &)
{
int match=CodeNum(0, lib2tqshape.hbound(), dist_match_index);
index = lib2tqshape[match];
return match;
}
// HANDLE SHORT LIST
int
JB2Dict::JB2Codec::update_short_list(const int v)
{
if (++ short_list_pos == 3)
short_list_pos = 0;
int * const s = short_list;
s[short_list_pos] = v;
return (s[0] >= s[1])
?((s[0] > s[2])?((s[1] >= s[2])?s[1]:s[2]):s[0])
:((s[0] < s[2])?((s[1] >= s[2])?s[2]:s[1]):s[0]);
}
// CODE PAIRS
void
JB2Dict::JB2Codec::Decode::code_inherited_tqshape_count(JB2Dict &jim)
{
int size=CodeNum(0, BIGPOSITIVE, inherited_tqshape_count_dist);
{
GP<JB2Dict> dict = jim.get_inherited_dict();
if (!dict && size>0)
{
// Call callback function to obtain dictionary
if (cbfunc)
dict = (*cbfunc)(cbarg);
if (dict)
jim.set_inherited_dict(dict);
}
if (!dict && size>0)
G_THROW( ERR_MSG("JB2Image.need_dict") );
if (dict && size!=dict->get_tqshape_count())
G_THROW( ERR_MSG("JB2Image.bad_dict") );
}
}
void
JB2Dict::JB2Codec::Decode::code_image_size(JB2Dict &jim)
{
int w=CodeNum(0, BIGPOSITIVE, image_size_dist);
int h=CodeNum(0, BIGPOSITIVE, image_size_dist);
if (w || h)
G_THROW( ERR_MSG("JB2Image.bad_dict2") );
JB2Codec::code_image_size(jim);
}
void
JB2Dict::JB2Codec::code_image_size(JB2Dict &)
{
last_left = 1;
last_row_left = 0;
last_row_bottom = 0;
last_right = 0;
fill_short_list(last_row_bottom);
gotstartrecordp = 1;
}
void
JB2Dict::JB2Codec::Decode::code_image_size(JB2Image &jim)
{
image_columns=CodeNum(0, BIGPOSITIVE, image_size_dist);
image_rows=CodeNum(0, BIGPOSITIVE, image_size_dist);
if (!image_columns || !image_rows)
G_THROW( ERR_MSG("JB2Image.zero_dim") );
jim.set_dimension(image_columns, image_rows);
JB2Codec::code_image_size(jim);
}
void
JB2Dict::JB2Codec::code_image_size(JB2Image &)
{
last_left = 1 + image_columns;
last_row_left = 0;
last_row_bottom = image_rows;
last_right = 0;
fill_short_list(last_row_bottom);
gotstartrecordp = 1;
}
inline int
JB2Dict::JB2Codec::Decode::get_diff(int,NumContext &rel_loc)
{
return CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_loc);
}
void
JB2Dict::JB2Codec::code_relative_location(JB2Blit *jblt, int rows, int columns)
{
// Check start record
if (!gotstartrecordp)
G_THROW( ERR_MSG("JB2Image.no_start") );
// Find location
int bottom=0, left=0, top=0, right=0;
int x_diff, y_diff;
if (encoding)
{
left = jblt->left + 1;
bottom = jblt->bottom + 1;
right = left + columns - 1;
top = bottom + rows - 1;
}
// Code offset type
int new_row=CodeBit((left<last_left), offset_type_dist);
if (new_row)
{
// Begin a new row
x_diff=get_diff(left-last_row_left,rel_loc_x_last);
y_diff=get_diff(top-last_row_bottom,rel_loc_y_last);
if (!encoding)
{
left = last_row_left + x_diff;
top = last_row_bottom + y_diff;
right = left + columns - 1;
bottom = top - rows + 1;
}
last_left = last_row_left = left;
last_right = right;
last_bottom = last_row_bottom = bottom;
fill_short_list(bottom);
}
else
{
// Same row
x_diff=get_diff(left-last_right,rel_loc_x_current);
y_diff=get_diff(bottom-last_bottom,rel_loc_y_current);
if (!encoding)
{
left = last_right + x_diff;
bottom = last_bottom + y_diff;
right = left + columns - 1;
top = bottom + rows - 1;
}
last_left = left;
last_right = right;
last_bottom = update_short_list(bottom);
}
// Store in blit record
if (!encoding)
{
jblt->bottom = bottom - 1;
jblt->left = left - 1;
}
}
void
JB2Dict::JB2Codec::Decode::code_absolute_location(JB2Blit *jblt, int rows, int columns)
{
// Check start record
if (!gotstartrecordp)
G_THROW( ERR_MSG("JB2Image.no_start") );
int left=CodeNum(1, image_columns, abs_loc_x);
int top=CodeNum(1, image_rows, abs_loc_y);
jblt->bottom = top - rows + 1 - 1;
jblt->left = left - 1;
}
void
JB2Dict::JB2Codec::Decode::code_absolute_mark_size(GBitmap &bm, int border)
{
int xsize=CodeNum(0, BIGPOSITIVE, abs_size_x);
int ysize=CodeNum(0, BIGPOSITIVE, abs_size_y);
if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize))
G_THROW( ERR_MSG("JB2Image.bad_number") );
bm.init(ysize, xsize, border);
}
void
JB2Dict::JB2Codec::Decode::code_relative_mark_size(GBitmap &bm, int cw, int ch, int border)
{
int xdiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_x);
int ydiff=CodeNum(BIGNEGATIVE, BIGPOSITIVE, rel_size_y);
int xsize = cw + xdiff;
int ysize = ch + ydiff;
if ((xsize!=(unsigned short)xsize) || (ysize!=(unsigned short)ysize))
G_THROW( ERR_MSG("JB2Image.bad_number") );
bm.init(ysize, xsize, border);
}
// CODE BITMAP DIRECTLY
void
JB2Dict::JB2Codec::code_bitmap_directly (GBitmap &bm)
{
// Make sure bitmap will not be disturbed
GMonitorLock lock(bm.monitor());
// ensure borders are adequate
bm.minborder(3);
// initialize row pointers
int dy = bm.rows() - 1;
code_bitmap_directly(bm,bm.columns(),dy,bm[dy+2],bm[dy+1],bm[dy]);
}
void
JB2Dict::JB2Codec::Decode::code_bitmap_directly(
GBitmap &bm,const int dw, int dy,
unsigned char *up2, unsigned char *up1, unsigned char *up0 )
{
ZPCodec &zp=*gzp;
// iterate on rows (decoding)
while (dy >= 0)
{
int context=get_direct_context(up2, up1, up0, 0);
for(int dx=0;dx < dw;)
{
int n = zp.decoder(bitdist[context]);
up0[dx++] = n;
context=shift_direct_context(context, n, up2, up1, up0, dx);
}
// next row
dy -= 1;
up2 = up1;
up1 = up0;
up0 = bm[dy];
}
#ifndef NDEBUG
bm.check_border();
#endif
}
// CODE BITMAP BY CROSS CODING
void
JB2Dict::JB2Codec::code_bitmap_by_cross_coding (GBitmap &bm, GP<GBitmap> &cbm, const int libno)
{
// Make sure bitmaps will not be disturbed
GP<GBitmap> copycbm=GBitmap::create();
if (cbm->monitor())
{
// Perform a copy when the bitmap is explicitely shared
GMonitorLock lock2(cbm->monitor());
copycbm->init(*cbm);
cbm = copycbm;
}
GMonitorLock lock1(bm.monitor());
// Center bitmaps
const int cw = cbm->columns();
const int dw = bm.columns();
const int dh = bm.rows();
const LibRect &l = libinfo[libno];
const int xd2c = (dw/2 - dw + 1) - ((l.right - l.left + 1)/2 - l.right);
const int yd2c = (dh/2 - dh + 1) - ((l.top - l.bottom + 1)/2 - l.top);
// Ensure borders are adequate
bm.minborder(2);
cbm->minborder(2-xd2c);
cbm->minborder(2+dw+xd2c-cw);
// Initialize row pointers
const int dy = dh - 1;
const int cy = dy + yd2c;
#ifndef NDEBUG
bm.check_border();
cbm->check_border();
#endif
code_bitmap_by_cross_coding (bm,*cbm, xd2c, dw, dy, cy, bm[dy+1], bm[dy],
(*cbm)[cy+1] + xd2c, (*cbm)[cy ] + xd2c, (*cbm)[cy-1] + xd2c);
}
void
JB2Dict::JB2Codec::Decode::code_bitmap_by_cross_coding (GBitmap &bm, GBitmap &cbm,
const int xd2c, const int dw, int dy, int cy,
unsigned char *up1, unsigned char *up0, unsigned char *xup1,
unsigned char *xup0, unsigned char *xdn1 )
{
ZPCodec &zp=*gzp;
// iterate on rows (decoding)
while (dy >= 0)
{
int context=get_cross_context(
up1, up0, xup1, xup0, xdn1, 0);
for(int dx=0;dx < dw;)
{
const int n = zp.decoder(cbitdist[context]);
up0[dx++] = n;
context=shift_cross_context(context, n,
up1, up0, xup1, xup0, xdn1, dx);
}
// next row
up1 = up0;
up0 = bm[--dy];
xup1 = xup0;
xup0 = xdn1;
xdn1 = cbm[(--cy)-1] + xd2c;
#ifndef NDEBUG
bm.check_border();
#endif
}
}
// CODE JB2DICT RECORD
void
JB2Dict::JB2Codec::code_record(
int &rectype, const GP<JB2Dict> &gjim, JB2Shape *xjshp)
{
GP<GBitmap> cbm;
GP<GBitmap> bm;
int tqshapeno = -1;
// Code record type
code_record_type(rectype);
// Pre-coding actions
switch(rectype)
{
case NEW_MARK_LIBRARY_ONLY:
case MATCHED_REFINE_LIBRARY_ONLY:
{
if(!xjshp)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Shape &jshp=*xjshp;
if (!encoding)
{
jshp.bits = GBitmap::create();
jshp.tqparent = -1;
}
bm = jshp.bits;
break;
}
}
// Coding actions
switch (rectype)
{
case START_OF_DATA:
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Dict &jim=*gjim;
code_image_size (jim);
code_eventual_lossless_refinement ();
if (! encoding)
init_library(jim);
break;
}
case NEW_MARK_LIBRARY_ONLY:
{
code_absolute_mark_size (*bm, 4);
code_bitmap_directly (*bm);
break;
}
case MATCHED_REFINE_LIBRARY_ONLY:
{
if(!xjshp||!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Dict &jim=*gjim;
JB2Shape &jshp=*xjshp;
int match = code_match_index (jshp.tqparent, jim);
cbm = jim.get_tqshape(jshp.tqparent).bits;
LibRect &l = libinfo[match];
code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4);
code_bitmap_by_cross_coding (*bm, cbm, jshp.tqparent);
break;
}
case PRESERVED_COMMENT:
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Dict &jim=*gjim;
code_comment(jim.comment);
break;
}
case RETQUIRED_DICT_OR_RESET:
{
if (! gotstartrecordp)
{
// Indicates need for a tqshape dictionary
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
code_inherited_tqshape_count(*gjim);
}else
// Reset all numerical contexts to zero
reset_numcoder();
break;
}
case END_OF_DATA:
{
break;
}
default:
{
G_THROW( ERR_MSG("JB2Image.bad_type") );
}
}
// Post-coding action
if (!encoding)
{
// add tqshape to dictionary
switch(rectype)
{
case NEW_MARK_LIBRARY_ONLY:
case MATCHED_REFINE_LIBRARY_ONLY:
{
if(!xjshp||!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Shape &jshp=*xjshp;
tqshapeno = gjim->add_tqshape(jshp);
add_library(tqshapeno, jshp);
break;
}
}
// make sure everything is compacted
// decompaction will occur automatically when needed
if (bm)
bm->compress();
}
}
// CODE JB2DICT
void
JB2Dict::JB2Codec::Decode::code(const GP<JB2Dict> &gjim)
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Dict &jim=*gjim;
// -------------------------
// THIS IS THE DECODING PART
// -------------------------
int rectype;
JB2Shape tmptqshape;
do
{
code_record(rectype, gjim, &tmptqshape);
}
while(rectype != END_OF_DATA);
if (!gotstartrecordp)
G_THROW( ERR_MSG("JB2Image.no_start") );
jim.compress();
}
// CODE JB2IMAGE RECORD
void
JB2Dict::JB2Codec::code_record(
int &rectype, const GP<JB2Image> &gjim, JB2Shape *xjshp, JB2Blit *jblt)
{
GP<GBitmap> bm;
GP<GBitmap> cbm;
int tqshapeno = -1;
int match;
// Code record type
code_record_type(rectype);
// Pre-coding actions
switch(rectype)
{
case NEW_MARK:
case NEW_MARK_LIBRARY_ONLY:
case NEW_MARK_IMAGE_ONLY:
case MATCHED_REFINE:
case MATCHED_REFINE_LIBRARY_ONLY:
case MATCHED_REFINE_IMAGE_ONLY:
case NON_MARK_DATA:
{
if(!xjshp)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Shape &jshp=*xjshp;
if (!encoding)
{
jshp.bits = GBitmap::create();
jshp.tqparent = -1;
if (rectype == NON_MARK_DATA)
jshp.tqparent = -2;
}
bm = jshp.bits;
break;
}
}
// Coding actions
switch (rectype)
{
case START_OF_DATA:
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
code_image_size (jim);
code_eventual_lossless_refinement ();
if (! encoding)
init_library(jim);
break;
}
case NEW_MARK:
{
code_absolute_mark_size (*bm, 4);
code_bitmap_directly (*bm);
code_relative_location (jblt, bm->rows(), bm->columns() );
break;
}
case NEW_MARK_LIBRARY_ONLY:
{
code_absolute_mark_size (*bm, 4);
code_bitmap_directly (*bm);
break;
}
case NEW_MARK_IMAGE_ONLY:
{
code_absolute_mark_size (*bm, 3);
code_bitmap_directly (*bm);
code_relative_location (jblt, bm->rows(), bm->columns() );
break;
}
case MATCHED_REFINE:
{
if(!xjshp || !gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Shape &jshp=*xjshp;
JB2Image &jim=*gjim;
match = code_match_index (jshp.tqparent, jim);
cbm = jim.get_tqshape(jshp.tqparent).bits;
LibRect &l = libinfo[match];
code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4);
code_bitmap_by_cross_coding (*bm, cbm, match);
code_relative_location (jblt, bm->rows(), bm->columns() );
break;
}
case MATCHED_REFINE_LIBRARY_ONLY:
{
if(!xjshp||!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
JB2Shape &jshp=*xjshp;
match = code_match_index (jshp.tqparent, jim);
cbm = jim.get_tqshape(jshp.tqparent).bits;
LibRect &l = libinfo[match];
code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4);
break;
}
case MATCHED_REFINE_IMAGE_ONLY:
{
if(!xjshp||!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
JB2Shape &jshp=*xjshp;
match = code_match_index (jshp.tqparent, jim);
cbm = jim.get_tqshape(jshp.tqparent).bits;
LibRect &l = libinfo[match];
code_relative_mark_size (*bm, l.right-l.left+1, l.top-l.bottom+1, 4);
code_bitmap_by_cross_coding (*bm, cbm, match);
code_relative_location (jblt, bm->rows(), bm->columns() );
break;
}
case MATCHED_COPY:
{
int temp;
if (encoding) temp = jblt->tqshapeno;
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
match = code_match_index (temp, jim);
if (!encoding) jblt->tqshapeno = temp;
bm = jim.get_tqshape(jblt->tqshapeno).bits;
LibRect &l = libinfo[match];
jblt->left += l.left;
jblt->bottom += l.bottom;
if (jim.reproduce_old_bug)
code_relative_location (jblt, bm->rows(), bm->columns() );
else
code_relative_location (jblt, l.top-l.bottom+1, l.right-l.left+1 );
jblt->left -= l.left;
jblt->bottom -= l.bottom;
break;
}
case NON_MARK_DATA:
{
code_absolute_mark_size (*bm, 3);
code_bitmap_directly (*bm);
code_absolute_location (jblt, bm->rows(), bm->columns() );
break;
}
case PRESERVED_COMMENT:
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
code_comment(jim.comment);
break;
}
case RETQUIRED_DICT_OR_RESET:
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
if (! gotstartrecordp)
// Indicates need for a tqshape dictionary
code_inherited_tqshape_count(jim);
else
// Reset all numerical contexts to zero
reset_numcoder();
break;
}
case END_OF_DATA:
{
break;
}
default:
{
G_THROW( ERR_MSG("JB2Image.unknown_type") );
}
}
// Post-coding action
if (!encoding)
{
// add tqshape to image
switch(rectype)
{
case NEW_MARK:
case NEW_MARK_LIBRARY_ONLY:
case NEW_MARK_IMAGE_ONLY:
case MATCHED_REFINE:
case MATCHED_REFINE_LIBRARY_ONLY:
case MATCHED_REFINE_IMAGE_ONLY:
case NON_MARK_DATA:
{
if(!xjshp||!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Shape &jshp=*xjshp;
tqshapeno = gjim->add_tqshape(jshp);
tqshape2lib.touch(tqshapeno);
tqshape2lib[tqshapeno] = -1;
break;
}
}
// add tqshape to library
switch(rectype)
{
case NEW_MARK:
case NEW_MARK_LIBRARY_ONLY:
case MATCHED_REFINE:
case MATCHED_REFINE_LIBRARY_ONLY:
if(!xjshp)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
add_library(tqshapeno, *xjshp);
break;
}
// make sure everything is compacted
// decompaction will occur automatically on cross-coding bitmaps
if (bm)
bm->compress();
// add blit to image
switch (rectype)
{
case NEW_MARK:
case NEW_MARK_IMAGE_ONLY:
case MATCHED_REFINE:
case MATCHED_REFINE_IMAGE_ONLY:
case NON_MARK_DATA:
jblt->tqshapeno = tqshapeno;
case MATCHED_COPY:
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
gjim->add_blit(* jblt);
break;
}
}
}
// CODE JB2IMAGE
void
JB2Dict::JB2Codec::Decode::code(const GP<JB2Image> &gjim)
{
if(!gjim)
{
G_THROW( ERR_MSG("JB2Image.bad_number") );
}
JB2Image &jim=*gjim;
// -------------------------
// THIS IS THE DECODING PART
// -------------------------
int rectype;
JB2Blit tmpblit;
JB2Shape tmptqshape;
do
{
code_record(rectype, gjim, &tmptqshape, &tmpblit);
}
while(rectype!=END_OF_DATA);
if (!gotstartrecordp)
G_THROW( ERR_MSG("JB2Image.no_start") );
jim.compress();
}
////////////////////////////////////////
//// HELPERS
////////////////////////////////////////
void
JB2Dict::JB2Codec::LibRect::compute_bounding_box(const GBitmap &bm)
{
// First lock the stuff.
GMonitorLock lock(bm.monitor());
// Get size
const int w = bm.columns();
const int h = bm.rows();
const int s = bm.rowsize();
// Right border
for(right=w-1;right >= 0;--right)
{
unsigned char const *p = bm[0] + right;
unsigned char const * const pe = p+(s*h);
for (;(p<pe)&&(!*p);p+=s)
continue;
if (p<pe)
break;
}
// Top border
for(top=h-1;top >= 0;--top)
{
unsigned char const *p = bm[top];
unsigned char const * const pe = p+w;
for (;(p<pe)&&(!*p); ++p)
continue;
if (p<pe)
break;
}
// Left border
for (left=0;left <= right;++left)
{
unsigned char const *p = bm[0] + left;
unsigned char const * const pe=p+(s*h);
for (;(p<pe)&&(!*p);p+=s)
continue;
if (p<pe)
break;
}
// Bottom border
for(bottom=0;bottom <= top;++bottom)
{
unsigned char const *p = bm[bottom];
unsigned char const * const pe = p+w;
for (;(p<pe)&&(!*p); ++p)
continue;
if (p<pe)
break;
}
}
GP<JB2Dict>
JB2Dict::create(void)
{
return new JB2Dict();
}
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif