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/GBitmap.cpp

1658 lines
43 KiB

//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: GBitmap.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
#include "GBitmap.h"
#include "ByteStream.h"
#include "GRect.h"
#include "GString.h"
#include "GThreads.h"
#include "GException.h"
#include <string.h>
// File "$Id: GBitmap.cpp,v 1.10 2004/04/17 23:56:11 leonb Exp $"
// - Author: Leon Bottou, 05/1997
#ifdef HAVE_NAMESPACES
namespace DJVU {
# ifdef NOT_DEFINED // Just to fool emacs c++ mode
}
#endif
#endif
// ----- constructor and destructor
GBitmap::~GBitmap()
{
}
void
GBitmap::destroy(void)
{
gbytes_data.resize(0);
bytes = 0;
grle.resize(0);
grlerows.resize(0);
rlelength = 0;
}
GBitmap::GBitmap()
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
}
GBitmap::GBitmap(int nrows, int ncolumns, int border)
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
G_TRY
{
init(nrows, ncolumns, border);
}
G_CATCH_ALL
{
destroy();
G_RETHROW;
}
G_ENDCATCH;
}
GBitmap::GBitmap(ByteStream &ref, int border)
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
G_TRY
{
init(ref, border);
}
G_CATCH_ALL
{
destroy();
G_RETHROW;
}
G_ENDCATCH;
}
GBitmap::GBitmap(const GBitmap &ref)
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
G_TRY
{
init(ref, ref.border);
}
G_CATCH_ALL
{
destroy();
G_RETHROW;
}
G_ENDCATCH;
}
GBitmap::GBitmap(const GBitmap &ref, int border)
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
G_TRY
{
init(ref, border);
}
G_CATCH_ALL
{
destroy();
G_RETHROW;
}
G_ENDCATCH;
}
GBitmap::GBitmap(const GBitmap &ref, const GRect &rect, int border)
: nrows(0), ncolumns(0), border(0),
bytes_per_row(0), grays(0), bytes(0), gbytes_data(bytes_data),
grle(rle), grlerows(rlerows), rlelength(0),
monitorptr(0)
{
G_TRY
{
init(ref, rect, border);
}
G_CATCH_ALL
{
destroy();
G_RETHROW;
}
G_ENDCATCH;
}
// ----- initialization
void
GBitmap::init(int arows, int acolumns, int aborder)
{
GMonitorLock lock(monitor());
destroy();
grays = 2;
nrows = arows;
ncolumns = acolumns;
border = aborder;
bytes_per_row = ncolumns + border;
int npixels = nrows * bytes_per_row + border;
gzerobuffer=zeroes(bytes_per_row + border);
if (npixels > 0)
{
gbytes_data.resize(npixels);
gbytes_data.clear();
bytes = bytes_data;
}
}
void
GBitmap::init(const GBitmap &ref, int aborder)
{
GMonitorLock lock(monitor());
if (this != &ref)
{
GMonitorLock lock(ref.monitor());
init(ref.nrows, ref.ncolumns, aborder);
grays = ref.grays;
unsigned char *row = bytes_data+border;
for (int n=0; n<nrows; n++, row+=bytes_per_row)
memcpy( (void*)row, (void*)ref[n], ncolumns );
}
else if (aborder > border)
{
minborder(aborder);
}
}
void
GBitmap::init(const GBitmap &ref, const GRect &rect, int border)
{
GMonitorLock lock(monitor());
// test bitmap physical equality
if (this == &ref)
{
GBitmap tmp;
tmp.grays = grays;
tmp.border = border;
tmp.bytes_per_row = bytes_per_row;
tmp.ncolumns = ncolumns;
tmp.nrows = nrows;
tmp.bytes = bytes;
tmp.gbytes_data.swap(gbytes_data);
tmp.grle.swap(grle);
bytes = 0 ;
init(tmp, rect, border);
}
else
{
GMonitorLock lock(ref.monitor());
// create empty bitmap
init(rect.height(), rect.width(), border);
grays = ref.grays;
// compute destination rectangle
GRect rect2(0, 0, ref.columns(), ref.rows() );
rect2.intersect(rect2, rect);
rect2.translate(-rect.xmin, -rect.ymin);
// copy bits
if (! rect2.isempty())
{
for (int y=rect2.ymin; y<rect2.ymax; y++)
{
unsigned char *dst = (*this)[y];
const unsigned char *src = ref[y+rect.ymin] + rect.xmin;
for (int x=rect2.xmin; x<rect2.xmax; x++)
dst[x] = src[x];
}
}
}
}
void
GBitmap::init(ByteStream &ref, int aborder)
{
GMonitorLock lock(monitor());
// Get magic number
char magic[2];
magic[0] = magic[1] = 0;
ref.readall((void*)magic, sizeof(magic));
char lookahead = '\n';
int acolumns = read_integer(lookahead, ref);
int arows = read_integer(lookahead, ref);
init(arows, acolumns, aborder);
// go reading file
if (magic[0]=='P')
{
switch(magic[1])
{
case '1':
grays = 2;
read_pbm_text(ref);
return;
case '2':
grays = 1 + read_integer(lookahead, ref);
if (grays > 256)
G_THROW("Cannot read PGM with depth greater than 8 bits.");
read_pgm_text(ref);
return;
case '4':
grays = 2;
read_pbm_raw(ref);
return;
case '5':
grays = 1 + read_integer(lookahead, ref);
if (grays > 256)
grays = 256;
read_pgm_raw(ref);
return;
}
}
else if (magic[0]=='R')
{
switch(magic[1])
{
case '4':
grays = 2;
read_rle_raw(ref);
return;
}
}
G_THROW( ERR_MSG("GBitmap.bad_format") );
}
void
GBitmap::donate_data(unsigned char *data, int w, int h)
{
destroy();
grays = 2;
nrows = h;
ncolumns = w;
border = 0;
bytes_per_row = w;
gbytes_data.replace(data,w*h);
bytes = bytes_data;
rlelength = 0;
}
void
GBitmap::donate_rle(unsigned char *rledata, unsigned int rledatalen, int w, int h)
{
destroy();
grays = 2;
nrows = h;
ncolumns = w;
border = 0;
bytes_per_row = w;
// rle = rledata;
grle.replace(rledata,rledatalen);
rlelength = rledatalen;
}
unsigned char *
GBitmap::take_data(size_t &offset)
{
GMonitorLock lock(monitor());
unsigned char *ret = bytes_data;
if (ret) offset = (size_t)border;
bytes_data=0;
return ret;
}
const unsigned char *
GBitmap::get_rle(unsigned int &rle_length)
{
if(!rle)
compress();
rle_length=rlelength;
return rle;
}
// ----- compression
void
GBitmap::compress()
{
if (grays > 2)
G_THROW( ERR_MSG("GBitmap.cant_compress") );
GMonitorLock lock(monitor());
if (bytes)
{
grle.resize(0);
grlerows.resize(0);
rlelength = encode(rle,grle);
if (rlelength)
{
gbytes_data.resize(0);
bytes = 0;
}
}
}
void
GBitmap::uncompress()
{
GMonitorLock lock(monitor());
if (!bytes && rle)
decode(rle);
}
unsigned int
GBitmap::get_memory_usage() const
{
unsigned long usage = sizeof(GBitmap);
if (bytes)
usage += nrows * bytes_per_row + border;
if (rle)
usage += rlelength;
return usage;
}
void
GBitmap::minborder(int minimum)
{
if (border < minimum)
{
GMonitorLock lock(monitor());
if (border < minimum)
{
if (bytes)
{
GBitmap tmp(*this, minimum);
bytes_per_row = tmp.bytes_per_row;
tmp.gbytes_data.swap(gbytes_data);
bytes = bytes_data;
tmp.bytes = 0;
}
border = minimum;
gzerobuffer=zeroes(border + ncolumns + border);
}
}
}
#define NMONITORS 8
static GMonitor monitors[NMONITORS];
void
GBitmap::share()
{
if (!monitorptr)
{
unsigned long x = (unsigned long)this;
monitorptr = &monitors[(x^(x>>5)) % NMONITORS];
}
}
// ----- gray levels
void
GBitmap::set_grays(int ngrays)
{
if (ngrays<2 || ngrays>256)
G_THROW( ERR_MSG("GBitmap.bad_levels") );
// set gray levels
GMonitorLock lock(monitor());
grays = ngrays;
if (ngrays>2 && !bytes)
uncompress();
}
void
GBitmap::change_grays(int ngrays)
{
GMonitorLock lock(monitor());
// set number of grays
int ng = ngrays - 1;
int og = grays - 1;
set_grays(ngrays);
// setup conversion table
unsigned char conv[256];
for (int i=0; i<256; i++)
{
if (i > og)
conv[i] = ng;
else
conv[i] = (i*ng+og/2)/og;
}
// perform conversion
for (int row=0; row<nrows; row++)
{
unsigned char *p = (*this)[row];
for (int n=0; n<ncolumns; n++)
p[n] = conv[ p[n] ];
}
}
void
GBitmap::binarize_grays(int threshold)
{
GMonitorLock lock(monitor());
if (bytes)
for (int row=0; row<nrows; row++)
{
unsigned char *p = (*this)[row];
for(unsigned char const * const pend=p+ncolumns;p<pend;++p)
{
*p = (*p>threshold) ? 1 : 0;
}
}
grays = 2;
}
// ----- additive blitting
#undef min
#undef max
static inline int
min(int x, int y)
{
return (x < y ? x : y);
}
static inline int
max(int x, int y)
{
return (x > y ? x : y);
}
void
GBitmap::blit(const GBitmap *bm, int x, int y)
{
// Check boundaries
if ((x >= ncolumns) ||
(y >= nrows) ||
(x + (int)bm->columns() < 0) ||
(y + (int)bm->rows() < 0) )
return;
// Perform blit
GMonitorLock lock1(monitor());
GMonitorLock lock2(bm->monitor());
if (bm->bytes)
{
if (!bytes_data)
uncompress();
// Blit from bitmap
const unsigned char *srow = bm->bytes + bm->border;
unsigned char *drow = bytes_data + border + y*bytes_per_row + x;
for (int sr = 0; sr < bm->nrows; sr++)
{
if (sr+y>=0 && sr+y<nrows)
{
int sc = max(0, -x);
int sc1 = min(bm->ncolumns, ncolumns-x);
while (sc < sc1)
{
drow[sc] += srow[sc];
sc += 1;
}
}
srow += bm->bytes_per_row;
drow += bytes_per_row;
}
}
else if (bm->rle)
{
if (!bytes_data)
uncompress();
// Blit from rle
const unsigned char *runs = bm->rle;
unsigned char *drow = bytes_data + border + y*bytes_per_row + x;
int sr = bm->nrows - 1;
drow += sr * bytes_per_row;
int sc = 0;
char p = 0;
while (sr >= 0)
{
const int z = read_run(runs);
if (sc+z > bm->ncolumns)
G_THROW( ERR_MSG("GBitmap.lost_sync") );
int nc = sc + z;
if (p && sr+y>=0 && sr+y<nrows)
{
if (sc + x < 0)
sc = min(-x, nc);
while (sc < nc && sc + x<ncolumns)
drow[sc++] += 1;
}
sc = nc;
p = 1 - p;
if (sc >= bm->ncolumns)
{
p = 0;
sc = 0;
drow -= bytes_per_row;
sr -= 1;
}
}
}
}
void
GBitmap::blit(const GBitmap *bm, int xh, int yh, int subsample)
{
// Use code when no subsampling is necessary
if (subsample == 1)
{
blit(bm, xh, yh);
return;
}
// Check boundaries
if ((xh >= ncolumns * subsample) ||
(yh >= nrows * subsample) ||
(xh + (int)bm->columns() < 0) ||
(yh + (int)bm->rows() < 0) )
return;
// Perform subsampling blit
GMonitorLock lock1(monitor());
GMonitorLock lock2(bm->monitor());
if (bm->bytes)
{
if (!bytes_data)
uncompress();
// Blit from bitmap
int dr, dr1, zdc, zdc1;
euclidian_ratio(yh, subsample, dr, dr1);
euclidian_ratio(xh, subsample, zdc, zdc1);
const unsigned char *srow = bm->bytes + bm->border;
unsigned char *drow = bytes_data + border + dr*bytes_per_row;
for (int sr = 0; sr < bm->nrows; sr++)
{
if (dr>=0 && dr<nrows)
{
int dc = zdc;
int dc1 = zdc1;
for (int sc=0; sc < bm->ncolumns; sc++)
{
if (dc>=0 && dc<ncolumns)
drow[dc] += srow[sc];
if (++dc1 >= subsample)
{
dc1 = 0;
dc += 1;
}
}
}
// next line in source
srow += bm->bytes_per_row;
// next line fraction in destination
if (++dr1 >= subsample)
{
dr1 = 0;
dr += 1;
drow += bytes_per_row;
}
}
}
else if (bm->rle)
{
if (!bytes_data)
uncompress();
// Blit from rle
int dr, dr1, zdc, zdc1;
euclidian_ratio(yh+bm->nrows-1, subsample, dr, dr1);
euclidian_ratio(xh, subsample, zdc, zdc1);
const unsigned char *runs = bm->rle;
unsigned char *drow = bytes_data + border + dr*bytes_per_row;
int sr = bm->nrows -1;
int sc = 0;
char p = 0;
int dc = zdc;
int dc1 = zdc1;
while (sr >= 0)
{
int z = read_run(runs);
if (sc+z > bm->ncolumns)
G_THROW( ERR_MSG("GBitmap.lost_sync") );
int nc = sc + z;
if (dr>=0 && dr<nrows)
while (z>0 && dc<ncolumns)
{
int zd = subsample - dc1;
if (zd > z)
zd = z;
if (p && dc>=0)
drow[dc] += zd;
z -= zd;
dc1 += zd;
if (dc1 >= subsample)
{
dc1 = 0;
dc += 1;
}
}
// next fractional row
sc = nc;
p = 1 - p;
if (sc >= bm->ncolumns)
{
sc = 0;
dc = zdc;
dc1 = zdc1;
p = 0;
sr -= 1;
if (--dr1 < 0)
{
dr1 = subsample - 1;
dr -= 1;
drow -= bytes_per_row;
}
}
}
}
}
// ------ load bitmaps
unsigned int
GBitmap::read_integer(char &c, ByteStream &bs)
{
unsigned int x = 0;
// eat blank before integer
while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#')
{
if (c=='#')
do { } while (bs.read(&c,1) && c!='\n' && c!='\r');
c = 0;
bs.read(&c, 1);
}
// check integer
if (c<'0' || c>'9')
G_THROW( ERR_MSG("GBitmap.not_int") );
// eat integer
while (c>='0' && c<='9')
{
x = x*10 + c - '0';
c = 0;
bs.read(&c, 1);
}
return x;
}
void
GBitmap::read_pbm_text(ByteStream &bs)
{
unsigned char *row = bytes_data + border;
row += (nrows-1) * bytes_per_row;
for (int n = nrows-1; n>=0; n--)
{
for (int c = 0; c<ncolumns; c++)
{
char bit = 0;
bs.read(&bit,1);
while (bit==' ' || bit=='\t' || bit=='\r' || bit=='\n')
{
bit=0;
bs.read(&bit,1);
}
if (bit=='1')
row[c] = 1;
else if (bit=='0')
row[c] = 0;
else
G_THROW( ERR_MSG("GBitmap.bad_PBM") );
}
row -= bytes_per_row;
}
}
void
GBitmap::read_pgm_text(ByteStream &bs)
{
unsigned char *row = bytes_data + border;
row += (nrows-1) * bytes_per_row;
char lookahead = '\n';
for (int n = nrows-1; n>=0; n--)
{
for (int c = 0; c<ncolumns; c++)
row[c] = grays - 1 - read_integer(lookahead, bs);
row -= bytes_per_row;
}
}
void
GBitmap::read_pbm_raw(ByteStream &bs)
{
unsigned char *row = bytes_data + border;
row += (nrows-1) * bytes_per_row;
for (int n = nrows-1; n>=0; n--)
{
unsigned char acc = 0;
unsigned char mask = 0;
for (int c = 0; c<ncolumns; c++)
{
if (!mask)
{
bs.read(&acc, 1);
mask = (unsigned char)0x80;
}
if (acc & mask)
row[c] = 1;
else
row[c] = 0;
mask >>= 1;
}
row -= bytes_per_row;
}
}
void
GBitmap::read_pgm_raw(ByteStream &bs)
{
unsigned char *row = bytes_data + border;
row += (nrows-1) * bytes_per_row;
for (int n = nrows-1; n>=0; n--)
{
for (int c = 0; c<ncolumns; c++)
{
unsigned char x;
bs.read((void*)&x, 1);
row[c] = grays - 1 - x;
}
row -= bytes_per_row;
}
}
void
GBitmap::read_rle_raw(ByteStream &bs)
{
// interpret runs data
unsigned char h;
unsigned char p = 0;
unsigned char *row = bytes_data + border;
int n = nrows - 1;
row += n * bytes_per_row;
int c = 0;
while (n >= 0)
{
bs.read(&h, 1);
int x = h;
if (x >= (int)RUNOVERFLOWVALUE)
{
bs.read(&h, 1);
x = h + ((x - (int)RUNOVERFLOWVALUE) << 8);
}
if (c+x > ncolumns)
G_THROW( ERR_MSG("GBitmap.lost_sync") );
while (x-- > 0)
row[c++] = p;
p = 1 - p;
if (c >= ncolumns)
{
c = 0;
p = 0;
row -= bytes_per_row;
n -= 1;
}
}
}
// ------ save bitmaps
void
GBitmap::save_pbm(ByteStream &bs, int raw)
{
// check arguments
if (grays > 2)
G_THROW( ERR_MSG("GBitmap.cant_make_PBM") );
GMonitorLock lock(monitor());
// header
{
GUTF8String head;
head.format("P%c\n%d %d\n", (raw ? '4' : '1'), ncolumns, nrows);
bs.writall((void*)(const char *)head, head.length());
}
// body
if(raw)
{
if(!rle)
compress();
const unsigned char *runs=rle;
const unsigned char * const runs_end=rle+rlelength;
const int count=(ncolumns+7)>>3;
unsigned char *buf;
GPBuffer<unsigned char> gbuf(buf,count);
while(runs<runs_end)
{
rle_get_bitmap(ncolumns,runs,buf,false);
bs.writall(buf,count);
}
}else
{
if (!bytes)
uncompress();
const unsigned char *row = bytes + border;
int n = nrows - 1;
row += n * bytes_per_row;
while (n >= 0)
{
unsigned char eol='\n';
for (int c=0; c<ncolumns;)
{
unsigned char bit= (row[c] ? '1' : '0');
bs.write((void*)&bit, 1);
c += 1;
if (c==ncolumns || (c&(int)RUNMSBMASK)==0)
bs.write((void*)&eol, 1);
}
// next row
row -= bytes_per_row;
n -= 1;
}
}
}
void
GBitmap::save_pgm(ByteStream &bs, int raw)
{
// checks
GMonitorLock lock(monitor());
if (!bytes)
uncompress();
// header
GUTF8String head;
head.format("P%c\n%d %d\n%d\n", (raw ? '5' : '2'), ncolumns, nrows, grays-1);
bs.writall((void*)(const char *)head, head.length());
// body
const unsigned char *row = bytes + border;
int n = nrows - 1;
row += n * bytes_per_row;
while (n >= 0)
{
if (raw)
{
for (int c=0; c<ncolumns; c++)
{
char x = grays - 1 - row[c];
bs.write((void*)&x, 1);
}
}
else
{
unsigned char eol='\n';
for (int c=0; c<ncolumns; )
{
head.format("%d ", grays - 1 - row[c]);
bs.writall((void*)(const char *)head, head.length());
c += 1;
if (c==ncolumns || (c&0x1f)==0)
bs.write((void*)&eol, 1);
}
}
row -= bytes_per_row;
n -= 1;
}
}
void
GBitmap::save_rle(ByteStream &bs)
{
// checks
if (ncolumns==0 || nrows==0)
G_THROW( ERR_MSG("GBitmap.not_init") );
GMonitorLock lock(monitor());
if (grays > 2)
G_THROW( ERR_MSG("GBitmap.cant_make_PBM") );
// header
GUTF8String head;
head.format("R4\n%d %d\n", ncolumns, nrows);
bs.writall((void*)(const char *)head, head.length());
// body
if (rle)
{
bs.writall((void*)rle, rlelength);
}
else
{
unsigned char *runs = 0;
GPBuffer<unsigned char> gruns(runs);
int size = encode(runs,gruns);
bs.writall((void*)runs, size);
}
}
// ------ runs
void
GBitmap::makerows(
int nrows, const int ncolumns, unsigned char *runs, unsigned char *rlerows[])
{
while (nrows-- > 0)
{
rlerows[nrows] = runs;
int c;
for(c=0;c<ncolumns;c+=GBitmap::read_run(runs))
EMPTY_LOOP;
if (c > ncolumns)
G_THROW( ERR_MSG("GBitmap.lost_sync2") );
}
}
void
GBitmap::rle_get_bitmap (
const int ncolumns,
const unsigned char *&runs,
unsigned char *bitmap,
const bool invert )
{
const int obyte_def=invert?0xff:0;
const int obyte_ndef=invert?0:0xff;
int mask=0x80,obyte=0;
for(int c=ncolumns;c > 0 ;)
{
int x=read_run(runs);
c-=x;
while((x--)>0)
{
if(!(mask>>=1))
{
*(bitmap++) = obyte^obyte_def;
obyte=0;
mask=0x80;
for(;x>=8;x-=8)
{
*(bitmap++)=obyte_def;
}
}
}
if(c>0)
{
int x=read_run(runs);
c-=x;
while((x--)>0)
{
obyte|=mask;
if(!(mask>>=1))
{
*(bitmap++)=obyte^obyte_def;
obyte=0;
mask=0x80;
for(;(x>8);x-=8)
*(bitmap++)=obyte_ndef;
}
}
}
}
if(mask != 0x80)
{
*(bitmap++)=obyte^obyte_def;
}
}
int
GBitmap::rle_get_bits(int rowno, unsigned char *bits) const
{
GMonitorLock lock(monitor());
if (!rle)
return 0;
if (rowno<0 || rowno>=nrows)
return 0;
if (!rlerows)
{
const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows);
makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows));
}
int n = 0;
int p = 0;
int c = 0;
unsigned char *runs = rlerows[rowno];
while (c < ncolumns)
{
const int x=read_run(runs);
if ((c+=x)>ncolumns)
c = ncolumns;
while (n<c)
bits[n++] = p;
p = 1-p;
}
return n;
}
int
GBitmap::rle_get_runs(int rowno, int *rlens) const
{
GMonitorLock lock(monitor());
if (!rle)
return 0;
if (rowno<0 || rowno>=nrows)
return 0;
if (!rlerows)
{
const_cast<GPBuffer<unsigned char *> &>(grlerows).resize(nrows);
makerows(nrows,ncolumns,rle,const_cast<unsigned char **>(rlerows));
}
int n = 0;
int d = 0;
int c = 0;
unsigned char *runs = rlerows[rowno];
while (c < ncolumns)
{
const int x=read_run(runs);
if (n>0 && !x)
{
n--;
d = d-rlens[n];
}
else
{
rlens[n++] = (c+=x)-d;
d = c;
}
}
return n;
}
int
GBitmap::rle_get_rect(GRect &rect) const
{
GMonitorLock lock(monitor());
if (!rle)
return 0;
int area = 0;
unsigned char *runs = rle;
rect.xmin = ncolumns;
rect.ymin = nrows;
rect.xmax = 0;
rect.ymax = 0;
int r = nrows;
while (--r >= 0)
{
int p = 0;
int c = 0;
int n = 0;
while (c < ncolumns)
{
const int x=read_run(runs);
if(x)
{
if (p)
{
if (c < rect.xmin)
rect.xmin = c;
if ((c += x) > rect.xmax)
rect.xmax = c-1;
n += x;
}
else
{
c += x;
}
}
p = 1-p;
}
area += n;
if (n)
{
rect.ymin = r;
if (r > rect.ymax)
rect.ymax = r;
}
}
if (area==0)
rect.clear();
return area;
}
// ------ helpers
int
GBitmap::encode(unsigned char *&pruns,GPBuffer<unsigned char> &gpruns) const
{
// uncompress rle information
if (nrows==0 || ncolumns==0)
{
gpruns.resize(0);
return 0;
}
if (!bytes)
{
unsigned char *runs;
GPBuffer<unsigned char> gruns(runs,rlelength);
memcpy((void*)runs, rle, rlelength);
gruns.swap(gpruns);
return rlelength;
}
gpruns.resize(0);
// create run array
int pos = 0;
int maxpos = 1024 + ncolumns + ncolumns;
unsigned char *runs;
GPBuffer<unsigned char> gruns(runs,maxpos);
// encode bitmap as rle
const unsigned char *row = bytes + border;
int n = nrows - 1;
row += n * bytes_per_row;
while (n >= 0)
{
if (maxpos < pos+ncolumns+ncolumns+2)
{
maxpos += 1024 + ncolumns + ncolumns;
gruns.resize(maxpos);
}
unsigned char *runs_pos=runs+pos;
const unsigned char * const runs_pos_start=runs_pos;
append_line(runs_pos,row,ncolumns);
pos+=(size_t)runs_pos-(size_t)runs_pos_start;
row -= bytes_per_row;
n -= 1;
}
// return result
gruns.resize(pos);
gpruns.swap(gruns);
return pos;
}
void
GBitmap::decode(unsigned char *runs)
{
// initialize pixel array
if (nrows==0 || ncolumns==0)
G_THROW( ERR_MSG("GBitmap.not_init") );
bytes_per_row = ncolumns + border;
if (runs==0)
G_THROW( ERR_MSG("GBitmap.null_arg") );
int npixels = nrows * bytes_per_row + border;
if (!bytes_data)
{
gbytes_data.resize(npixels);
bytes = bytes_data;
}
gbytes_data.clear();
gzerobuffer=zeroes(bytes_per_row + border);
// interpret runs data
int c, n;
unsigned char p = 0;
unsigned char *row = bytes_data + border;
n = nrows - 1;
row += n * bytes_per_row;
c = 0;
while (n >= 0)
{
int x = read_run(runs);
if (c+x > ncolumns)
G_THROW( ERR_MSG("GBitmap.lost_sync2") );
while (x-- > 0)
row[c++] = p;
p = 1 - p;
if (c >= ncolumns)
{
c = 0;
p = 0;
row -= bytes_per_row;
n -= 1;
}
}
// Free rle data possibly attached to this bitmap
grle.resize(0);
grlerows.resize(0);
rlelength = 0;
#ifndef NDEBUG
check_border();
#endif
}
class GBitmap::ZeroBuffer : public GPEnabled
{
public:
ZeroBuffer(const unsigned int zerosize);
unsigned char *zerobuffer;
GPBuffer<unsigned char> gzerobuffer;
};
GBitmap::ZeroBuffer::ZeroBuffer(const unsigned int zerosize)
: gzerobuffer(zerobuffer,zerosize)
{
gzerobuffer.clear();
GBitmap::zerobuffer=zerobuffer;
GBitmap::zerosize=zerosize;
}
static const unsigned char static_zerobuffer[]=
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 32
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 64
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 96
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 128
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 160
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 192
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 234
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 256
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 288
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 320
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 352
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 384
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 416
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 448
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 480
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 512
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 544
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 576
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 608
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 640
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 672
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 704
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 736
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 768
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 800
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 832
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 864
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 896
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 928
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 960
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 992
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+32
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+64
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+96
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+128
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+160
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+192
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+234
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+256
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+288
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+320
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+352
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+384
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+416
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+448
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+480
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+512
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+544
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+576
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+608
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+640
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+672
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+704
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+736
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+768
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+800
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+832
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+864
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+896
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+928
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+960
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 1024+992
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+32
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+64
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+96
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+128
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+160
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+192
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+234
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+256
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+288
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+320
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+352
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+384
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+416
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+448
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+480
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+512
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+544
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+576
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+608
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+640
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+672
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+704
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+736
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+768
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+800
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+832
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+864
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+896
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+928
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+960
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 2048+992
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+32
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+64
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+96
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+128
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+160
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+192
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+234
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+256
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+288
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+320
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+352
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+384
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+416
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+448
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+480
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+512
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+544
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+576
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+608
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+640
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+672
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+704
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+736
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+768
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+800
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+832
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+864
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+896
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+928
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+960
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 3072+992
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // 4096
int GBitmap::zerosize = sizeof(static_zerobuffer);
unsigned char *GBitmap::zerobuffer=const_cast<unsigned char *>(static_zerobuffer);
GP<GBitmap::ZeroBuffer>
GBitmap::zeroes(int required)
{
GMonitorLock lock(&monitors[0]); // any monitor would do
static GP<GBitmap::ZeroBuffer> gzerobuffer;
if (zerosize < required)
{
int z;
for(z=zerosize;z<required;z <<= 1)
EMPTY_LOOP;
z=(z+0xfff)&(~0xfff);
gzerobuffer=new GBitmap::ZeroBuffer((unsigned int)z);
}
return gzerobuffer;
}
// Fills a bitmap with the given value
void
GBitmap::fill(unsigned char value)
{
GMonitorLock lock(monitor());
for(unsigned int y=0; y<rows(); y++)
{
unsigned char* bm_y = (*this)[y];
for(unsigned int x=0; x<columns(); x++)
bm_y[x] = value;
}
}
void
GBitmap::append_long_run(unsigned char *&data, int count)
{
while (count > MAXRUNSIZE)
{
data[0] = data[1] = 0xff;
data[2] = 0;
data += 3;
count -= MAXRUNSIZE;
}
if (count < RUNOVERFLOWVALUE)
{
data[0] = count;
data += 1;
}
else
{
data[0] = (count>>8) + GBitmap::RUNOVERFLOWVALUE;
data[1] = (count & 0xff);
data += 2;
}
}
void
GBitmap::append_line(unsigned char *&data,const unsigned char *row,
const int rowlen,bool invert)
{
const unsigned char *rowend=row+rowlen;
bool p=!invert;
while(row<rowend)
{
int count=0;
if ((p=!p))
{
if(*row)
for(++count,++row;(row<rowend)&&*row;++count,++row)
EMPTY_LOOP;
}
else if(!*row)
{
for(++count,++row;(row<rowend)&&!*row;++count,++row)
EMPTY_LOOP;
}
append_run(data,count);
}
}
#if 0
static inline int
GetRowTDLRNR(
GBitmap &bit,const int row, const unsigned char *&startptr,
const unsigned char *&stopptr)
{
stopptr=(startptr=bit[row])+bit.columns();
return 1;
}
static inline int
GetRowTDLRNR(
GBitmap &bit,const int row, const unsigned char *&startptr,
const unsigned char *&stopptr)
{
stopptr=(startptr=bit[row])+bit.columns();
return 1;
}
static inline int
GetRowTDRLNR(
GBitmap &bit,const int row, const unsigned char *&startptr,
const unsigned char *&stopptr)
{
startptr=(stopptr=bit[row]-1)+bit.columns();
return -1;
}
#endif // 0
GP<GBitmap>
GBitmap::rotate(int count)
{
GP<GBitmap> newbitmap=this;
if((count%=4))
{
if( count & 0x01 )
{
newbitmap = new GBitmap(ncolumns, nrows);
}else
{
newbitmap = new GBitmap(nrows, ncolumns);
}
GMonitorLock lock(monitor());
if (!bytes_data)
uncompress();
GBitmap &dbitmap = *newbitmap;
dbitmap.set_grays(grays);
switch(count)
{
case 1: // rotate 90 counter clockwise
{
const int lastrow = dbitmap.rows()-1;
for(int y=0; y<nrows; y++)
{
const unsigned char *r=operator[] (y);
for(int x=0,xnew=lastrow;xnew>=0; x++,xnew--)
{
dbitmap[xnew][y] = r[x];
}
}
}
break;
case 2: // rotate 180 counter clockwise
{
const int lastrow = dbitmap.rows()-1;
const int lastcolumn = dbitmap.columns()-1;
for(int y=0,ynew=lastrow;ynew>=0; y++,ynew--)
{
const unsigned char *r=operator[] (y);
unsigned char *d=dbitmap[ynew];
for(int xnew=lastcolumn;xnew>=0; r++,--xnew)
{
d[xnew] = *r;
}
}
}
break;
case 3: // rotate 270 counter clockwise
{
const int lastcolumn = dbitmap.columns()-1;
for(int y=0,ynew=lastcolumn;ynew>=0;y++,ynew--)
{
const unsigned char *r=operator[] (y);
for(int x=0; x<ncolumns; x++)
{
dbitmap[x][ynew] = r[x];
}
}
}
break;
}
if(grays == 2)
{
compress();
dbitmap.compress();
}
}
return newbitmap;
}
#ifndef NDEBUG
void
GBitmap::check_border() const
{int col ;
if (bytes)
{
const unsigned char *p = (*this)[-1];
for (col=-border; col<ncolumns+border; col++)
if (p[col])
G_THROW( ERR_MSG("GBitmap.zero_damaged") );
for (int row=0; row<nrows; row++)
{
p = (*this)[row];
for (col=-border; col<0; col++)
if (p[col])
G_THROW( ERR_MSG("GBitmap.left_damaged") );
for (col=ncolumns; col<ncolumns+border; col++)
if (p[col])
G_THROW( ERR_MSG("GBitmap.right_damaged") );
}
}
}
#endif
#ifdef HAVE_NAMESPACES
}
# ifndef NOT_USING_DJVU_NAMESPACE
using namespace DJVU;
# endif
#endif