summaryrefslogtreecommitdiffstats
path: root/kviewshell/plugins/djvu/libdjvu/GPixmap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kviewshell/plugins/djvu/libdjvu/GPixmap.cpp')
-rw-r--r--kviewshell/plugins/djvu/libdjvu/GPixmap.cpp1676
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
+