//C- -*- C++ -*- //C- ------------------------------------------------------------------- //C- DjVuLibre-3.5 //C- Copyright (c) 2002 Leon Bottou and Yann Le Cun. //C- Copyright (c) 2001 AT&T //C- //C- This software is subject to, and may be distributed under, the //C- GNU General Public License, either Version 2 of the license, //C- or (at your option) any later version. The license should have //C- accompanied the software or you may obtain a copy of the license //C- from the Free Software Foundation at http://www.fsf.org . //C- //C- This program is distributed in the hope that it will be useful, //C- but WITHOUT ANY WARRANTY; without even the implied warranty of //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //C- GNU General Public License for more details. //C- //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from //C- Lizardtech Software. Lizardtech Software has authorized us to //C- replace the original DjVu(r) Reference Library notice by the following //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu): //C- //C- ------------------------------------------------------------------ //C- | DjVu (r) Reference Library (v. 3.5) //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved. //C- | The DjVu Reference Library is protected by U.S. Pat. No. //C- | 6,058,214 and patents pending. //C- | //C- | This software is subject to, and may be distributed under, the //C- | GNU General Public License, either Version 2 of the license, //C- | or (at your option) any later version. The license should have //C- | accompanied the software or you may obtain a copy of the license //C- | from the Free Software Foundation at http://www.fsf.org . //C- | //C- | The computer code originally released by LizardTech under this //C- | license and unmodified by other parties is deemed "the LIZARDTECH //C- | ORIGINAL CODE." Subject to any third party intellectual property //C- | claims, LizardTech grants recipient a worldwide, royalty-free, //C- | non-exclusive license to make, use, sell, or otherwise dispose of //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU //C- | General Public License. This grant only confers the right to //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to //C- | the extent such infringement is reasonably necessary to enable //C- | recipient to make, have made, practice, sell, or otherwise dispose //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to //C- | any greater extent that may be necessary to utilize further //C- | modifications or combinations. //C- | //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. //C- +------------------------------------------------------------------ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if NEED_GNUG_PRAGMAS # pragma implementation #endif #include "GBitmap.h" #include "ByteStream.h" #include "GRect.h" #include "GString.h" #include "GThreads.h" #include "GException.h" #include #include #include // - 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) { if (arows != (unsigned short) arows || acolumns != (unsigned short) acolumns || acolumns + aborder != (unsigned short)(acolumns + aborder)) G_THROW("Illegal arguments"); 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 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 65535) G_THROW("Cannot read PGM with depth greater than 16 bits."); grays = (maxval>255 ? 256 : maxval+1); read_pgm_text(ref, maxval); return; case '4': grays = 2; read_pbm_raw(ref); return; case '5': maxval = read_integer(lookahead, ref); if (maxval > 65535) G_THROW("Cannot read PGM with depth greater than 16 bits."); grays = (maxval>255 ? 256 : maxval+1); read_pgm_raw(ref, maxval); 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) { size_t x = (size_t)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; rowthreshold) ? 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+yncolumns, 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= 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 && drncolumns; sc++) { if (dc>=0 && dc= 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 && dr0 && dc 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 ramp(0, maxval); for (int i=0; i<=maxval; i++) ramp[i] = (i=0; n--) { for (int c = 0; c=0; n--) { unsigned char acc = 0; unsigned char mask = 0; for (int c = 0; c>= 1; } row -= bytes_per_row; } } void GBitmap::read_pgm_raw(ByteStream &bs, int maxval) { int maxbin = (maxval>255) ? 65536 : 256; GTArray ramp(0, maxbin-1); for (int i=0; i=0; n--) { if (maxbin > 256) { for (int c = 0; c= 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 gbuf(buf,count); while(runs= 0) { unsigned char eol='\n'; for (int c=0; c= 0) { if (raw) { for (int c=0; c 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 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) 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 &>(grlerows).resize(nrows); makerows(nrows,ncolumns,rle,const_cast(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=nrows) return 0; if (!rlerows) { const_cast &>(grlerows).resize(nrows); makerows(nrows,ncolumns,rle,const_cast(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 &gpruns) const { // uncompress rle information if (nrows==0 || ncolumns==0) { gpruns.resize(0); return 0; } if (!bytes) { unsigned char *runs; GPBuffer 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 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 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(static_zerobuffer); GP GBitmap::zeroes(int required) { GMonitorLock lock(&monitors[0]); // any monitor would do static GP gzerobuffer; if (zerosize < required) { int z; for(z=zerosize;z 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 GBitmap::rotate(int count) { GP newbitmap=this; count = count & 3; if(count) { 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 3: // rotate 90 counter clockwise { const int lastrow = dbitmap.rows()-1; for(int y=0; y=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 1: // 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