//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: GPixmap.cpp,v 1.12 2004/08/06 15:11:29 leonb Exp $ // $Name: release_3_5_15 $ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if NEED_GNUG_PRAGMAS # pragma implementation #endif // -- Implements class PIXMAP // Author: Leon Bottou 07/1997 #include "GPixmap.h" #include "GString.h" #include "GException.h" #include "ByteStream.h" #include "GRect.h" #include "GBitmap.h" #include "GThreads.h" #include "Arrays.h" #include "JPEGDecoder.h" #include #include #include #ifdef HAVE_NAMESPACES namespace DJVU { # ifdef NOT_DEFINED // Just to fool emacs c++ mode } #endif #endif ////////////////////////////////////////////////// // ------- predefined colors ////////////////////////////////////////////////// const GPixel GPixel::WHITE = { 255, 255, 255 }; const GPixel GPixel::BLACK = { 0, 0, 0 }; const GPixel GPixel::BLUE = { 255, 0, 0 }; const GPixel GPixel::GREEN = { 0, 255, 0 }; const GPixel GPixel::RED = { 0, 0, 255 }; ////////////////////////////////////////////////// // ----- utilities ////////////////////////////////////////////////// static const GPixel * new_gray_ramp(int grays,GPixel *ramp) { int color = 0xff0000; int decrement = color / (grays-1); for (int i=0; i> 16; ramp[i].b = level; ramp[i].g = level; ramp[i].r = level; color -= decrement; } return ramp; } static inline int mini(int x, int y) { return (x < y ? x : y); } static inline int maxi(int x, int y) { return (x > y ? x : y); } static inline void euclidian_ratio(int a, int b, int &q, int &r) { q = a / b; r = a - b*q; if (r < 0) { q -= 1; r += b; } } ////////////////////////////////////////////////// // global lock used by some rare operations ////////////////////////////////////////////////// static GMonitor &pixmap_monitor() { static GMonitor xpixmap_monitor; return xpixmap_monitor; } ////////////////////////////////////////////////// // constructors and destructors ////////////////////////////////////////////////// GPixmap::~GPixmap() { delete [] pixels_data; } void GPixmap::destroy(void) { delete [] pixels_data; pixels = pixels_data = 0; } GPixmap::GPixmap() : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { } GPixmap::GPixmap(int nrows, int ncolumns, const GPixel *filler) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(nrows, ncolumns, filler); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } GPixmap::GPixmap(ByteStream &bs) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(bs); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } GPixmap::GPixmap(const GBitmap &ref) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(ref, 0); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } GPixmap::GPixmap(const GBitmap &ref, const GRect &rect) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(ref, rect, 0); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } GPixmap::GPixmap(const GPixmap &ref) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(ref); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } GPixmap::GPixmap(const GPixmap &ref, const GRect &rect) : nrows(0), ncolumns(0), pixels(0), pixels_data(0) { G_TRY { init(ref, rect); } G_CATCH_ALL { destroy(); G_RETHROW; } G_ENDCATCH; } ////////////////////////////////////////////////// // Initialization ////////////////////////////////////////////////// void GPixmap::init(int arows, int acolumns, const GPixel *filler) { destroy(); nrows = arows; ncolumns = acolumns; nrowsize = acolumns; int npix = nrows * nrowsize; if (npix > 0) { pixels = pixels_data = new GPixel[npix]; if (filler) { while (--npix>=0) pixels_data[npix] = *filler; } } } void GPixmap::init(const GBitmap &ref, const GPixel *userramp) { init(ref.rows(), ref.columns(), 0); GPixel *xramp; GPBuffer gxramp(xramp); if (nrows>0 && ncolumns>0) { // Create pixel ramp const GPixel *ramp = userramp; if (!userramp) { gxramp.resize(256); gxramp.clear(); ramp = new_gray_ramp(ref.get_grays(),xramp); } // Copy pixels for (int y=0; y gxramp(xramp); // allocate ramp const GPixel *ramp = userramp; if (!userramp) { gxramp.resize(256); gxramp.clear(); ramp = new_gray_ramp(ref.get_grays(),xramp); } // copy pixels for (int y=rect2.ymin; y0 && ncolumns>0) { for (int y=0; y'9') G_THROW( ERR_MSG("GPixmap.no_int") ); // eat integer while (c>='0' && c<='9') { x = x*10 + c - '0'; c = 0; bs.read(&c, 1); } return x; } void GPixmap::init(ByteStream &bs) { // Read header int raw = 0; char magic[2]; magic[0] = magic[1] = 0; bs.readall((void*)magic, sizeof(magic)); if (magic[0]=='P' && magic[1]=='3') { raw = 0; }else if (magic[0]=='P' && magic[1]=='6') { raw = 1; }else { #ifdef NEED_JPEG_DECODER bs.seek(0L); JPEGDecoder::decode(bs,*this); return; #else // NEED_JPEG_DECODER G_THROW( ERR_MSG("GPixmap.unk_PPM") ); #endif // NEED_JPEG_DECODER } // Read image size char lookahead = '\n'; int acolumns = read_integer(lookahead, bs); int arows = read_integer(lookahead, bs); int maxval = read_integer(lookahead, bs); if (maxval > 255) G_THROW("Cannot read PPM with depth greater than 24 bits."); init(arows, acolumns, 0); // Read image data if (raw) { GTArray line(ncolumns*3); for (int y=nrows-1; y>=0; y--) { GPixel *p = (*this)[y]; unsigned char *rgb = &line[0]; if ( bs.readall((void*)rgb, ncolumns*3) < (size_t)(ncolumns*3)) G_THROW( ByteStream::EndOfFile ); for (int x=0; x=0; y--) { GPixel *p = (*this)[y]; for (int x=0; x0 && maxval<255) { char table[256]; for (int i=0; i<256; i++) table[i] = (i xrgb(rowsize); for (int y=nrows-1; y>=0; y--) { const GPixel *p = (*this)[y]; unsigned char *d = xrgb; for (int x=0; x=0; y--) { const GPixel *p = (*this)[y]; unsigned char eol='\n'; for (int x=0; x10.0) G_THROW( ERR_MSG("GPixmap.bad_param") ); if (gamma<1.001 && gamma>0.999) { // Trivial correction for (int i=0; i<256; i++) gtable[i] = i; } else { // Must compute the correction for (int i=0; i<256; i++) { double x = (double)(i)/255.0; #ifdef BEZIERGAMMA double t = ( sqrt(1.0+(gamma*gamma-1.0)*x) - 1.0 ) / (gamma - 1.0); x = ( (1.0 - gamma)*t + 2.0 * gamma ) * t / (gamma + 1.0); #else x = pow(x, 1.0/gamma); #endif gtable[i] = (int) floor(255.0 * x + 0.5); } // Make sure that min and max values are exactly black or white gtable[0] = 0; gtable[255] = 255; } } static void color_correction_table_cache(double gamma, unsigned char gtable[256] ) { // Compute color correction table if (gamma<1.001 && gamma>0.999) { color_correction_table(gamma, gtable); } else { static double lgamma = -1.0; static unsigned char ctable[256]; GMonitorLock lock(&pixmap_monitor()); if (gamma != lgamma) { color_correction_table(gamma, ctable); lgamma = gamma; } memcpy(gtable, ctable, 256*sizeof(unsigned char)); } } void GPixmap::color_correct(double gamma_correction) { // Trivial corrections if (gamma_correction>0.999 && gamma_correction<1.001) return; // Compute correction table unsigned char gtable[256]; color_correction_table_cache(gamma_correction, gtable); // Perform correction for (int y=0; yr = gtable[ pix->r ]; pix->g = gtable[ pix->g ]; pix->b = gtable[ pix->b ]; } } } void GPixmap::color_correct(double gamma_correction, GPixel *pix, int npixels) { // Trivial corrections if (gamma_correction>0.999 && gamma_correction<1.001) return; // Compute correction table unsigned char gtable[256]; color_correction_table_cache(gamma_correction, gtable); // Perform correction while (--npixels>=0) { pix->r = gtable[pix->r]; pix->g = gtable[pix->g]; pix->b = gtable[pix->b]; pix++; } } ////////////////////////////////////////////////// // Dithering ////////////////////////////////////////////////// void GPixmap::ordered_666_dither(int xmin, int ymin) { static unsigned char quantize[256+0x33+0x33]; static unsigned char *quant = quantize + 0x33; static char dither_ok = 0; static short dither[16][16] = { { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } }; // Prepare tables if (!dither_ok) { int i, j; for (i=0; i<16; i++) for (j=0; j<16; j++) dither[i][j] = ((255 - 2*dither[i][j]) * 0x33) / 512; j = -0x33; for (i=0x19; i<256; i+=0x33) while (j <= i) quant[j++] = i-0x19; assert(i-0x19 == 0xff); while (j< 256+0x33) quant[j++] = i-0x19; dither_ok = 1; } // Go dithering for (int y=0; yr = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; } } } void GPixmap::ordered_32k_dither(int xmin, int ymin) { static unsigned char quantize[256+8+8]; static unsigned char *quant = quantize + 8; static char dither_ok = 0; static short dither[16][16] = { { 0,192, 48,240, 12,204, 60,252, 3,195, 51,243, 15,207, 63,255 }, { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 }, { 32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 }, { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 }, { 8,200, 56,248, 4,196, 52,244, 11,203, 59,251, 7,199, 55,247 }, { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 }, { 40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 }, { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 }, { 2,194, 50,242, 14,206, 62,254, 1,193, 49,241, 13,205, 61,253 }, { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 }, { 34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 }, { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 }, { 10,202, 58,250, 6,198, 54,246, 9,201, 57,249, 5,197, 53,245 }, { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 }, { 42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 }, { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 } }; // Prepare tables if (!dither_ok) { int i, j; for (i=0; i<16; i++) for (j=0; j<16; j++) dither[i][j] = ((255 - 2*dither[i][j]) * 8) / 512; j = -8; for (i=3; i<256; i+=8) while (j <= i) quant[j++] = i; while (j<256+8) quant[j++] = 0xff; dither_ok = 1; } // Go dithering for (int y=0; yr = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ]; pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ]; pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ]; } } } ////////////////////////////////////////////////// // Upsample Downsample ////////////////////////////////////////////////// void GPixmap::downsample(const GPixmap *src, int factor, const GRect *pdr) { // check arguments GRect rect(0, 0, (src->columns()+factor-1)/factor, (src->rows()+factor-1)/factor); if (pdr != 0) { if (pdr->xmin < rect.xmin || pdr->ymin < rect.ymin || pdr->xmax > rect.xmax || pdr->ymax > rect.ymax ) G_THROW( ERR_MSG("GPixmap.overflow1") ); rect = *pdr; } // precompute inverse map static int invmap[256]; static int invmapok = 0; if (! invmapok) { invmapok = 1; for (int i=1; i<(int)(sizeof(invmap)/sizeof(int)); i++) invmap[i] = 0x10000 / i; } // initialise pixmap init(rect.height(), rect.width(), 0); // determine starting and ending points in source rectangle int sy = rect.ymin * factor; int sxz = rect.xmin * factor; // loop over source rows const GPixel *sptr = (*src)[sy]; GPixel *dptr = (*this)[0]; for (int y=0; y (int)src->rows()) lsy = (int)src->rows(); int lsx = sx + factor; if (lsx > (int)src->columns()) lsx = (int)src->columns(); // compute average for (int rsy=sy; rsyrowsize(); } // set pixel color if (s >= (int)(sizeof(invmap)/sizeof(int))) { dptr[x].r = r / s; dptr[x].g = g / s; dptr[x].b = b / s; } else { dptr[x].r = (r*invmap[s] + 0x8000) >> 16; dptr[x].g = (g*invmap[s] + 0x8000) >> 16; dptr[x].b = (b*invmap[s] + 0x8000) >> 16; } // next column sx = sx + factor; } // next row sy = sy + factor; sptr = sptr + factor * src->rowsize(); dptr = dptr + rowsize(); } } void GPixmap::upsample(const GPixmap *src, int factor, const GRect *pdr) { // check arguments GRect rect(0, 0, src->columns()*factor, src->rows()*factor); if (pdr != 0) { if (pdr->xmin < rect.xmin || pdr->ymin < rect.ymin || pdr->xmax > rect.xmax || pdr->ymax > rect.ymax ) G_THROW( ERR_MSG("GPixmap.overflow2") ); rect = *pdr; } // initialise pixmap init(rect.height(), rect.width(), 0); // compute starting point in source rectangle int sy, sy1, sxz, sx1z; euclidian_ratio(rect.ymin, factor, sy, sy1); euclidian_ratio(rect.xmin, factor, sxz, sx1z); // loop over rows const GPixel *sptr = (*src)[sy]; GPixel *dptr = (*this)[0]; for (int y=0; y= factor) { sx1 = 0; sx += 1; } } // next row dptr += rowsize(); if (++sy1 >= factor) { sy1 = 0; sptr += src->rowsize(); } } } static inline void downsample_4x4_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) { const GPixel *x = s; const GPixel *y = x + sadd; d[0].b = ( 11*x[0].b + 2*(x[1].b + y[0].b ) + y[1].b + 8) >> 4; d[0].g = ( 11*x[0].g + 2*(x[1].g + y[0].g ) + y[1].g + 8) >> 4; d[0].r = ( 11*x[0].r + 2*(x[1].r + y[0].r ) + y[1].r + 8) >> 4; d[1].b = ( 7*(x[1].b + x[2].b) + y[1].b + y[2].b + 8 ) >> 4; d[1].g = ( 7*(x[1].g + x[2].g) + y[1].g + y[2].g + 8 ) >> 4; d[1].r = ( 7*(x[1].r + x[2].r) + y[1].r + y[2].r + 8 ) >> 4; d[2].b = ( 11*x[3].b + 2*(x[2].b + y[3].b ) + y[2].b + 8) >> 4; d[2].g = ( 11*x[3].g + 2*(x[2].g + y[3].g ) + y[2].g + 8) >> 4; d[2].r = ( 11*x[3].r + 2*(x[2].r + y[3].r ) + y[2].r + 8) >> 4; d = d + dadd; x = x + sadd + sadd; d[0].b = ( 7*(x[0].b + y[0].b) + x[1].b + y[1].b + 8 ) >> 4; d[0].g = ( 7*(x[0].g + y[0].g) + x[1].g + y[1].g + 8 ) >> 4; d[0].r = ( 7*(x[0].r + y[0].r) + x[1].r + y[1].r + 8 ) >> 4; d[1].b = ( x[2].b + y[2].b + x[1].b + y[1].b + 2 ) >> 2; d[1].g = ( x[2].g + y[2].g + x[1].g + y[1].g + 2 ) >> 2; d[1].r = ( x[2].r + y[2].r + x[1].r + y[1].r + 2 ) >> 2; d[2].b = ( 7*(x[3].b + y[3].b) + x[2].b + y[2].b + 8 ) >> 4; d[2].g = ( 7*(x[3].g + y[3].g) + x[2].g + y[2].g + 8 ) >> 4; d[2].r = ( 7*(x[3].r + y[3].r) + x[2].r + y[2].r + 8 ) >> 4; d = d + dadd; y = y + sadd + sadd; d[0].b = ( 11*y[0].b + 2*(y[1].b + x[0].b ) + x[1].b + 8) >> 4; d[0].g = ( 11*y[0].g + 2*(y[1].g + x[0].g ) + x[1].g + 8) >> 4; d[0].r = ( 11*y[0].r + 2*(y[1].r + x[0].r ) + x[1].r + 8) >> 4; d[1].b = ( 7*(y[1].b + y[2].b) + x[1].b + x[2].b + 8 ) >> 4; d[1].g = ( 7*(y[1].g + y[2].g) + x[1].g + x[2].g + 8 ) >> 4; d[1].r = ( 7*(y[1].r + y[2].r) + x[1].r + x[2].r + 8 ) >> 4; d[2].b = ( 11*y[3].b + 2*(y[2].b + x[3].b ) + x[2].b + 8) >> 4; d[2].g = ( 11*y[3].g + 2*(y[2].g + x[3].g ) + x[2].g + 8) >> 4; d[2].r = ( 11*y[3].r + 2*(y[2].r + x[3].r ) + x[2].r + 8) >> 4; } static inline void upsample_2x2_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd) { const GPixel *x = s; const GPixel *y = x + sadd; d[0] = x[0]; d[1].b = (x[0].b + x[1].b + 1) >> 1; d[1].g = (x[0].g + x[1].g + 1) >> 1; d[1].r = (x[0].r + x[1].r + 1) >> 1; d[2] = x[1]; d = d + dadd; d[0].b = (x[0].b + y[0].b + 1) >> 1; d[0].g = (x[0].g + y[0].g + 1) >> 1; d[0].r = (x[0].r + y[0].r + 1) >> 1; d[1].b = (x[0].b + y[0].b + x[1].b + y[1].b + 2) >> 2; d[1].g = (x[0].g + y[0].g + x[1].g + y[1].g + 2) >> 2; d[1].r = (x[0].r + y[0].r + x[1].r + y[1].r + 2) >> 2; d[2].b = (x[1].b + y[1].b + 1) >> 1; d[2].g = (x[1].g + y[1].g + 1) >> 1; d[2].r = (x[1].r + y[1].r + 1) >> 1; d = d + dadd; d[0] = y[0]; d[1].b = (y[0].b + y[1].b + 1) >> 1; d[1].g = (y[0].g + y[1].g + 1) >> 1; d[1].r = (y[0].r + y[1].r + 1) >> 1; d[2] = y[1]; } static inline void copy_to_partial(int w, int h, const GPixel *s, int sadd, GPixel *d, int dadd, int xmin, int xmax, int ymin, int ymax) { int y = 0; while (y0 ? xmin : 0); while (x0 ? sadd * ymin : 0); while (ycolumns(); int srcheight = src->rows(); int destwidth = (srcwidth * 3 + 3 ) / 4; int destheight = (srcheight * 3 + 3) / 4; GRect rect(0, 0, destwidth, destheight); if (pdr != 0) { if (pdr->xmin < rect.xmin || pdr->ymin < rect.ymin || pdr->xmax > rect.xmax || pdr->ymax > rect.ymax ) G_THROW( ERR_MSG("GPixmap.overflow3") ); rect = *pdr; destwidth = rect.width(); destheight = rect.height(); } // initialize pixmap init(destheight, destwidth, 0); // compute bounds int dxz, dy; // location of bottomleft block in destination image int sxz, sy; // location of bottomleft block in source image euclidian_ratio(rect.ymin, 3, sy, dy); euclidian_ratio(rect.xmin, 3, sxz, dxz); sxz = 4 * sxz; sy = 4 * sy; dxz = - dxz; dy = - dy; // prepare variables int sadd = src->rowsize(); int dadd = this->rowsize(); const GPixel *sptr = (*src)[0] + sy * sadd; GPixel *dptr = (*this)[0] + dy * dadd; int s4add = 4 * sadd; int d3add = 3 * dadd; // iterate over row blocks while (dy < destheight) { int sx = sxz; int dx = dxz; // iterate over column blocks while (dx < destwidth) { GPixel xin[16], xout[9]; if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) { if (sx+4<=srcwidth && sy+4<=srcheight) { downsample_4x4_to_3x3(sptr+sx, sadd, dptr+dx, dadd); } else { copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); downsample_4x4_to_3x3(xin, 4, dptr+dx, dadd); } } else { if (sx+4<=srcwidth && sy+4<=srcheight) { downsample_4x4_to_3x3(sptr+sx, sadd, xout, 3); copy_to_partial(3,3, xout, 3, dptr+dx, dadd,-dx,destwidth-dx,-dy,destheight-dy); } else { copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4); downsample_4x4_to_3x3(xin, 4, xout, 3); copy_to_partial(3,3, xout,3, dptr+dx,dadd,-dx,destwidth-dx,-dy,destheight-dy); } } // next column dx += 3; sx += 4; } // next row dy += 3; dptr += d3add; sy += 4; sptr += s4add; } } void GPixmap::upsample23(const GPixmap *src, const GRect *pdr) { // check arguments int srcwidth = src->columns(); int srcheight = src->rows(); int destwidth = (srcwidth * 3 + 1 ) / 2; int destheight = (srcheight * 3 + 1) / 2; GRect rect(0, 0, destwidth, destheight); if (pdr != 0) { if (pdr->xmin < rect.xmin || pdr->ymin < rect.ymin || pdr->xmax > rect.xmax || pdr->ymax > rect.ymax ) G_THROW( ERR_MSG("GPixmap.overflow4") ); rect = *pdr; destwidth = rect.width(); destheight = rect.height(); } // initialize pixmap init(destheight, destwidth, 0); // compute bounds int dxz, dy; // location of bottomleft block in destination image int sxz, sy; // location of bottomleft block in source image euclidian_ratio(rect.ymin, 3, sy, dy); euclidian_ratio(rect.xmin, 3, sxz, dxz); sxz = 2 * sxz; sy = 2 * sy; dxz = - dxz; dy = - dy; // prepare variables int sadd = src->rowsize(); int dadd = this->rowsize(); const GPixel *sptr = (*src)[0] + sy * sadd; GPixel *dptr = (*this)[0] + dy * dadd; int s2add = 2 * sadd; int d3add = 3 * dadd; // iterate over row blocks while (dy < destheight) { int sx = sxz; int dx = dxz; // iterate over column blocks while (dx < destwidth) { GPixel xin[4], xout[9]; if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight) { if (sx+2<=srcwidth && sy+2<=srcheight) { upsample_2x2_to_3x3( sptr+sx, sadd, dptr+dx, dadd); } else { copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); upsample_2x2_to_3x3(xin, 2, dptr+dx, dadd); } } else { if (sx+2<=srcwidth && sy+2<=srcheight) { upsample_2x2_to_3x3( sptr+sx, sadd, xout, 3); copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); } else { copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2); upsample_2x2_to_3x3(xin, 2, xout, 3); copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy); } } // next column dx += 3; sx += 2; } // next row dy += 3; dptr += d3add; sy += 2; sptr += s2add; } } ////////////////////////////////////////////////// // Blitting and attenuating ////////////////////////////////////////////////// static unsigned char clip[512]; static bool clipok = false; static void compute_clip() { clipok = true; for (unsigned int i=0; irows(), nrows) - maxi(0, ypos), xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); if(xrows <= 0 || xcolumns <= 0) return; // Precompute multiplier map unsigned int multiplier[256]; unsigned int maxgray = bm->get_grays() - 1; for (unsigned int i=0; irowsize()-mini(0,xpos); GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); // Loop over rows for (int y=0; y 0) { if (srcpix >= maxgray) { dst[x].b = 0; dst[x].g = 0; dst[x].r = 0; } else { unsigned int level = multiplier[srcpix]; dst[x].b -= (dst[x].b * level) >> 16; dst[x].g -= (dst[x].g * level) >> 16; dst[x].r -= (dst[x].r * level) >> 16; } } } // Next line dst += rowsize(); src += bm->rowsize(); } } void GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixel *color) { // Check if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); if (!clipok) compute_clip(); if (!color) return; // Compute number of rows and columns int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); if(xrows <= 0 || xcolumns <= 0) return; // Precompute multiplier map unsigned int multiplier[256]; unsigned int maxgray = bm->get_grays() - 1; for (unsigned int i=1; ir; unsigned char gg = color->g; unsigned char gb = color->b; // Compute starting point const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos); GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); // Loop over rows for (int y=0; y 0) { if (srcpix >= maxgray) { dst[x].b = clip[dst[x].b + gb]; dst[x].g = clip[dst[x].g + gg]; dst[x].r = clip[dst[x].r + gr]; } else { unsigned int level = multiplier[srcpix]; dst[x].b = clip[dst[x].b + ((gb * level) >> 16)]; dst[x].g = clip[dst[x].g + ((gg * level) >> 16)]; dst[x].r = clip[dst[x].r + ((gr * level) >> 16)]; } } } // Next line dst += rowsize(); src += bm->rowsize(); } } void GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) { // Check if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); if (!clipok) compute_clip(); if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) G_THROW( ERR_MSG("GPixmap.diff_size") ); // Compute number of rows and columns int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); if(xrows <= 0 || xcolumns <= 0) return; // Precompute multiplier map unsigned int multiplier[256]; unsigned int maxgray = bm->get_grays() - 1; for (unsigned int i=1; irowsize()-mini(0,xpos); const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); // Loop over rows for (int y=0; y 0) { if (srcpix >= maxgray) { dst[x].b = clip[dst[x].b + src2[x].b]; dst[x].g = clip[dst[x].g + src2[x].g]; dst[x].r = clip[dst[x].r + src2[x].r]; } else { unsigned int level = multiplier[srcpix]; dst[x].b = clip[dst[x].b + ((src2[x].b * level) >> 16)]; dst[x].g = clip[dst[x].g + ((src2[x].g * level) >> 16)]; dst[x].r = clip[dst[x].r + ((src2[x].r * level) >> 16)]; } } } // Next line dst += rowsize(); src += bm->rowsize(); src2 += color->rowsize(); } } void GPixmap::blend(const GBitmap *bm, int xpos, int ypos, const GPixmap *color) { // Check if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); if (!color) G_THROW( ERR_MSG("GPixmap.null_color") ); if (!clipok) compute_clip(); if (bm->rows()!=color->rows() || bm->columns()!=color->columns()) G_THROW( ERR_MSG("GPixmap.diff_size") ); // Compute number of rows and columns int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos), xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos); if(xrows <= 0 || xcolumns <= 0) return; // Precompute multiplier map unsigned int multiplier[256]; unsigned int maxgray = bm->get_grays() - 1; for (unsigned int i=1; irowsize()-mini(0,xpos); const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos); GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos); // Loop over rows for (int y=0; y 0) { if (srcpix >= maxgray) { dst[x].b = src2[x].b; dst[x].g = src2[x].g; dst[x].r = src2[x].r; } else { unsigned int level = multiplier[srcpix]; dst[x].b -= (((int)dst[x].b - (int)src2[x].b) * level) >> 16; dst[x].g -= (((int)dst[x].g - (int)src2[x].g) * level) >> 16; dst[x].r -= (((int)dst[x].r - (int)src2[x].r) * level) >> 16; } } } // Next line dst += rowsize(); src += bm->rowsize(); src2 += color->rowsize(); } } void GPixmap::stencil(const GBitmap *bm, const GPixmap *pm, int pms, const GRect *pmr, double corr) { // Check arguments GRect rect(0, 0, pm->columns()*pms, pm->rows()*pms); if (pmr != 0) { if (pmr->xmin < rect.xmin || pmr->ymin < rect.ymin || pmr->xmax > rect.xmax || pmr->ymax > rect.ymax ) G_THROW( ERR_MSG("GPixmap.overflow5") ); rect = *pmr; } // Compute number of rows int xrows = nrows; if ((int)bm->rows() < xrows) xrows = bm->rows(); if (rect.height() < xrows) xrows = rect.height(); // Compute number of columns int xcolumns = ncolumns; if ((int)bm->columns() < xcolumns) xcolumns = bm->columns(); if (rect.width() < xcolumns) xcolumns = rect.width(); // Precompute multiplier map unsigned int multiplier[256]; unsigned int maxgray = bm->get_grays() - 1; for (unsigned int i=1; i 0) { if (srcpix >= maxgray) { dst[x].b = gtable[fg[fgx].b]; dst[x].g = gtable[fg[fgx].g]; dst[x].r = gtable[fg[fgx].r]; } else { unsigned int level = multiplier[srcpix]; dst[x].b -= (((int)dst[x].b - (int)gtable[fg[fgx].b]) * level) >> 16; dst[x].g -= (((int)dst[x].g - (int)gtable[fg[fgx].g]) * level) >> 16; dst[x].r -= (((int)dst[x].r - (int)gtable[fg[fgx].r]) * level) >> 16; } } // Next column if (++fgx1 >= pms) { fgx1 = 0; fgx += 1; } } // Next line dst += rowsize(); src += bm->rowsize(); if (++fgy1 >= pms) { fgy1 = 0; fg += pm->rowsize(); } } } GP GPixmap::rotate(int count) { GP newpixmap(this); if((count %= 4)) { if( count&0x01) newpixmap = new GPixmap(ncolumns, nrows); else newpixmap = new GPixmap(nrows, ncolumns); GPixmap &dpixmap = *newpixmap; GMonitorLock lock(&pixmap_monitor()); switch(count) { case 1: //// rotate 90 counter clockwise { int lastrow = dpixmap.rows()-1; for(int y=0; y=0; x++,xnew--) { dpixmap[xnew][y] = r[x]; } } } break; case 2: //// rotate 180 counter clockwise { int lastrow = dpixmap.rows()-1; int lastcolumn = dpixmap.columns()-1; for(int y=0,ynew=lastrow; ynew>=0; y++,ynew--) { const GPixel *r=operator [] (y); GPixel *d=dpixmap[ynew]; for(int xnew=lastcolumn; xnew>=0; r++,xnew--) { d[xnew] = *r; } } } break; case 3: //// rotate 270 counter clockwise { int lastcolumn = dpixmap.columns()-1; for(int y=0,ynew=lastcolumn; ynew>=0; y++,ynew--) { const GPixel *r=operator [] (y); for(int x=0; x