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