1 //C-  -*- C++ -*-
2 //C- -------------------------------------------------------------------
3 //C- DjVuLibre-3.5
4 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5 //C- Copyright (c) 2001  AT&T
6 //C-
7 //C- This software is subject to, and may be distributed under, the
8 //C- GNU General Public License, either Version 2 of the license,
9 //C- or (at your option) any later version. The license should have
10 //C- accompanied the software or you may obtain a copy of the license
11 //C- from the Free Software Foundation at http://www.fsf.org .
12 //C-
13 //C- This program is distributed in the hope that it will be useful,
14 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //C- GNU General Public License for more details.
17 //C-
18 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19 //C- Lizardtech Software.  Lizardtech Software has authorized us to
20 //C- replace the original DjVu(r) Reference Library notice by the following
21 //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22 //C-
23 //C-  ------------------------------------------------------------------
24 //C- | DjVu (r) Reference Library (v. 3.5)
25 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
27 //C- | 6,058,214 and patents pending.
28 //C- |
29 //C- | This software is subject to, and may be distributed under, the
30 //C- | GNU General Public License, either Version 2 of the license,
31 //C- | or (at your option) any later version. The license should have
32 //C- | accompanied the software or you may obtain a copy of the license
33 //C- | from the Free Software Foundation at http://www.fsf.org .
34 //C- |
35 //C- | The computer code originally released by LizardTech under this
36 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
37 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
38 //C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39 //C- | non-exclusive license to make, use, sell, or otherwise dispose of
40 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42 //C- | General Public License.   This grant only confers the right to
43 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44 //C- | the extent such infringement is reasonably necessary to enable
45 //C- | recipient to make, have made, practice, sell, or otherwise dispose
46 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47 //C- | any greater extent that may be necessary to utilize further
48 //C- | modifications or combinations.
49 //C- |
50 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54 //C- +------------------------------------------------------------------
55 
56 #ifdef HAVE_CONFIG_H
57 # include "config.h"
58 #endif
59 #if NEED_GNUG_PRAGMAS
60 # pragma implementation
61 #endif
62 
63 // -- Implements class PIXMAP
64 // Author: Leon Bottou 07/1997
65 
66 
67 
68 #include "GPixmap.h"
69 
70 #include "GString.h"
71 #include "GException.h"
72 #include "ByteStream.h"
73 #include "GRect.h"
74 #include "GBitmap.h"
75 #include "GThreads.h"
76 #include "Arrays.h"
77 #include "JPEGDecoder.h"
78 
79 #include <stddef.h>
80 #include <stdlib.h>
81 #include <math.h>
82 #include <assert.h>
83 
84 
85 #ifdef HAVE_NAMESPACES
86 namespace DJVU {
87 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
88 }
89 #endif
90 #endif
91 
92 
93 
94 //////////////////////////////////////////////////
95 // ------- predefined colors
96 //////////////////////////////////////////////////
97 
98 
99 const GPixel GPixel::WHITE = { 255, 255, 255 };
100 const GPixel GPixel::BLACK = {   0,   0,   0 };
101 const GPixel GPixel::BLUE  = { 255,   0,   0 };
102 const GPixel GPixel::GREEN = {   0, 255,   0 };
103 const GPixel GPixel::RED   = {   0,   0, 255 };
104 
105 
106 //////////////////////////////////////////////////
107 // ----- utilities
108 //////////////////////////////////////////////////
109 
110 
111 static const GPixel *
new_gray_ramp(int grays,GPixel * ramp)112 new_gray_ramp(int grays,GPixel *ramp)
113 {
114   int color = 0xff0000;
115   int decrement = color / (grays-1);
116   for (int i=0; i<grays; i++)
117     {
118       int level = color >> 16;
119       ramp[i].b = level;
120       ramp[i].g = level;
121       ramp[i].r = level;
122       color -= decrement;
123     }
124   return ramp;
125 }
126 
127 
128 static inline int
mini(int x,int y)129 mini(int x, int y)
130 {
131   return (x < y ? x : y);
132 }
133 
134 
135 static inline int
maxi(int x,int y)136 maxi(int x, int y)
137 {
138   return (x > y ? x : y);
139 }
140 
141 
142 static inline void
euclidian_ratio(int a,int b,int & q,int & r)143 euclidian_ratio(int a, int b, int &q, int &r)
144 {
145   q = a / b;
146   r = a - b*q;
147   if (r < 0)
148   {
149     q -= 1;
150     r += b;
151   }
152 }
153 
154 
155 //////////////////////////////////////////////////
156 // global lock used by some rare operations
157 //////////////////////////////////////////////////
158 
pixmap_monitor()159 static GMonitor &pixmap_monitor() {
160   static GMonitor xpixmap_monitor;
161   return xpixmap_monitor;
162 }
163 
164 
165 //////////////////////////////////////////////////
166 // constructors and destructors
167 //////////////////////////////////////////////////
168 
169 
~GPixmap()170 GPixmap::~GPixmap()
171 {
172   delete [] pixels_data;
173 }
174 
175 void
destroy(void)176 GPixmap::destroy(void)
177 {
178   delete [] pixels_data;
179   pixels = pixels_data = 0;
180 }
181 
GPixmap()182 GPixmap::GPixmap()
183 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
184 {
185 }
186 
GPixmap(int nrows,int ncolumns,const GPixel * filler)187 GPixmap::GPixmap(int nrows, int ncolumns, const GPixel *filler)
188 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
189 {
190   G_TRY
191   {
192     init(nrows, ncolumns, filler);
193   }
194   G_CATCH_ALL
195   {
196 	destroy();
197 	G_RETHROW;
198   }
199   G_ENDCATCH;
200 }
201 
GPixmap(ByteStream & bs)202 GPixmap::GPixmap(ByteStream &bs)
203 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
204 {
205   G_TRY
206   {
207 	init(bs);
208   }
209   G_CATCH_ALL
210   {
211 	destroy();
212 	G_RETHROW;
213   }
214   G_ENDCATCH;
215 }
216 
GPixmap(const GBitmap & ref)217 GPixmap::GPixmap(const GBitmap &ref)
218 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
219 {
220   G_TRY
221   {
222 	init(ref, 0);
223   }
224   G_CATCH_ALL
225   {
226 	destroy();
227 	G_RETHROW;
228   }
229   G_ENDCATCH;
230 }
231 
GPixmap(const GBitmap & ref,const GRect & rect)232 GPixmap::GPixmap(const GBitmap &ref, const GRect &rect)
233 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
234 {
235   G_TRY
236   {
237     init(ref, rect, 0);
238   }
239   G_CATCH_ALL
240   {
241 	destroy();
242 	G_RETHROW;
243   }
244   G_ENDCATCH;
245 }
246 
GPixmap(const GPixmap & ref)247 GPixmap::GPixmap(const GPixmap &ref)
248 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
249 {
250   G_TRY
251   {
252     init(ref);
253   }
254   G_CATCH_ALL
255   {
256 	destroy();
257 	G_RETHROW;
258   }
259   G_ENDCATCH;
260 }
261 
GPixmap(const GPixmap & ref,const GRect & rect)262 GPixmap::GPixmap(const GPixmap &ref, const GRect &rect)
263 : nrows(0), ncolumns(0), pixels(0), pixels_data(0)
264 {
265   G_TRY
266   {
267 	init(ref, rect);
268   }
269   G_CATCH_ALL
270   {
271 	destroy();
272 	G_RETHROW;
273   }
274   G_ENDCATCH;
275 }
276 
277 
278 
279 //////////////////////////////////////////////////
280 // Initialization
281 //////////////////////////////////////////////////
282 
283 
284 void
init(int arows,int acolumns,const GPixel * filler)285 GPixmap::init(int arows, int acolumns, const GPixel *filler)
286 {
287   size_t np = arows * acolumns;
288   if (arows != (unsigned short) arows ||
289       acolumns != (unsigned short) acolumns ||
290       (arows>0 && np/(size_t)arows!=(size_t)acolumns) )
291     G_THROW("GPixmap: image size exceeds maximum (corrupted file?)");
292   destroy();
293   nrows = arows;
294   ncolumns = acolumns;
295   nrowsize = acolumns;
296   int npix = nrows * nrowsize;
297   if (npix > 0)
298   {
299     pixels = pixels_data = new GPixel[npix];
300     if (filler)
301     {
302       while (--npix>=0)
303         pixels_data[npix] = *filler;
304     }
305   }
306 }
307 
308 
309 void
init(const GBitmap & ref,const GPixel * userramp)310 GPixmap::init(const GBitmap &ref, const GPixel *userramp)
311 {
312   init(ref.rows(), ref.columns(), 0);
313   GPixel *xramp;
314   GPBuffer<GPixel> gxramp(xramp);
315   if (nrows>0 && ncolumns>0)
316   {
317     // Create pixel ramp
318     const GPixel *ramp = userramp;
319     if (!userramp)
320 	{
321           gxramp.resize(256);
322           gxramp.clear();
323 	  ramp = new_gray_ramp(ref.get_grays(),xramp);
324 	}
325     // Copy pixels
326     for (int y=0; y<nrows; y++)
327     {
328       GPixel *dst = (*this)[y];
329       const unsigned char *src = ref[y];
330       for (int x=0; x<ncolumns; x++)
331         dst[x] = ramp[ src[x] ];
332     }
333     // Free ramp
334 //    if (!userramp)
335 //      delete [] (GPixel*)ramp;
336   }
337 }
338 
339 
340 void
init(const GBitmap & ref,const GRect & rect,const GPixel * userramp)341 GPixmap::init(const GBitmap &ref, const GRect &rect, const GPixel *userramp)
342 {
343   init(rect.height(), rect.width(), 0);
344   // compute destination rectangle
345   GRect rect2(0, 0, ref.columns(), ref.rows() );
346   rect2.intersect(rect2, rect);
347   rect2.translate(-rect.xmin, -rect.ymin);
348   // copy bits
349   if (! rect2.isempty())
350   {
351     GPixel *xramp;
352     GPBuffer<GPixel> gxramp(xramp);
353     // allocate ramp
354     const GPixel *ramp = userramp;
355     if (!userramp)
356 	{
357 	  gxramp.resize(256);
358           gxramp.clear();
359           ramp = new_gray_ramp(ref.get_grays(),xramp);
360 	}
361     // copy pixels
362     for (int y=rect2.ymin; y<rect2.ymax; y++)
363     {
364       GPixel *dst = (*this)[y];
365       const unsigned char *src = ref[y+rect.ymin] + rect.xmin;
366       for (int x=rect2.xmin; x<rect2.xmax; x++)
367         dst[x] = ramp[ src[x] ];
368     }
369     // free ramp
370 //    if (!userramp)
371 //      delete [] (GPixel*) ramp;
372   }
373 }
374 
375 
376 void
init(const GPixmap & ref)377 GPixmap::init(const GPixmap &ref)
378 {
379   init(ref.rows(), ref.columns(), 0);
380   if (nrows>0 && ncolumns>0)
381   {
382     for (int y=0; y<nrows; y++)
383     {
384       GPixel *dst = (*this)[y];
385       const GPixel *src = ref[y];
386       for (int x=0; x<ncolumns; x++)
387         dst[x] = src[x];
388     }
389   }
390 }
391 
392 
393 void
init(const GPixmap & ref,const GRect & rect)394 GPixmap::init(const GPixmap &ref, const GRect &rect)
395 {
396   init(rect.height(), rect.width(), 0);
397   // compute destination rectangle
398   GRect rect2(0, 0, ref.columns(), ref.rows() );
399   rect2.intersect(rect2, rect);
400   rect2.translate(-rect.xmin, -rect.ymin);
401   // copy bits
402   if (! rect2.isempty())
403   {
404     for (int y=rect2.ymin; y<rect2.ymax; y++)
405     {
406       GPixel *dst = (*this)[y];
407       const GPixel *src = ref[y+rect.ymin] + rect.xmin;
408       for (int x=rect2.xmin; x<rect2.xmax; x++)
409         dst[x] = src[x];
410     }
411   }
412 }
413 
414 
415 void
donate_data(GPixel * data,int w,int h)416 GPixmap::donate_data(GPixel *data, int w, int h)
417 {
418   destroy();
419   nrows = h;
420   ncolumns = w;
421   nrowsize = w;
422   pixels_data=pixels=data;
423 }
424 
425 
426 GPixel *
take_data(size_t & offset)427 GPixmap::take_data(size_t &offset)
428 {
429   GPixel *ret = pixels_data;
430   pixels_data = 0;
431   offset = 0;
432   return ret;
433 }
434 
435 
436 
437 //////////////////////////////////////////////////
438 // Save and load ppm files
439 //////////////////////////////////////////////////
440 
441 
442 static unsigned int
read_integer(char & c,ByteStream & bs)443 read_integer(char &c, ByteStream &bs)
444 {
445   unsigned int x = 0;
446   // eat blank before integer
447   while (c==' ' || c=='\t' || c=='\r' || c=='\n' || c=='#')
448     {
449       if (c=='#')
450         do { } while (bs.read(&c,1) && c!='\n' && c!='\r');
451       c = 0;
452       bs.read(&c, 1);
453     }
454   // check integer
455   if (c<'0' || c>'9')
456     G_THROW( ERR_MSG("GPixmap.no_int") );
457   // eat integer
458   while (c>='0' && c<='9')
459     {
460       x = x*10 + c - '0';
461       c = 0;
462       bs.read(&c, 1);
463     }
464   return x;
465 }
466 
467 
468 void
init(ByteStream & bs)469 GPixmap::init(ByteStream &bs)
470 {
471   // Read header
472   bool raw = false;
473   bool grey = false;
474   int magic = bs.read16();
475   GP<GBitmap> bm;
476   switch (magic)
477     {
478     case ('P'<<8)+'2':
479       grey = true;
480       break;
481     case ('P'<<8)+'3':
482       break;
483     case ('P'<<8)+'5':
484       raw = grey = true;
485       /* FALLTHRU */
486     case ('P'<<8)+'6':
487       raw = true;
488       break;
489     case ('P'<<8)+'1':
490     case ('P'<<8)+'4':
491       bs.seek(0L);
492       bm = GBitmap::create(bs);
493       init(*bm);
494       return;
495     default:
496 #ifdef NEED_JPEG_DECODER
497       bs.seek(0L);
498       JPEGDecoder::decode(bs,*this);
499       return;
500 #else
501 
502       G_THROW( ERR_MSG("GPixmap.unk_PPM") );
503 #endif
504     }
505   // Read image size
506   char lookahead = '\n';
507   int bytesperrow = 0;
508   int bytespercomp = 1;
509   int acolumns = read_integer(lookahead, bs);
510   int arows = read_integer(lookahead, bs);
511   int maxval = read_integer(lookahead, bs);
512   if (maxval > 65535)
513     G_THROW("Cannot read PPM with depth greater than 48 bits.");
514   if (maxval > 255)
515     bytespercomp = 2;
516   init(arows, acolumns, 0);
517   // Prepare ramp
518   GTArray<unsigned char> ramp;
519   int maxbin = 1 << (8 * bytespercomp);
520   ramp.resize(0, maxbin-1);
521   for (int i=0; i<maxbin; i++)
522     ramp[i] = (i<maxval ? (255*i + maxval/2) / maxval : 255);
523   unsigned char *bramp = ramp;
524   // Read image data
525   if (raw && grey)
526     {
527       bytesperrow = ncolumns * bytespercomp;
528       GTArray<unsigned char> line(bytesperrow);
529       for (int y=nrows-1; y>=0; y--)
530         {
531           GPixel *p = (*this)[y];
532           unsigned char *g = &line[0];
533           if ( bs.readall((void*)g, bytesperrow) < (size_t)bytesperrow)
534             G_THROW( ByteStream::EndOfFile );
535           if (bytespercomp <= 1)
536             {
537               for (int x=0; x<ncolumns; x+=1, g+=1)
538                 p[x].r = p[x].g = p[x].b = bramp[g[0]];
539             }
540           else
541             {
542               for (int x=0; x<ncolumns; x+=1, g+=2)
543                 p[x].r = p[x].g = p[x].b = bramp[g[0]*256+g[1]];
544             }
545         }
546     }
547   else if (raw)
548     {
549       bytesperrow = ncolumns * bytespercomp * 3;
550       GTArray<unsigned char> line(bytesperrow);
551       for (int y=nrows-1; y>=0; y--)
552         {
553           GPixel *p = (*this)[y];
554           unsigned char *rgb = &line[0];
555           if ( bs.readall((void*)rgb, bytesperrow) < (size_t)bytesperrow)
556             G_THROW( ByteStream::EndOfFile );
557           if (bytespercomp <= 1)
558             {
559               for (int x=0; x<ncolumns; x+=1, rgb+=3)
560                 {
561                   p[x].r = bramp[rgb[0]];
562                   p[x].g = bramp[rgb[1]];
563                   p[x].b = bramp[rgb[2]];
564                 }
565             }
566           else
567             for (int x=0; x<ncolumns; x+=1, rgb+=6)
568               {
569                 p[x].r = bramp[rgb[0]*256+rgb[1]];
570                 p[x].g = bramp[rgb[2]*256+rgb[3]];
571                 p[x].b = bramp[rgb[4]*256+rgb[5]];
572               }
573         }
574     }
575   else
576     {
577       for (int y=nrows-1; y>=0; y--)
578         {
579           GPixel *p = (*this)[y];
580           for (int x=0; x<ncolumns; x++)
581             if (grey)
582               {
583                 p[x].g = p[x].b = p[x].r = ramp[(int)read_integer(lookahead, bs)];
584               }
585             else
586               {
587                 p[x].r = ramp[(int)read_integer(lookahead, bs)];
588                 p[x].g = ramp[(int)read_integer(lookahead, bs)];
589                 p[x].b = ramp[(int)read_integer(lookahead, bs)];
590               }
591         }
592     }
593 }
594 
595 
596 void
save_ppm(ByteStream & bs,int raw) const597 GPixmap::save_ppm(ByteStream &bs, int raw) const
598 {
599   GUTF8String head;
600   head.format("P%c\n%d %d\n255\n", (raw ? '6' : '3'), ncolumns, nrows);
601   bs.writall((void*)(const char *)head, head.length());
602   if (raw)
603     {
604       int rowsize = ncolumns+ncolumns+ncolumns;
605       GTArray<unsigned char> xrgb(rowsize);
606       for (int y=nrows-1; y>=0; y--)
607         {
608           const GPixel *p = (*this)[y];
609           unsigned char *d = xrgb;
610           for (int x=0; x<ncolumns; x++)
611             {
612               *d++ = p[x].r;
613               *d++ = p[x].g;
614               *d++ = p[x].b;
615             }
616           bs.writall((void*)(unsigned char*)xrgb, ncolumns * 3);
617         }
618     }
619   else
620     {
621       for (int y=nrows-1; y>=0; y--)
622         {
623           const GPixel *p = (*this)[y];
624           unsigned char eol='\n';
625           for (int x=0; x<ncolumns; )
626             {
627               head.format("%d %d %d  ", p[x].r, p[x].g, p[x].b);
628               bs.writall((void*)(const char *)head, head.length());
629               x += 1;
630               if (x==ncolumns || (x&0x7)==0)
631                 bs.write((void*)&eol, 1);
632             }
633         }
634     }
635 }
636 
637 
638 
639 
640 //////////////////////////////////////////////////
641 // Color correction
642 //////////////////////////////////////////////////
643 
644 
645 static void
color_correction_table(double gamma,GPixel white,unsigned char gtable[256][3])646 color_correction_table(double gamma, GPixel white,
647                        unsigned char gtable[256][3] )
648 {
649   // Check argument
650   if (gamma<0.1 || gamma>10.0)
651     G_THROW( ERR_MSG("GPixmap.bad_param") );
652   if (gamma<1.001 && gamma>0.999 && white==GPixel::WHITE)
653     {
654       // Trivial correction
655       for (int i=0; i<256; i++)
656         gtable[i][0] = gtable[i][1] = gtable[i][2] = i;
657     }
658   else
659     {
660       // Must compute the correction
661       for (int i=0; i<256; i++)
662         {
663           double x = (double)(i)/255.0;
664 #ifdef BEZIERGAMMA
665           double t = ( sqrt(1.0+(gamma*gamma-1.0)*x) - 1.0 ) / (gamma - 1.0);
666           x = ( (1.0 - gamma)*t + 2.0 * gamma ) * t / (gamma + 1.0);
667 #else
668           x = pow(x, 1.0/gamma);
669 #endif
670           gtable[i][0] = (int) floor(white.b * x + 0.5);
671           gtable[i][1] = (int) floor(white.g * x + 0.5);
672           gtable[i][2] = (int) floor(white.r * x + 0.5);
673         }
674       // Make sure that min and max values are exactly black or white
675       gtable[0][0] = 0;
676       gtable[0][1] = 0;
677       gtable[0][2] = 0;
678       gtable[255][0] = white.b;
679       gtable[255][1] = white.g;
680       gtable[255][2] = white.r;
681     }
682 }
683 
684 static void
color_correction_table_cache(double gamma,GPixel white,unsigned char gtable[256][3])685 color_correction_table_cache(double gamma, GPixel white,
686                              unsigned char gtable[256][3] )
687 {
688   // Compute color correction table
689   if (gamma<1.001 && gamma>0.999 && white==GPixel::WHITE)
690     {
691       color_correction_table(gamma, white, gtable);
692     }
693   else
694     {
695       static double lgamma = -1.0;
696       static GPixel lwhite = GPixel::BLACK;
697       static unsigned char ctable[256][3];
698       GMonitorLock lock(&pixmap_monitor());
699       if (gamma != lgamma || white != lwhite)
700         {
701           color_correction_table(gamma, white, ctable);
702           lgamma = gamma;
703           lwhite = white;
704         }
705       memcpy(gtable, ctable, 256*3*sizeof(unsigned char));
706     }
707 }
708 
709 void
color_correct(double gamma_correction,GPixel white)710 GPixmap::color_correct(double gamma_correction, GPixel white)
711 {
712   // Trivial corrections
713   if (gamma_correction>0.999 && gamma_correction<1.001 && white==GPixel::WHITE)
714     return;
715   // Compute correction table
716   unsigned char gtable[256][3];
717   color_correction_table_cache(gamma_correction, white, gtable);
718   // Perform correction
719   for (int y=0; y<nrows; y++)
720   {
721     GPixel *pix = (*this)[y];
722     for (int x=0; x<ncolumns; x++, pix++)
723     {
724       pix->b = gtable[ pix->b ][0];
725       pix->g = gtable[ pix->g ][1];
726       pix->r = gtable[ pix->r ][2];
727     }
728   }
729 }
730 
731 void
color_correct(double gamma_correction)732 GPixmap::color_correct(double gamma_correction)
733 {
734   // Trivial corrections
735   if (gamma_correction<=0.999 || gamma_correction>=1.001)
736     color_correct(gamma_correction, GPixel::WHITE);
737 }
738 
739 
740 void
color_correct(double gamma_correction,GPixel white,GPixel * pix,int npixels)741 GPixmap::color_correct(double gamma_correction, GPixel white,
742                        GPixel *pix, int npixels)
743 {
744   // Trivial corrections
745   if (gamma_correction>0.999 && gamma_correction<1.001 && white==GPixel::WHITE)
746     return;
747   // Compute correction table
748   unsigned char gtable[256][3];
749   color_correction_table_cache(gamma_correction, white, gtable);
750   // Perform correction
751   while (--npixels>=0)
752     {
753       pix->b = gtable[pix->b][0];
754       pix->g = gtable[pix->g][1];
755       pix->r = gtable[pix->r][2];
756       pix++;
757     }
758 }
759 
760 
761 void
color_correct(double gamma_correction,GPixel * pix,int npixels)762 GPixmap::color_correct(double gamma_correction, GPixel *pix, int npixels)
763 {
764   // Trivial corrections
765   if (gamma_correction<=0.999 || gamma_correction>=1.001)
766     color_correct(gamma_correction,GPixel::WHITE,pix,npixels);
767 }
768 
769 
770 //////////////////////////////////////////////////
771 // Dithering
772 //////////////////////////////////////////////////
773 
774 
775 void
ordered_666_dither(int xmin,int ymin)776 GPixmap::ordered_666_dither(int xmin, int ymin)
777 {
778   static unsigned char quantize[256+0x33+0x33];
779   static unsigned char *quant = quantize + 0x33;
780   static char  dither_ok = 0;
781   static short dither[16][16] =
782   {
783     {   0,192, 48,240, 12,204, 60,252,  3,195, 51,243, 15,207, 63,255 },
784     { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
785     {  32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
786     { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
787     {   8,200, 56,248,  4,196, 52,244, 11,203, 59,251,  7,199, 55,247 },
788     { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
789     {  40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
790     { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
791     {   2,194, 50,242, 14,206, 62,254,  1,193, 49,241, 13,205, 61,253 },
792     { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
793     {  34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
794     { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
795     {  10,202, 58,250,  6,198, 54,246,  9,201, 57,249,  5,197, 53,245 },
796     { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
797     {  42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
798     { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
799   };
800   // Prepare tables
801   if (!dither_ok)
802   {
803     int i, j;
804     for (i=0; i<16; i++)
805       for (j=0; j<16; j++)
806         dither[i][j] = ((255 - 2*dither[i][j]) * 0x33) / 512;
807     j = -0x33;
808     for (i=0x19; i<256; i+=0x33)
809       while (j <= i)
810         quant[j++] = i-0x19;
811     assert(i-0x19 == 0xff);
812     while (j< 256+0x33)
813       quant[j++] = i-0x19;
814     dither_ok = 1;
815   }
816   // Go dithering
817   for (int y=0; y<nrows; y++)
818   {
819     GPixel *pix = (*this)[y];
820     for (int x=0; x<ncolumns; x++, pix++)
821     {
822       pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ];
823       pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ];
824       pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ];
825     }
826   }
827 }
828 
829 void
ordered_32k_dither(int xmin,int ymin)830 GPixmap::ordered_32k_dither(int xmin, int ymin)
831 {
832   static unsigned char quantize[256+8+8];
833   static unsigned char *quant = quantize + 8;
834   static char  dither_ok = 0;
835   static short dither[16][16] =
836   {
837     {   0,192, 48,240, 12,204, 60,252,  3,195, 51,243, 15,207, 63,255 },
838     { 128, 64,176,112,140, 76,188,124,131, 67,179,115,143, 79,191,127 },
839     {  32,224, 16,208, 44,236, 28,220, 35,227, 19,211, 47,239, 31,223 },
840     { 160, 96,144, 80,172,108,156, 92,163, 99,147, 83,175,111,159, 95 },
841     {   8,200, 56,248,  4,196, 52,244, 11,203, 59,251,  7,199, 55,247 },
842     { 136, 72,184,120,132, 68,180,116,139, 75,187,123,135, 71,183,119 },
843     {  40,232, 24,216, 36,228, 20,212, 43,235, 27,219, 39,231, 23,215 },
844     { 168,104,152, 88,164,100,148, 84,171,107,155, 91,167,103,151, 87 },
845     {   2,194, 50,242, 14,206, 62,254,  1,193, 49,241, 13,205, 61,253 },
846     { 130, 66,178,114,142, 78,190,126,129, 65,177,113,141, 77,189,125 },
847     {  34,226, 18,210, 46,238, 30,222, 33,225, 17,209, 45,237, 29,221 },
848     { 162, 98,146, 82,174,110,158, 94,161, 97,145, 81,173,109,157, 93 },
849     {  10,202, 58,250,  6,198, 54,246,  9,201, 57,249,  5,197, 53,245 },
850     { 138, 74,186,122,134, 70,182,118,137, 73,185,121,133, 69,181,117 },
851     {  42,234, 26,218, 38,230, 22,214, 41,233, 25,217, 37,229, 21,213 },
852     { 170,106,154, 90,166,102,150, 86,169,105,153, 89,165,101,149, 85 }
853   };
854   // Prepare tables
855   if (!dither_ok)
856   {
857     int i, j;
858     for (i=0; i<16; i++)
859       for (j=0; j<16; j++)
860         dither[i][j] = ((255 - 2*dither[i][j]) * 8) / 512;
861     j = -8;
862     for (i=3; i<256; i+=8)
863       while (j <= i)
864         quant[j++] = i;
865     while (j<256+8)
866       quant[j++] = 0xff;
867     dither_ok = 1;
868   }
869   // Go dithering
870   for (int y=0; y<nrows; y++)
871   {
872     GPixel *pix = (*this)[y];
873     for (int x=0; x<ncolumns; x++, pix++)
874     {
875       pix->r = quant[ pix->r + dither[(x+xmin+0)&0xf][(y+ymin+0)&0xf] ];
876       pix->g = quant[ pix->g + dither[(x+xmin+5)&0xf][(y+ymin+11)&0xf] ];
877       pix->b = quant[ pix->b + dither[(x+xmin+11)&0xf][(y+ymin+5)&0xf] ];
878     }
879   }
880 }
881 
882 
883 //////////////////////////////////////////////////
884 // Upsample Downsample
885 //////////////////////////////////////////////////
886 
887 
888 void
downsample(const GPixmap * src,int factor,const GRect * pdr)889 GPixmap::downsample(const GPixmap *src, int factor, const GRect *pdr)
890 {
891   // check arguments
892   GRect rect(0, 0, (src->columns()+factor-1)/factor, (src->rows()+factor-1)/factor);
893   if (pdr != 0)
894   {
895     if (pdr->xmin < rect.xmin ||
896         pdr->ymin < rect.ymin ||
897         pdr->xmax > rect.xmax ||
898         pdr->ymax > rect.ymax  )
899       G_THROW( ERR_MSG("GPixmap.overflow1") );
900     rect = *pdr;
901   }
902 
903   // precompute inverse map
904   static int invmap[256];
905   static int invmapok = 0;
906   if (! invmapok)
907   {
908     invmapok = 1;
909     for (int i=1; i<(int)(sizeof(invmap)/sizeof(int)); i++)
910       invmap[i] = 0x10000 / i;
911   }
912 
913   // initialise pixmap
914   init(rect.height(), rect.width(), 0);
915 
916   // determine starting and ending points in source rectangle
917   int sy = rect.ymin * factor;
918   int sxz = rect.xmin * factor;
919 
920 
921   // loop over source rows
922   const GPixel *sptr = (*src)[sy];
923   GPixel *dptr = (*this)[0];
924   for (int y=0; y<nrows; y++)
925   {
926     int sx = sxz;
927     // loop over source columns
928     for (int x=0; x<ncolumns; x++)
929     {
930       int r=0, g=0, b=0, s=0;
931       // compute average bounds
932       const GPixel *ksptr = sptr;
933       int lsy = sy + factor;
934       if (lsy > (int)src->rows())
935         lsy = (int)src->rows();
936       int lsx = sx + factor;
937       if (lsx > (int)src->columns())
938         lsx = (int)src->columns();
939       // compute average
940       for (int rsy=sy; rsy<lsy; rsy++)
941       {
942         for (int rsx = sx; rsx<lsx; rsx++)
943         {
944           r += ksptr[rsx].r;
945           g += ksptr[rsx].g;
946           b += ksptr[rsx].b;
947           s += 1;
948         }
949         ksptr += src->rowsize();
950       }
951       // set pixel color
952       if (s >= (int)(sizeof(invmap)/sizeof(int)))
953       {
954         dptr[x].r = r / s;
955         dptr[x].g = g / s;
956         dptr[x].b = b / s;
957       }
958       else
959       {
960         dptr[x].r = (r*invmap[s] + 0x8000) >> 16;
961         dptr[x].g = (g*invmap[s] + 0x8000) >> 16;
962         dptr[x].b = (b*invmap[s] + 0x8000) >> 16;
963       }
964       // next column
965       sx = sx + factor;
966     }
967     // next row
968     sy = sy + factor;
969     sptr = sptr + factor * src->rowsize();
970     dptr = dptr + rowsize();
971   }
972 }
973 
974 void
upsample(const GPixmap * src,int factor,const GRect * pdr)975 GPixmap::upsample(const GPixmap *src, int factor, const GRect *pdr)
976 {
977   // check arguments
978   GRect rect(0, 0, src->columns()*factor, src->rows()*factor);
979   if (pdr != 0)
980   {
981     if (pdr->xmin < rect.xmin ||
982         pdr->ymin < rect.ymin ||
983         pdr->xmax > rect.xmax ||
984         pdr->ymax > rect.ymax  )
985       G_THROW( ERR_MSG("GPixmap.overflow2") );
986     rect = *pdr;
987   }
988   // initialise pixmap
989   init(rect.height(), rect.width(), 0);
990   // compute starting point in source rectangle
991   int sy, sy1, sxz, sx1z;
992   euclidian_ratio(rect.ymin, factor, sy, sy1);
993   euclidian_ratio(rect.xmin, factor, sxz, sx1z);
994   // loop over rows
995   const GPixel *sptr = (*src)[sy];
996   GPixel *dptr = (*this)[0];
997   for (int y=0; y<nrows; y++)
998   {
999     // loop over columns
1000     int sx = sxz;
1001     int sx1 = sx1z;
1002     for (int x=0; x<ncolumns; x++)
1003     {
1004       dptr[x] = sptr[sx];
1005       // next column
1006       if (++sx1 >= factor)
1007       {
1008         sx1 = 0;
1009         sx += 1;
1010       }
1011     }
1012     // next row
1013     dptr += rowsize();
1014     if (++sy1 >= factor)
1015     {
1016       sy1 = 0;
1017       sptr += src->rowsize();
1018     }
1019   }
1020 }
1021 
1022 
1023 static inline void
downsample_4x4_to_3x3(const GPixel * s,int sadd,GPixel * d,int dadd)1024 downsample_4x4_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd)
1025 {
1026   const GPixel *x = s;
1027   const GPixel *y = x + sadd;
1028   d[0].b = ( 11*x[0].b + 2*(x[1].b + y[0].b ) + y[1].b  + 8) >> 4;
1029   d[0].g = ( 11*x[0].g + 2*(x[1].g + y[0].g ) + y[1].g  + 8) >> 4;
1030   d[0].r = ( 11*x[0].r + 2*(x[1].r + y[0].r ) + y[1].r  + 8) >> 4;
1031   d[1].b = ( 7*(x[1].b + x[2].b) + y[1].b + y[2].b + 8 )     >> 4;
1032   d[1].g = ( 7*(x[1].g + x[2].g) + y[1].g + y[2].g + 8 )     >> 4;
1033   d[1].r = ( 7*(x[1].r + x[2].r) + y[1].r + y[2].r + 8 )     >> 4;
1034   d[2].b = ( 11*x[3].b + 2*(x[2].b + y[3].b ) + y[2].b  + 8) >> 4;
1035   d[2].g = ( 11*x[3].g + 2*(x[2].g + y[3].g ) + y[2].g  + 8) >> 4;
1036   d[2].r = ( 11*x[3].r + 2*(x[2].r + y[3].r ) + y[2].r  + 8) >> 4;
1037   d = d + dadd;
1038   x = x + sadd + sadd;
1039   d[0].b = ( 7*(x[0].b + y[0].b) + x[1].b + y[1].b + 8 )     >> 4;
1040   d[0].g = ( 7*(x[0].g + y[0].g) + x[1].g + y[1].g + 8 )     >> 4;
1041   d[0].r = ( 7*(x[0].r + y[0].r) + x[1].r + y[1].r + 8 )     >> 4;
1042   d[1].b = ( x[2].b + y[2].b + x[1].b + y[1].b + 2 )         >> 2;
1043   d[1].g = ( x[2].g + y[2].g + x[1].g + y[1].g + 2 )         >> 2;
1044   d[1].r = ( x[2].r + y[2].r + x[1].r + y[1].r + 2 )         >> 2;
1045   d[2].b = ( 7*(x[3].b + y[3].b) + x[2].b + y[2].b + 8 )     >> 4;
1046   d[2].g = ( 7*(x[3].g + y[3].g) + x[2].g + y[2].g + 8 )     >> 4;
1047   d[2].r = ( 7*(x[3].r + y[3].r) + x[2].r + y[2].r + 8 )     >> 4;
1048   d = d + dadd;
1049   y = y + sadd + sadd;
1050   d[0].b = ( 11*y[0].b + 2*(y[1].b + x[0].b ) + x[1].b  + 8) >> 4;
1051   d[0].g = ( 11*y[0].g + 2*(y[1].g + x[0].g ) + x[1].g  + 8) >> 4;
1052   d[0].r = ( 11*y[0].r + 2*(y[1].r + x[0].r ) + x[1].r  + 8) >> 4;
1053   d[1].b = ( 7*(y[1].b + y[2].b) + x[1].b + x[2].b + 8 )     >> 4;
1054   d[1].g = ( 7*(y[1].g + y[2].g) + x[1].g + x[2].g + 8 )     >> 4;
1055   d[1].r = ( 7*(y[1].r + y[2].r) + x[1].r + x[2].r + 8 )     >> 4;
1056   d[2].b = ( 11*y[3].b + 2*(y[2].b + x[3].b ) + x[2].b  + 8) >> 4;
1057   d[2].g = ( 11*y[3].g + 2*(y[2].g + x[3].g ) + x[2].g  + 8) >> 4;
1058   d[2].r = ( 11*y[3].r + 2*(y[2].r + x[3].r ) + x[2].r  + 8) >> 4;
1059 }
1060 
1061 
1062 static inline void
upsample_2x2_to_3x3(const GPixel * s,int sadd,GPixel * d,int dadd)1063 upsample_2x2_to_3x3 (const GPixel *s, int sadd, GPixel *d, int dadd)
1064 {
1065   const GPixel *x = s;
1066   const GPixel *y = x + sadd;
1067   d[0] = x[0];
1068   d[1].b = (x[0].b + x[1].b + 1) >> 1;
1069   d[1].g = (x[0].g + x[1].g + 1) >> 1;
1070   d[1].r = (x[0].r + x[1].r + 1) >> 1;
1071   d[2] = x[1];
1072   d = d + dadd;
1073   d[0].b = (x[0].b + y[0].b + 1) >> 1;
1074   d[0].g = (x[0].g + y[0].g + 1) >> 1;
1075   d[0].r = (x[0].r + y[0].r + 1) >> 1;
1076   d[1].b = (x[0].b + y[0].b + x[1].b + y[1].b + 2) >> 2;
1077   d[1].g = (x[0].g + y[0].g + x[1].g + y[1].g + 2) >> 2;
1078   d[1].r = (x[0].r + y[0].r + x[1].r + y[1].r + 2) >> 2;
1079   d[2].b = (x[1].b + y[1].b + 1) >> 1;
1080   d[2].g = (x[1].g + y[1].g + 1) >> 1;
1081   d[2].r = (x[1].r + y[1].r + 1) >> 1;
1082   d = d + dadd;
1083   d[0] = y[0];
1084   d[1].b = (y[0].b + y[1].b + 1) >> 1;
1085   d[1].g = (y[0].g + y[1].g + 1) >> 1;
1086   d[1].r = (y[0].r + y[1].r + 1) >> 1;
1087   d[2] = y[1];
1088 }
1089 
1090 
1091 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)1092 copy_to_partial(int w, int h,
1093                 const GPixel *s, int sadd,
1094                 GPixel *d, int dadd, int xmin, int xmax, int ymin, int ymax)
1095 {
1096   int y = 0;
1097   while (y<ymin  && y<h)
1098     {
1099       y += 1;
1100       s += sadd;
1101       d += dadd;
1102     }
1103   while (y<ymax && y<h)
1104     {
1105       int x = (xmin>0 ? xmin : 0);
1106       while (x<w && x<xmax)
1107         {
1108           d[x] = s[x];
1109           x++;
1110         }
1111       y += 1;
1112       s += sadd;
1113       d += dadd;
1114     }
1115 }
1116 
1117 
1118 static inline void
copy_line(const GPixel * s,int smin,int smax,GPixel * d,int dmin,int dmax)1119 copy_line(const GPixel *s, int smin, int smax,
1120           GPixel *d, int dmin, int dmax)
1121 {
1122   int x = dmin;
1123   while (x < smin)
1124   {
1125     d[x] = s[smin];
1126     x++;
1127   }
1128   while (x < dmax && x < smax)
1129   {
1130     d[x] = s[x];
1131     x++;
1132   }
1133   while (x < dmax)
1134   {
1135     d[x] = s[smax-1];
1136     x++;
1137   }
1138 }
1139 
1140 
1141 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)1142 copy_from_partial(int w, int h,
1143                   const GPixel *s, int sadd, int xmin, int xmax, int ymin, int ymax,
1144                   GPixel *d, int dadd)
1145 {
1146   int y = 0;
1147   s += (ymin>0 ? sadd * ymin : 0);
1148   while (y<ymin  && y<h)
1149     {
1150       copy_line(s, xmin, xmax, d, 0, w);
1151       y += 1;
1152       d += dadd;
1153     }
1154   while (y<ymax && y<h)
1155     {
1156       copy_line(s, xmin, xmax, d, 0, w);
1157       y += 1;
1158       s += sadd;
1159       d += dadd;
1160     }
1161   s -= sadd;
1162   while (y < h)
1163     {
1164       copy_line(s, xmin, xmax, d, 0, w);
1165       y += 1;
1166       d += dadd;
1167     }
1168 }
1169 
1170 
1171 
1172 
1173 
1174 void
downsample43(const GPixmap * src,const GRect * pdr)1175 GPixmap::downsample43(const GPixmap *src, const GRect *pdr)
1176 {
1177   // check arguments
1178   int srcwidth = src->columns();
1179   int srcheight = src->rows();
1180   int destwidth = (srcwidth * 3 + 3 ) / 4;
1181   int destheight = (srcheight * 3 + 3) / 4;
1182   GRect rect(0, 0, destwidth, destheight);
1183   if (pdr != 0)
1184   {
1185     if (pdr->xmin < rect.xmin ||
1186         pdr->ymin < rect.ymin ||
1187         pdr->xmax > rect.xmax ||
1188         pdr->ymax > rect.ymax  )
1189       G_THROW( ERR_MSG("GPixmap.overflow3") );
1190     rect = *pdr;
1191     destwidth = rect.width();
1192     destheight = rect.height();
1193   }
1194   // initialize pixmap
1195   init(destheight, destwidth, 0);
1196 
1197   // compute bounds
1198   int dxz, dy;   // location of bottomleft block in destination image
1199   int sxz, sy;   // location of bottomleft block in source image
1200   euclidian_ratio(rect.ymin, 3, sy, dy);
1201   euclidian_ratio(rect.xmin, 3, sxz, dxz);
1202   sxz = 4 * sxz;
1203   sy  = 4 * sy;
1204   dxz = - dxz;
1205   dy  = - dy;
1206 
1207   // prepare variables
1208   int sadd = src->rowsize();
1209   int dadd = this->rowsize();
1210   const GPixel *sptr = (*src)[0]  + sy * sadd;
1211   GPixel *dptr = (*this)[0] + dy * dadd;
1212   int s4add = 4 * sadd;
1213   int d3add = 3 * dadd;
1214 
1215   // iterate over row blocks
1216   while (dy < destheight)
1217   {
1218     int sx = sxz;
1219     int dx = dxz;
1220     // iterate over column blocks
1221     while (dx < destwidth)
1222     {
1223       GPixel xin[16], xout[9];
1224 
1225       if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight)
1226         {
1227           if (sx+4<=srcwidth && sy+4<=srcheight)
1228             {
1229               downsample_4x4_to_3x3(sptr+sx, sadd, dptr+dx, dadd);
1230             }
1231           else
1232             {
1233               copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4);
1234               downsample_4x4_to_3x3(xin, 4, dptr+dx, dadd);
1235             }
1236         }
1237       else
1238         {
1239           if (sx+4<=srcwidth && sy+4<=srcheight)
1240             {
1241               downsample_4x4_to_3x3(sptr+sx, sadd, xout, 3);
1242               copy_to_partial(3,3, xout, 3, dptr+dx, dadd,-dx,destwidth-dx,-dy,destheight-dy);
1243             }
1244           else
1245             {
1246               copy_from_partial(4,4, sptr+sx,sadd,-sx,srcwidth-sx,-sy,srcheight-sy, xin,4);
1247               downsample_4x4_to_3x3(xin, 4, xout, 3);
1248               copy_to_partial(3,3, xout,3, dptr+dx,dadd,-dx,destwidth-dx,-dy,destheight-dy);
1249             }
1250         }
1251       // next column
1252       dx += 3;
1253       sx += 4;
1254     }
1255     // next row
1256     dy += 3;
1257     dptr += d3add;
1258     sy += 4;
1259     sptr += s4add;
1260   }
1261 }
1262 
1263 
1264 void
upsample23(const GPixmap * src,const GRect * pdr)1265 GPixmap::upsample23(const GPixmap *src, const GRect *pdr)
1266 {
1267   // check arguments
1268   int srcwidth = src->columns();
1269   int srcheight = src->rows();
1270   int destwidth = (srcwidth * 3 + 1 ) / 2;
1271   int destheight = (srcheight * 3 + 1) / 2;
1272   GRect rect(0, 0, destwidth, destheight);
1273   if (pdr != 0)
1274   {
1275     if (pdr->xmin < rect.xmin ||
1276         pdr->ymin < rect.ymin ||
1277         pdr->xmax > rect.xmax ||
1278         pdr->ymax > rect.ymax  )
1279       G_THROW( ERR_MSG("GPixmap.overflow4") );
1280     rect = *pdr;
1281     destwidth = rect.width();
1282     destheight = rect.height();
1283   }
1284   // initialize pixmap
1285   init(destheight, destwidth, 0);
1286 
1287   // compute bounds
1288   int dxz, dy;   // location of bottomleft block in destination image
1289   int sxz, sy;   // location of bottomleft block in source image
1290   euclidian_ratio(rect.ymin, 3, sy, dy);
1291   euclidian_ratio(rect.xmin, 3, sxz, dxz);
1292   sxz = 2 * sxz;
1293   sy  = 2 * sy;
1294   dxz = - dxz;
1295   dy  = - dy;
1296 
1297   // prepare variables
1298   int sadd = src->rowsize();
1299   int dadd = this->rowsize();
1300   const GPixel *sptr = (*src)[0]  + sy * sadd;
1301   GPixel *dptr = (*this)[0] + dy * dadd;
1302   int s2add = 2 * sadd;
1303   int d3add = 3 * dadd;
1304 
1305   // iterate over row blocks
1306   while (dy < destheight)
1307   {
1308     int sx = sxz;
1309     int dx = dxz;
1310     // iterate over column blocks
1311     while (dx < destwidth)
1312     {
1313       GPixel xin[4], xout[9];
1314 
1315       if (dx>=0 && dy>=0 && dx+3<=destwidth && dy+3<=destheight)
1316       {
1317         if (sx+2<=srcwidth && sy+2<=srcheight)
1318         {
1319           upsample_2x2_to_3x3( sptr+sx, sadd, dptr+dx, dadd);
1320         }
1321         else
1322         {
1323           copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2);
1324           upsample_2x2_to_3x3(xin, 2, dptr+dx, dadd);
1325         }
1326       }
1327       else
1328       {
1329         if (sx+2<=srcwidth && sy+2<=srcheight)
1330         {
1331           upsample_2x2_to_3x3( sptr+sx, sadd, xout, 3);
1332           copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy);
1333         }
1334         else
1335         {
1336           copy_from_partial(2, 2, sptr+sx, sadd, -sx, srcwidth-sx, -sy, srcheight-sy, xin, 2);
1337           upsample_2x2_to_3x3(xin, 2, xout, 3);
1338           copy_to_partial(3,3, xout, 3, dptr+dx, dadd, -dx, destwidth-dx, -dy, destheight-dy);
1339         }
1340       }
1341       // next column
1342       dx += 3;
1343       sx += 2;
1344     }
1345     // next row
1346     dy += 3;
1347     dptr += d3add;
1348     sy += 2;
1349     sptr += s2add;
1350   }
1351 }
1352 
1353 
1354 //////////////////////////////////////////////////
1355 // Blitting and attenuating
1356 //////////////////////////////////////////////////
1357 
1358 
1359 static unsigned char clip[512];
1360 static bool clipok = false;
1361 
1362 static void
compute_clip()1363 compute_clip()
1364 {
1365   clipok = true;
1366   for (unsigned int i=0; i<sizeof(clip); i++)
1367     clip[i] = (i<256 ? i : 255);
1368 }
1369 
1370 
1371 void
attenuate(const GBitmap * bm,int xpos,int ypos)1372 GPixmap::attenuate(const GBitmap *bm, int xpos, int ypos)
1373 {
1374   // Check
1375   if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") );
1376   // Compute number of rows and columns
1377   int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos),
1378     xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos);
1379   if(xrows <= 0 || xcolumns <= 0)
1380     return;
1381   // Precompute multiplier map
1382   unsigned int multiplier[256];
1383   unsigned int maxgray = bm->get_grays() - 1;
1384   for (unsigned int i=0; i<maxgray ; i++)
1385     multiplier[i] = 0x10000 * i / maxgray;
1386   // Compute starting point
1387   const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos);
1388   GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos);
1389   // Loop over rows
1390   for (int y=0; y<xrows; y++)
1391     {
1392       // Loop over columns
1393       for (int x=0; x<xcolumns; x++)
1394         {
1395           unsigned char srcpix = src[x];
1396           // Perform pixel operation
1397           if (srcpix > 0)
1398             {
1399               if (srcpix >= maxgray)
1400                 {
1401                   dst[x].b = 0;
1402                   dst[x].g = 0;
1403                   dst[x].r = 0;
1404                 }
1405               else
1406                 {
1407                   unsigned int level = multiplier[srcpix];
1408                   dst[x].b -=  (dst[x].b * level) >> 16;
1409                   dst[x].g -=  (dst[x].g * level) >> 16;
1410                   dst[x].r -=  (dst[x].r * level) >> 16;
1411                 }
1412             }
1413         }
1414       // Next line
1415       dst += rowsize();
1416       src += bm->rowsize();
1417     }
1418 }
1419 
1420 
1421 void
blit(const GBitmap * bm,int xpos,int ypos,const GPixel * color)1422 GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixel *color)
1423 {
1424   // Check
1425   if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") );
1426   if (!clipok) compute_clip();
1427   if (!color) return;
1428   // Compute number of rows and columns
1429   int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos),
1430     xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos);
1431   if(xrows <= 0 || xcolumns <= 0)
1432     return;
1433   // Precompute multiplier map
1434   unsigned int multiplier[256];
1435   unsigned int maxgray = bm->get_grays() - 1;
1436   for (unsigned int i=1; i<maxgray ; i++)
1437     multiplier[i] = 0x10000 * i / maxgray;
1438   // Cache target color
1439   unsigned char gr = color->r;
1440   unsigned char gg = color->g;
1441   unsigned char gb = color->b;
1442   // Compute starting point
1443   const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos);
1444   GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos);
1445   // Loop over rows
1446   for (int y=0; y<xrows; y++)
1447     {
1448       // Loop over columns
1449       for (int x=0; x<xcolumns; x++)
1450         {
1451           unsigned char srcpix = src[x];
1452           // Perform pixel operation
1453           if (srcpix > 0)
1454             {
1455               if (srcpix >= maxgray)
1456                 {
1457                   dst[x].b = clip[dst[x].b + gb];
1458                   dst[x].g = clip[dst[x].g + gg];
1459                   dst[x].r = clip[dst[x].r + gr];
1460                 }
1461               else
1462                 {
1463                   unsigned int level = multiplier[srcpix];
1464                   dst[x].b = clip[dst[x].b + ((gb * level) >> 16)];
1465                   dst[x].g = clip[dst[x].g + ((gg * level) >> 16)];
1466                   dst[x].r = clip[dst[x].r + ((gr * level) >> 16)];
1467                 }
1468             }
1469         }
1470       // Next line
1471       dst += rowsize();
1472       src += bm->rowsize();
1473     }
1474 }
1475 
1476 
1477 void
blit(const GBitmap * bm,int xpos,int ypos,const GPixmap * color)1478 GPixmap::blit(const GBitmap *bm, int xpos, int ypos, const GPixmap *color)
1479 {
1480   // Check
1481   if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") );
1482   if (!color) G_THROW( ERR_MSG("GPixmap.null_color") );
1483   if (!clipok) compute_clip();
1484   if (bm->rows()!=color->rows() || bm->columns()!=color->columns())
1485     G_THROW( ERR_MSG("GPixmap.diff_size") );
1486   // Compute number of rows and columns
1487   int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos),
1488       xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos);
1489   if(xrows <= 0 || xcolumns <= 0)
1490     return;
1491   // Precompute multiplier map
1492   unsigned int multiplier[256];
1493   unsigned int maxgray = bm->get_grays() - 1;
1494   for (unsigned int i=1; i<maxgray ; i++)
1495     multiplier[i] = 0x10000 * i / maxgray;
1496   // Cache target color
1497   // Compute starting point
1498   const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos);
1499   const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos);
1500   GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos);
1501   // Loop over rows
1502   for (int y=0; y<xrows; y++)
1503     {
1504       // Loop over columns
1505       for (int x=0; x<xcolumns; x++)
1506         {
1507           unsigned char srcpix = src[x];
1508           // Perform pixel operation
1509           if (srcpix > 0)
1510             {
1511               if (srcpix >= maxgray)
1512                 {
1513                   dst[x].b = clip[dst[x].b + src2[x].b];
1514                   dst[x].g = clip[dst[x].g + src2[x].g];
1515                   dst[x].r = clip[dst[x].r + src2[x].r];
1516                 }
1517               else
1518                 {
1519                   unsigned int level = multiplier[srcpix];
1520                   dst[x].b = clip[dst[x].b + ((src2[x].b * level) >> 16)];
1521                   dst[x].g = clip[dst[x].g + ((src2[x].g * level) >> 16)];
1522                   dst[x].r = clip[dst[x].r + ((src2[x].r * level) >> 16)];
1523                 }
1524             }
1525         }
1526       // Next line
1527       dst += rowsize();
1528       src += bm->rowsize();
1529       src2 += color->rowsize();
1530     }
1531 }
1532 
1533 
1534 
1535 void
blend(const GBitmap * bm,int xpos,int ypos,const GPixmap * color)1536 GPixmap::blend(const GBitmap *bm, int xpos, int ypos, const GPixmap *color)
1537 {
1538   // Check
1539   if (!bm) G_THROW( ERR_MSG("GPixmap.null_alpha") );
1540   if (!color) G_THROW( ERR_MSG("GPixmap.null_color") );
1541   if (!clipok) compute_clip();
1542   if (bm->rows()!=color->rows() || bm->columns()!=color->columns())
1543     G_THROW( ERR_MSG("GPixmap.diff_size") );
1544   // Compute number of rows and columns
1545   int xrows = mini(ypos + (int)bm->rows(), nrows) - maxi(0, ypos),
1546       xcolumns = mini(xpos + (int) bm->columns(), ncolumns) - maxi(0, xpos);
1547   if(xrows <= 0 || xcolumns <= 0)
1548     return;
1549   // Precompute multiplier map
1550   unsigned int multiplier[256];
1551   unsigned int maxgray = bm->get_grays() - 1;
1552   for (unsigned int i=1; i<maxgray ; i++)
1553     multiplier[i] = 0x10000 * i / maxgray;
1554   // Cache target color
1555   // Compute starting point
1556   const unsigned char *src = (*bm)[0] - mini(0,ypos)*bm->rowsize()-mini(0,xpos);
1557   const GPixel *src2 = (*color)[0] + maxi(0, ypos)*color->rowsize()+maxi(0, xpos);
1558   GPixel *dst = (*this)[0] + maxi(0, ypos)*rowsize()+maxi(0, xpos);
1559   // Loop over rows
1560   for (int y=0; y<xrows; y++)
1561     {
1562       // Loop over columns
1563       for (int x=0; x<xcolumns; x++)
1564         {
1565           unsigned char srcpix = src[x];
1566           // Perform pixel operation
1567           if (srcpix > 0)
1568             {
1569               if (srcpix >= maxgray)
1570                 {
1571                   dst[x].b = src2[x].b;
1572                   dst[x].g = src2[x].g;
1573                   dst[x].r = src2[x].r;
1574                 }
1575               else
1576                 {
1577                   unsigned int level = multiplier[srcpix];
1578                   dst[x].b -= (((int)dst[x].b - (int)src2[x].b) * level) >> 16;
1579                   dst[x].g -= (((int)dst[x].g - (int)src2[x].g) * level) >> 16;
1580                   dst[x].r -= (((int)dst[x].r - (int)src2[x].r) * level) >> 16;
1581                 }
1582             }
1583         }
1584       // Next line
1585       dst += rowsize();
1586       src += bm->rowsize();
1587       src2 += color->rowsize();
1588     }
1589 }
1590 
1591 
1592 
1593 
1594 void
stencil(const GBitmap * bm,const GPixmap * pm,int pms,const GRect * pmr,double corr,GPixel white)1595 GPixmap::stencil(const GBitmap *bm,
1596                 const GPixmap *pm, int pms, const GRect *pmr,
1597                  double corr, GPixel white)
1598 {
1599   // Check arguments
1600   GRect rect(0, 0, pm->columns()*pms, pm->rows()*pms);
1601   if (pmr != 0)
1602     {
1603       if (pmr->xmin < rect.xmin ||
1604           pmr->ymin < rect.ymin ||
1605           pmr->xmax > rect.xmax ||
1606           pmr->ymax > rect.ymax  )
1607         G_THROW( ERR_MSG("GPixmap.overflow5") );
1608       rect = *pmr;
1609     }
1610   // Compute number of rows
1611   int xrows = nrows;
1612   if ((int)bm->rows() < xrows)
1613     xrows = bm->rows();
1614   if (rect.height() < xrows)
1615     xrows = rect.height();
1616   // Compute number of columns
1617   int xcolumns = ncolumns;
1618   if ((int)bm->columns() < xcolumns)
1619     xcolumns = bm->columns();
1620   if (rect.width() < xcolumns)
1621     xcolumns = rect.width();
1622   // Precompute multiplier map
1623   unsigned int multiplier[256];
1624   unsigned int maxgray = bm->get_grays() - 1;
1625   for (unsigned int i=1; i<maxgray ; i++)
1626     multiplier[i] = 0x10000 * i / maxgray;
1627   // Prepare color correction table
1628   unsigned char gtable[256][3];
1629   color_correction_table_cache(corr, white, gtable);
1630   // Compute starting point in blown up foreground pixmap
1631   int fgy, fgy1, fgxz, fgx1z;
1632   euclidian_ratio(rect.ymin, pms, fgy, fgy1);
1633   euclidian_ratio(rect.xmin, pms, fgxz, fgx1z);
1634   const GPixel *fg = (*pm)[fgy];
1635   const unsigned char *src = (*bm)[0];
1636   GPixel *dst = (*this)[0];
1637   // Loop over rows
1638   for (int y=0; y<xrows; y++)
1639   {
1640     // Loop over columns
1641     int fgx = fgxz;
1642     int fgx1 = fgx1z;
1643     for (int x=0; x<xcolumns; x++)
1644     {
1645       unsigned char srcpix = src[x];
1646       // Perform pixel operation
1647       if (srcpix > 0)
1648       {
1649         if (srcpix >= maxgray)
1650         {
1651           dst[x].b = gtable[fg[fgx].b][0];
1652           dst[x].g = gtable[fg[fgx].g][1];
1653           dst[x].r = gtable[fg[fgx].r][2];
1654         }
1655         else
1656         {
1657           unsigned int level = multiplier[srcpix];
1658           dst[x].b -= (((int)dst[x].b-(int)gtable[fg[fgx].b][0])*level) >> 16;
1659           dst[x].g -= (((int)dst[x].g-(int)gtable[fg[fgx].g][1])*level) >> 16;
1660           dst[x].r -= (((int)dst[x].r-(int)gtable[fg[fgx].r][2])*level) >> 16;
1661         }
1662       }
1663       // Next column
1664       if (++fgx1 >= pms)
1665       {
1666         fgx1 = 0;
1667         fgx += 1;
1668       }
1669     }
1670     // Next line
1671     dst += rowsize();
1672     src += bm->rowsize();
1673     if (++fgy1 >= pms)
1674     {
1675       fgy1 = 0;
1676       fg += pm->rowsize();
1677     }
1678   }
1679 }
1680 
1681 void
stencil(const GBitmap * bm,const GPixmap * pm,int pms,const GRect * pmr,double corr)1682 GPixmap::stencil(const GBitmap *bm,
1683                 const GPixmap *pm, int pms, const GRect *pmr,
1684                 double corr)
1685 {
1686   stencil(bm, pm, pms, pmr, corr, GPixel::WHITE);
1687 }
1688 
1689 
rotate(int count)1690 GP<GPixmap> GPixmap::rotate(int count)
1691 {
1692   GP<GPixmap> newpixmap(this);
1693   count = count & 3;
1694   if(count)
1695   {
1696     if( count&0x01)
1697       newpixmap = new GPixmap(ncolumns, nrows);
1698     else
1699       newpixmap = new GPixmap(nrows, ncolumns);
1700 
1701     GPixmap &dpixmap = *newpixmap;
1702 
1703     GMonitorLock lock(&pixmap_monitor());
1704     switch(count)
1705     {
1706     case 3: //// rotate 90 counter clockwise
1707         {
1708             int lastrow = dpixmap.rows()-1;
1709 
1710             for(int y=0; y<nrows; y++)
1711             {
1712                 const GPixel *r=operator [] (y);
1713                 for(int x=0,xnew=lastrow; xnew>=0; x++,xnew--)
1714                 {
1715                     dpixmap[xnew][y] = r[x];
1716                 }
1717             }
1718         }
1719         break;
1720     case 2: //// rotate 180 counter clockwise
1721         {
1722             int lastrow = dpixmap.rows()-1;
1723             int lastcolumn = dpixmap.columns()-1;
1724 
1725             for(int y=0,ynew=lastrow; ynew>=0; y++,ynew--)
1726             {
1727                 const GPixel *r=operator [] (y);
1728                 GPixel *d=dpixmap[ynew];
1729                 for(int xnew=lastcolumn; xnew>=0; r++,xnew--)
1730                 {
1731                     d[xnew] = *r;
1732                 }
1733             }
1734         }
1735         break;
1736     case 1: //// rotate 270 counter clockwise
1737         {
1738             int lastcolumn = dpixmap.columns()-1;
1739 
1740             for(int y=0,ynew=lastcolumn; ynew>=0; y++,ynew--)
1741             {
1742                 const GPixel *r=operator [] (y);
1743                 for(int x=0; x<ncolumns; x++)
1744                 {
1745                     dpixmap[x][ynew] = r[x];
1746                 }
1747             }
1748         }
1749         break;
1750     }
1751   }
1752   return newpixmap;
1753 }
1754 
1755 
1756 
1757 #ifdef HAVE_NAMESPACES
1758 }
1759 # ifndef NOT_USING_DJVU_NAMESPACE
1760 using namespace DJVU;
1761 # endif
1762 #endif
1763 
1764