diff options
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/GPixmap.cpp')
-rw-r--r-- | kviewshell/plugins/djvu/libdjvu/GPixmap.cpp | 1676 |
1 files changed, 1676 insertions, 0 deletions
diff --git a/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp new file mode 100644 index 00000000..81c1dd71 --- /dev/null +++ b/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp @@ -0,0 +1,1676 @@ +//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 <stdlib.h> +#include <math.h> +#include <assert.h> + + +#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<grays; i++) + { + int level = color >> 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<GPixel> 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<nrows; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = ramp[ src[x] ]; + } + // Free ramp +// if (!userramp) +// delete [] (GPixel*)ramp; + } +} + + +void +GPixmap::init(const GBitmap &ref, const GRect &rect, const GPixel *userramp) +{ + init(rect.height(), rect.width(), 0); + // 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()) + { + GPixel *xramp; + GPBuffer<GPixel> 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; y<rect2.ymax; y++) + { + GPixel *dst = (*this)[y]; + const unsigned char *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = ramp[ src[x] ]; + } + // free ramp +// if (!userramp) +// delete [] (GPixel*) ramp; + } +} + + +void +GPixmap::init(const GPixmap &ref) +{ + init(ref.rows(), ref.columns(), 0); + if (nrows>0 && ncolumns>0) + { + for (int y=0; y<nrows; y++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y]; + for (int x=0; x<ncolumns; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::init(const GPixmap &ref, const GRect &rect) +{ + init(rect.height(), rect.width(), 0); + // 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++) + { + GPixel *dst = (*this)[y]; + const GPixel *src = ref[y+rect.ymin] + rect.xmin; + for (int x=rect2.xmin; x<rect2.xmax; x++) + dst[x] = src[x]; + } + } +} + + +void +GPixmap::donate_data(GPixel *data, int w, int h) +{ + destroy(); + nrows = h; + ncolumns = w; + nrowsize = w; + pixels_data=pixels=data; +} + + +GPixel * +GPixmap::take_data(size_t &offset) +{ + GPixel *ret = pixels_data; + pixels_data = 0; + offset = 0; + return ret; +} + + + +////////////////////////////////////////////////// +// Save and load ppm files +////////////////////////////////////////////////// + + +static unsigned int +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("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<unsigned char> 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<ncolumns; x+=1, rgb+=3) + { + p[x].r = rgb[0]; + p[x].g = rgb[1]; + p[x].b = rgb[2]; + } + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = read_integer(lookahead, bs); + p[x].g = read_integer(lookahead, bs); + p[x].b = read_integer(lookahead, bs); + } + } + } + // Process small values of maxval + if (maxval>0 && maxval<255) + { + char table[256]; + for (int i=0; i<256; i++) + table[i] = (i<maxval ? (255*i + maxval/2) / maxval : 255); + for (int y=0; y<nrows; y++) + { + GPixel *p = (*this)[y]; + for (int x=0; x<ncolumns; x++) + { + p[x].r = table[p[x].r]; + p[x].g = table[p[x].g]; + p[x].b = table[p[x].b]; + } + } + } +} + + +void +GPixmap::save_ppm(ByteStream &bs, int raw) const +{ + GUTF8String head; + head.format("P%c\n%d %d\n255\n", (raw ? '6' : '3'), ncolumns, nrows); + bs.writall((void*)(const char *)head, head.length()); + if (raw) + { + int rowsize = ncolumns+ncolumns+ncolumns; + GTArray<unsigned char> xrgb(rowsize); + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char *d = xrgb; + for (int x=0; x<ncolumns; x++) + { + *d++ = p[x].r; + *d++ = p[x].g; + *d++ = p[x].b; + } + bs.writall((void*)(unsigned char*)xrgb, ncolumns * 3); + } + } + else + { + for (int y=nrows-1; y>=0; y--) + { + const GPixel *p = (*this)[y]; + unsigned char eol='\n'; + for (int x=0; x<ncolumns; ) + { + head.format("%d %d %d ", p[x].r, p[x].g, p[x].b); + bs.writall((void*)(const char *)head, head.length()); + x += 1; + if (x==ncolumns || (x&0x7)==0) + bs.write((void*)&eol, 1); + } + } + } +} + + + + +////////////////////////////////////////////////// +// Color correction +////////////////////////////////////////////////// + + +static void +color_correction_table(double gamma, unsigned char gtable[256] ) +{ + // Check argument + if (gamma<0.1 || gamma>10.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; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = 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; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = 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; y<nrows; y++) + { + GPixel *pix = (*this)[y]; + for (int x=0; x<ncolumns; x++, pix++) + { + pix->r = 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<nrows; y++) + { + int sx = sxz; + // loop over source columns + for (int x=0; x<ncolumns; x++) + { + int r=0, g=0, b=0, s=0; + // compute average bounds + const GPixel *ksptr = sptr; + int lsy = sy + factor; + if (lsy > (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; rsy<lsy; rsy++) + { + for (int rsx = sx; rsx<lsx; rsx++) + { + r += ksptr[rsx].r; + g += ksptr[rsx].g; + b += ksptr[rsx].b; + s += 1; + } + ksptr += src->rowsize(); + } + // 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<nrows; y++) + { + // loop over columns + int sx = sxz; + int sx1 = sx1z; + for (int x=0; x<ncolumns; x++) + { + dptr[x] = sptr[sx]; + // next column + if (++sx1 >= 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 (y<ymin && y<h) + { + y += 1; + s += sadd; + d += dadd; + } + while (y<ymax && y<h) + { + int x = (xmin>0 ? xmin : 0); + while (x<w && x<xmax) + { + d[x] = s[x]; + x++; + } + y += 1; + s += sadd; + d += dadd; + } +} + + +static inline void +copy_line(const GPixel *s, int smin, int smax, + GPixel *d, int dmin, int dmax) +{ + int x = dmin; + while (x < smin) + { + d[x] = s[smin]; + x++; + } + while (x < dmax && x < smax) + { + d[x] = s[x]; + x++; + } + while (x < dmax) + { + d[x] = s[smax]; + x++; + } +} + + +static inline void +copy_from_partial(int w, int h, + const GPixel *s, int sadd, int xmin, int xmax, int ymin, int ymax, + GPixel *d, int dadd) +{ + int y = 0; + s += (ymin>0 ? sadd * ymin : 0); + while (y<ymin && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } + while (y<ymax && y<h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + s += sadd; + d += dadd; + } + s -= sadd; + while (y < h) + { + copy_line(s, xmin, xmax, d, 0, w); + y += 1; + d += dadd; + } +} + + + + + +void +GPixmap::downsample43(const GPixmap *src, const GRect *pdr) +{ + // check arguments + int srcwidth = src->columns(); + 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; i<sizeof(clip); i++) + clip[i] = (i<256 ? i : 255); +} + + +void +GPixmap::attenuate(const GBitmap *bm, int xpos, int ypos) +{ + // Check + if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") ); + // 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=0; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // 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<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 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; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + unsigned char gr = color->r; + 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<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 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; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-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<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 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; i<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Cache target color + // Compute starting point + const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-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<xrows; y++) + { + // Loop over columns + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 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<maxgray ; i++) + multiplier[i] = 0x10000 * i / maxgray; + // Prepare color correction table + unsigned char gtable[256]; + color_correction_table_cache(corr, gtable); + // Compute starting point in blown up foreground pixmap + int fgy, fgy1, fgxz, fgx1z; + euclidian_ratio(rect.ymin, pms, fgy, fgy1); + euclidian_ratio(rect.xmin, pms, fgxz, fgx1z); + const GPixel *fg = (*pm)[fgy]; + const unsigned char *src = (*bm)[0]; + GPixel *dst = (*this)[0]; + // Loop over rows + for (int y=0; y<xrows; y++) + { + // Loop over columns + int fgx = fgxz; + int fgx1 = fgx1z; + for (int x=0; x<xcolumns; x++) + { + unsigned char srcpix = src[x]; + // Perform pixel operation + if (srcpix > 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> GPixmap::rotate(int count) +{ + GP<GPixmap> 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<nrows; y++) + { + const GPixel *r=operator [] (y); + for(int x=0,xnew=lastrow; xnew>=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<ncolumns; x++) + { + dpixmap[x][ynew] = r[x]; + } + } + } + break; + } + } + return newpixmap; +} + + + +#ifdef HAVE_NAMESPACES +} +# ifndef NOT_USING_DJVU_NAMESPACE +using namespace DJVU; +# endif +#endif + |