1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8 
9 //========================================================================
10 //
11 // Modified under the Poppler project - http://poppler.freedesktop.org
12 //
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
15 //
16 // Copyright (C) 2005 Takashi Iwai <tiwai@suse.de>
17 // Copyright (C) 2006 Stefan Schweizer <genstef@gentoo.org>
18 // Copyright (C) 2006-2011 Albert Astals Cid <aacid@kde.org>
19 // Copyright (C) 2006 Krzysztof Kowalczyk <kkowalczyk@gmail.com>
20 // Copyright (C) 2006 Scott Turner <scotty1024@mac.com>
21 // Copyright (C) 2007 Koji Otani <sho@bbr.jp>
22 // Copyright (C) 2009 Petr Gajdos <pgajdos@novell.com>
23 // Copyright (C) 2009-2011 Thomas Freitag <Thomas.Freitag@alfa.de>
24 // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
25 // Copyright (C) 2009 William Bader <williambader@hotmail.com>
26 // Copyright (C) 2010 Patrick Spendrin <ps_ml@gmx.de>
27 // Copyright (C) 2010 Brian Cameron <brian.cameron@oracle.com>
28 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com>
29 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
30 //
31 // To see a description of the changes please see the Changelog file that
32 // came with your tarball or type make ChangeLog if you are building from git
33 //
34 //========================================================================
35 
36 #include <config.h>
37 
38 #ifdef USE_GCC_PRAGMAS
39 #pragma implementation
40 #endif
41 
42 #include <string.h>
43 #include <math.h>
44 #include "goo/gfile.h"
45 #include "GlobalParams.h"
46 #include "Error.h"
47 #include "Object.h"
48 #include "GfxFont.h"
49 #include "Link.h"
50 #include "CharCodeToUnicode.h"
51 #include "FontEncodingTables.h"
52 #include "fofi/FoFiTrueType.h"
53 #include "splash/SplashBitmap.h"
54 #include "splash/SplashGlyphBitmap.h"
55 #include "splash/SplashPattern.h"
56 #include "splash/SplashScreen.h"
57 #include "splash/SplashPath.h"
58 #include "splash/SplashState.h"
59 #include "splash/SplashErrorCodes.h"
60 #include "splash/SplashFontEngine.h"
61 #include "splash/SplashFont.h"
62 #include "splash/SplashFontFile.h"
63 #include "splash/SplashFontFileID.h"
64 #include "splash/Splash.h"
65 #include "SplashOutputDev.h"
66 
67 #ifdef VMS
68 #if (__VMS_VER < 70000000)
69 extern "C" int unlink(char *filename);
70 #endif
71 #endif
72 
73 #ifdef _MSC_VER
74 #include <float.h>
75 #define isfinite(x) _finite(x)
76 #endif
77 
78 #ifdef __sun
79 #include <ieeefp.h>
80 #define isfinite(x) finite(x)
81 #endif
82 
convertGfxColor(SplashColorPtr dest,SplashColorMode colorMode,GfxColorSpace * colorSpace,GfxColor * src)83 static inline void convertGfxColor(SplashColorPtr dest,
84                                    SplashColorMode colorMode,
85                                    GfxColorSpace *colorSpace,
86                                    GfxColor *src) {
87   SplashColor color;
88   GfxGray gray;
89   GfxRGB rgb;
90 #if SPLASH_CMYK
91   GfxCMYK cmyk;
92 #endif
93 
94   switch (colorMode) {
95     case splashModeMono1:
96     case splashModeMono8:
97       colorSpace->getGray(src, &gray);
98       color[0] = colToByte(gray);
99     break;
100     case splashModeXBGR8:
101       color[3] = 255;
102     case splashModeBGR8:
103     case splashModeRGB8:
104       colorSpace->getRGB(src, &rgb);
105       color[0] = colToByte(rgb.r);
106       color[1] = colToByte(rgb.g);
107       color[2] = colToByte(rgb.b);
108     break;
109 #if SPLASH_CMYK
110     case splashModeCMYK8:
111       colorSpace->getCMYK(src, &cmyk);
112       color[0] = colToByte(cmyk.c);
113       color[1] = colToByte(cmyk.m);
114       color[2] = colToByte(cmyk.y);
115       color[3] = colToByte(cmyk.k);
116     break;
117 #endif
118   }
119   splashColorCopy(dest, color);
120 }
121 
122 
123 //------------------------------------------------------------------------
124 // SplashGouraudPattern
125 //------------------------------------------------------------------------
SplashGouraudPattern(GBool bDirectColorTranslationA,GfxState * stateA,GfxGouraudTriangleShading * shadingA)126 SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA,
127                                            GfxState *stateA, GfxGouraudTriangleShading *shadingA) {
128   state = stateA;
129   shading = shadingA;
130   bDirectColorTranslation = bDirectColorTranslationA;
131 }
132 
~SplashGouraudPattern()133 SplashGouraudPattern::~SplashGouraudPattern() {
134 }
135 
getParameterizedColor(double colorinterp,SplashColorMode mode,SplashColorPtr dest)136 void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
137   GfxColor src;
138   GfxColorSpace* srcColorSpace = shading->getColorSpace();
139   int colorComps = 3;
140 #if SPLASH_CMYK
141   if (mode == splashModeCMYK8)
142     colorComps=4;
143 #endif
144 
145   shading->getParameterizedColor(colorinterp, &src);
146 
147   if (bDirectColorTranslation) {
148     for (int m = 0; m < colorComps; ++m)
149       dest[m] = colToByte(src.c[m]);
150   } else {
151     convertGfxColor(dest, mode, srcColorSpace, &src);
152   }
153 }
154 
155 //------------------------------------------------------------------------
156 // SplashAxialPattern
157 //------------------------------------------------------------------------
158 
SplashAxialPattern(SplashColorMode colorModeA,GfxState * stateA,GfxAxialShading * shadingA)159 SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA) {
160   Matrix ctm;
161 
162   shading = shadingA;
163   state = stateA;
164   colorMode = colorModeA;
165   state->getCTM(&ctm);
166   ctm.invertTo(&ictm);
167 
168   shading->getCoords(&x0, &y0, &x1, &y1);
169   dx = x1 - x0;
170   dy = y1 - y0;
171   mul = 1 / (dx * dx + dy * dy);
172 
173   // get the function domain
174   t0 = shading->getDomain0();
175   t1 = shading->getDomain1();
176 }
177 
~SplashAxialPattern()178 SplashAxialPattern::~SplashAxialPattern() {
179 }
180 
getColor(int x,int y,SplashColorPtr c)181 GBool SplashAxialPattern::getColor(int x, int y, SplashColorPtr c) {
182   GfxColor gfxColor;
183   double tt;
184   double xc, yc, xaxis;
185 
186   ictm.transform(x, y, &xc, &yc);
187   xaxis = ((xc - x0) * dx + (yc - y0) * dy) * mul;
188   if (xaxis < 0 && shading->getExtend0()) {
189     tt = t0;
190   } else if (xaxis > 1 && shading->getExtend1()) {
191     tt = t1;
192   } else if (xaxis >= 0 && xaxis <= 1) {
193     tt = t0 + (t1 -t0) * xaxis;
194   } else
195     return gFalse;
196 
197   shading->getColor(tt, &gfxColor);
198   state->setFillColor(&gfxColor);
199   convertGfxColor(c, colorMode, state->getFillColorSpace(), state->getFillColor());
200   return gTrue;
201 }
202 
203 //------------------------------------------------------------------------
204 
205 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)206 static inline Guchar div255(int x) {
207   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
208 }
209 
210 #if SPLASH_CMYK
211 
212 #include "GfxState_helpers.h"
213 
214 //-------------------------------------------------------------------------
215 // helper for Blend functions (convert CMYK to RGB, do blend, convert back)
216 //-------------------------------------------------------------------------
217 
218 // based in GfxState.cc
219 
cmykToRGB(SplashColorPtr cmyk,SplashColor rgb)220 static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) {
221   double c, m, y, k, c1, m1, y1, k1, r, g, b;
222 
223   c = colToDbl(byteToCol(cmyk[0]));
224   m = colToDbl(byteToCol(cmyk[1]));
225   y = colToDbl(byteToCol(cmyk[2]));
226   k = colToDbl(byteToCol(cmyk[3]));
227   c1 = 1 - c;
228   m1 = 1 - m;
229   y1 = 1 - y;
230   k1 = 1 - k;
231   cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
232   rgb[0] = colToByte(clip01(dblToCol(r)));
233   rgb[1] = colToByte(clip01(dblToCol(g)));
234   rgb[2] = colToByte(clip01(dblToCol(b)));
235 }
236 
rgbToCMYK(SplashColor rgb,SplashColorPtr cmyk)237 static void rgbToCMYK(SplashColor rgb, SplashColorPtr cmyk) {
238   GfxColorComp c, m, y, k;
239 
240   c = clip01(gfxColorComp1 - byteToCol(rgb[0]));
241   m = clip01(gfxColorComp1 - byteToCol(rgb[1]));
242   y = clip01(gfxColorComp1 - byteToCol(rgb[2]));
243   k = c;
244   if (m < k) {
245     k = m;
246   }
247   if (y < k) {
248     k = y;
249   }
250   cmyk[0] = colToByte(c - k);
251   cmyk[1] = colToByte(m - k);
252   cmyk[2] = colToByte(y - k);
253   cmyk[3] = colToByte(k);
254 }
255 
256 #endif
257 
258 //------------------------------------------------------------------------
259 // Blend functions
260 //------------------------------------------------------------------------
261 
splashOutBlendMultiply(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)262 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
263 				   SplashColorPtr blend, SplashColorMode cm) {
264   int i;
265 
266 #ifdef SPLASH_CMYK
267   if (cm == splashModeCMYK8) {
268     SplashColor rgbSrc;
269     SplashColor rgbDest;
270     SplashColor rgbBlend;
271     cmykToRGB(src, rgbSrc);
272     cmykToRGB(dest, rgbDest);
273     for (i = 0; i < 3; ++i) {
274       rgbBlend[i] = (rgbDest[i] * rgbSrc[i]) / 255;
275     }
276     rgbToCMYK(rgbBlend, blend);
277   } else
278 #endif
279   {
280     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
281       blend[i] = (dest[i] * src[i]) / 255;
282     }
283   }
284 }
285 
splashOutBlendScreen(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)286 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
287 				 SplashColorPtr blend, SplashColorMode cm) {
288   int i;
289 
290 #ifdef SPLASH_CMYK
291   if (cm == splashModeCMYK8) {
292     SplashColor rgbSrc;
293     SplashColor rgbDest;
294     SplashColor rgbBlend;
295     cmykToRGB(src, rgbSrc);
296     cmykToRGB(dest, rgbDest);
297     for (i = 0; i < 3; ++i) {
298       rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (rgbDest[i] * rgbSrc[i]) / 255;
299     }
300     rgbToCMYK(rgbBlend, blend);
301   } else
302 #endif
303   {
304     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
305       blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
306     }
307   }
308 }
309 
splashOutBlendOverlay(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)310 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
311 				  SplashColorPtr blend, SplashColorMode cm) {
312   int i;
313 
314 #ifdef SPLASH_CMYK
315   if (cm == splashModeCMYK8) {
316     SplashColor rgbSrc;
317     SplashColor rgbDest;
318     SplashColor rgbBlend;
319     cmykToRGB(src, rgbSrc);
320     cmykToRGB(dest, rgbDest);
321     for (i = 0; i < 3; ++i) {
322       rgbBlend[i] = rgbDest[i] < 0x80
323                       ? (rgbSrc[i] * 2 * rgbDest[i]) / 255
324                       : 255 - 2 * ((255 - rgbSrc[i]) * (255 - rgbDest[i])) / 255;
325     }
326     rgbToCMYK(rgbBlend, blend);
327   } else
328 #endif
329   {
330     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
331       blend[i] = dest[i] < 0x80
332                    ? (src[i] * 2 * dest[i]) / 255
333                    : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
334     }
335   }
336 }
337 
splashOutBlendDarken(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)338 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
339 				 SplashColorPtr blend, SplashColorMode cm) {
340   int i;
341 
342 #ifdef SPLASH_CMYK
343   if (cm == splashModeCMYK8) {
344     SplashColor rgbSrc;
345     SplashColor rgbDest;
346     SplashColor rgbBlend;
347     cmykToRGB(src, rgbSrc);
348     cmykToRGB(dest, rgbDest);
349     for (i = 0; i < 3; ++i) {
350       rgbBlend[i] = rgbDest[i] < rgbSrc[i] ? rgbDest[i] : rgbSrc[i];
351     }
352     rgbToCMYK(rgbBlend, blend);
353   } else
354 #endif
355   {
356     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
357       blend[i] = dest[i] < src[i] ? dest[i] : src[i];
358     }
359   }
360 }
361 
splashOutBlendLighten(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)362 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
363 				  SplashColorPtr blend, SplashColorMode cm) {
364   int i;
365 
366 #ifdef SPLASH_CMYK
367   if (cm == splashModeCMYK8) {
368     SplashColor rgbSrc;
369     SplashColor rgbDest;
370     SplashColor rgbBlend;
371     cmykToRGB(src, rgbSrc);
372     cmykToRGB(dest, rgbDest);
373     for (i = 0; i < 3; ++i) {
374       rgbBlend[i] = rgbDest[i] > rgbSrc[i] ? rgbDest[i] : rgbSrc[i];
375     }
376     rgbToCMYK(rgbBlend, blend);
377   } else
378 #endif
379   {
380     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
381       blend[i] = dest[i] > src[i] ? dest[i] : src[i];
382     }
383   }
384 }
385 
splashOutBlendColorDodge(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)386 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
387 				     SplashColorPtr blend,
388 				     SplashColorMode cm) {
389   int i, x;
390 
391 #ifdef SPLASH_CMYK
392   if (cm == splashModeCMYK8) {
393     SplashColor rgbSrc;
394     SplashColor rgbDest;
395     SplashColor rgbBlend;
396     cmykToRGB(src, rgbSrc);
397     cmykToRGB(dest, rgbDest);
398     for (i = 0; i < 3; ++i) {
399       if (rgbSrc[i] == 255) {
400         rgbBlend[i] = 255;
401       } else {
402         x = (rgbDest[i] * 255) / (255 - rgbSrc[i]);
403         rgbBlend[i] = x <= 255 ? x : 255;
404       }
405     }
406     rgbToCMYK(rgbBlend, blend);
407   } else
408 #endif
409   {
410     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
411       if (src[i] == 255) {
412         blend[i] = 255;
413       } else {
414         x = (dest[i] * 255) / (255 - src[i]);
415         blend[i] = x <= 255 ? x : 255;
416       }
417     }
418   }
419 }
420 
splashOutBlendColorBurn(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)421 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
422 				    SplashColorPtr blend, SplashColorMode cm) {
423   int i, x;
424 
425 #ifdef SPLASH_CMYK
426   if (cm == splashModeCMYK8) {
427     SplashColor rgbSrc;
428     SplashColor rgbDest;
429     SplashColor rgbBlend;
430     cmykToRGB(src, rgbSrc);
431     cmykToRGB(dest, rgbDest);
432     for (i = 0; i < 3; ++i) {
433       if (rgbSrc[i] == 0) {
434         rgbBlend[i] = 0;
435       } else {
436         x = ((255 - rgbDest[i]) * 255) / rgbSrc[i];
437         rgbBlend[i] = x <= 255 ? 255 - x : 0;
438       }
439     }
440     rgbToCMYK(rgbBlend, blend);
441   } else
442 #endif
443   {
444     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
445       if (src[i] == 0) {
446         blend[i] = 0;
447       } else {
448         x = ((255 - dest[i]) * 255) / src[i];
449         blend[i] = x <= 255 ? 255 - x : 0;
450       }
451     }
452   }
453 }
454 
splashOutBlendHardLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)455 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
456 				    SplashColorPtr blend, SplashColorMode cm) {
457   int i;
458 
459 #ifdef SPLASH_CMYK
460   if (cm == splashModeCMYK8) {
461     SplashColor rgbSrc;
462     SplashColor rgbDest;
463     SplashColor rgbBlend;
464     cmykToRGB(src, rgbSrc);
465     cmykToRGB(dest, rgbDest);
466     for (i = 0; i < 3; ++i) {
467       rgbBlend[i] = rgbSrc[i] < 0x80
468                       ? (rgbDest[i] * 2 * rgbSrc[i]) / 255
469                       : 255 - 2 * ((255 - rgbDest[i]) * (255 - rgbSrc[i])) / 255;
470     }
471     rgbToCMYK(rgbBlend, blend);
472   } else
473 #endif
474   {
475     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
476       blend[i] = src[i] < 0x80
477                    ? (dest[i] * 2 * src[i]) / 255
478                    : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
479     }
480   }
481 }
482 
splashOutBlendSoftLight(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)483 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
484 				    SplashColorPtr blend, SplashColorMode cm) {
485   int i, x;
486 
487 #ifdef SPLASH_CMYK
488   if (cm == splashModeCMYK8) {
489     SplashColor rgbSrc;
490     SplashColor rgbDest;
491     SplashColor rgbBlend;
492     cmykToRGB(src, rgbSrc);
493     cmykToRGB(dest, rgbDest);
494     for (i = 0; i < 3; ++i) {
495       if (rgbSrc[i] < 0x80) {
496         rgbBlend[i] = rgbDest[i] - (255 - 2 * rgbSrc[i]) * rgbDest[i] * (255 - rgbDest[i]) / (255 * 255);
497       } else {
498         if (rgbDest[i] < 0x40) {
499           x = (((((16 * rgbDest[i] - 12 * 255) * rgbDest[i]) / 255) + 4 * 255) * rgbDest[i]) / 255;
500         } else {
501           x = (int)sqrt(255.0 * rgbDest[i]);
502         }
503         rgbBlend[i] = rgbDest[i] + (2 * rgbSrc[i] - 255) * (x - rgbDest[i]) / 255;
504       }
505     }
506     rgbToCMYK(rgbBlend, blend);
507   } else
508 #endif
509   {
510     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
511       if (src[i] < 0x80) {
512         blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255);
513       } else {
514         if (dest[i] < 0x40) {
515           x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255;
516         } else {
517           x = (int)sqrt(255.0 * dest[i]);
518         }
519         blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
520       }
521     }
522   }
523 }
524 
splashOutBlendDifference(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)525 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
526 				     SplashColorPtr blend,
527 				     SplashColorMode cm) {
528   int i;
529 
530   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
531 #ifdef SPLASH_CMYK
532     if (cm == splashModeCMYK8)
533     {
534       blend[i] = dest[i] < src[i] ? 255 - (src[i] - dest[i]) : 255 - (dest[i] - src[i]);
535     }
536     else
537 #endif
538     {
539       blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
540     }
541   }
542 }
543 
splashOutBlendExclusion(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)544 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
545 				    SplashColorPtr blend, SplashColorMode cm) {
546   int i;
547 
548 #ifdef SPLASH_CMYK
549   if (cm == splashModeCMYK8) {
550     SplashColor rgbSrc;
551     SplashColor rgbDest;
552     SplashColor rgbBlend;
553     cmykToRGB(src, rgbSrc);
554     cmykToRGB(dest, rgbDest);
555     for (i = 0; i < 3; ++i) {
556       rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (2 * rgbDest[i] * rgbSrc[i]) / 255;
557     }
558     rgbToCMYK(rgbBlend, blend);
559   } else
560 #endif
561   {
562     for (i = 0; i < splashColorModeNComps[cm]; ++i) {
563       blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
564     }
565   }
566 }
567 
cvtRGBToHSV(Guchar r,Guchar g,Guchar b,int * h,int * s,int * v)568 static void cvtRGBToHSV(Guchar r, Guchar g, Guchar b, int *h, int *s, int *v) {
569   int cmax, cmid, cmin, x;
570 
571   if (r >= g) {
572     if (g >= b)      { x = 0; cmax = r; cmid = g; cmin = b; }
573     else if (b >= r) { x = 4; cmax = b; cmid = r; cmin = g; }
574     else             { x = 5; cmax = r; cmid = b; cmin = g; }
575   } else {
576     if (r >= b)      { x = 1; cmax = g; cmid = r; cmin = b; }
577     else if (g >= b) { x = 2; cmax = g; cmid = b; cmin = r; }
578     else             { x = 3; cmax = b; cmid = g; cmin = r; }
579   }
580   if (cmax == cmin) {
581     *h = *s = 0;
582   } else {
583     *h = x * 60;
584     if (x & 1) {
585       *h += ((cmax - cmid) * 60) / (cmax - cmin);
586     } else {
587       *h += ((cmid - cmin) * 60) / (cmax - cmin);
588     }
589     *s = (255 * (cmax - cmin)) / cmax;
590   }
591   *v = cmax;
592 }
593 
cvtHSVToRGB(int h,int s,int v,Guchar * r,Guchar * g,Guchar * b)594 static void cvtHSVToRGB(int h, int s, int v, Guchar *r, Guchar *g, Guchar *b) {
595   int x, f, cmax, cmid, cmin;
596 
597   if (s == 0) {
598     *r = *g = *b = v;
599   } else {
600     x = h / 60;
601     f = h % 60;
602     cmax = v;
603     if (x & 1) {
604       cmid = div255(v * 255 - ((s * f) / 60));
605     } else {
606       cmid = div255(v * (255 - ((s * (60 - f)) / 60)));
607     }
608     cmin = div255(v * (255 - s));
609     switch (x) {
610     case 0: *r = cmax; *g = cmid; *b = cmin; break;
611     case 1: *g = cmax; *r = cmid; *b = cmin; break;
612     case 2: *g = cmax; *b = cmid; *r = cmin; break;
613     case 3: *b = cmax; *g = cmid; *r = cmin; break;
614     case 4: *b = cmax; *r = cmid; *g = cmin; break;
615     case 5: *r = cmax; *b = cmid; *g = cmin; break;
616     }
617   }
618 }
619 
splashOutBlendHue(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)620 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
621 			      SplashColorPtr blend, SplashColorMode cm) {
622   int hs, ss, vs, hd, sd, vd;
623 #if SPLASH_CMYK
624   Guchar r, g, b;
625 #endif
626 
627   switch (cm) {
628   case splashModeMono1:
629   case splashModeMono8:
630     blend[0] = dest[0];
631     break;
632   case splashModeXBGR8:
633     src[3] = 255;
634   case splashModeRGB8:
635   case splashModeBGR8:
636     cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
637     cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
638     cvtHSVToRGB(hs, sd, vd, &blend[0], &blend[1], &blend[2]);
639     break;
640 #if SPLASH_CMYK
641   case splashModeCMYK8:
642     //~ (0xff - ...) should be clipped
643     cvtRGBToHSV(0xff - (src[0] + src[3]),
644 		0xff - (src[1] + src[3]),
645 		0xff - (src[2] + src[3]), &hs, &ss, &vs);
646     cvtRGBToHSV(0xff - (dest[0] + dest[3]),
647 		0xff - (dest[1] + dest[3]),
648 		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
649     cvtHSVToRGB(hs, sd, vd, &r, &g, &b);
650     //~ should do black generation
651     blend[0] = 0xff - r;
652     blend[1] = 0xff - g;
653     blend[2] = 0xff - b;
654     blend[3] = 0;
655     break;
656 #endif
657   }
658 }
659 
splashOutBlendSaturation(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)660 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
661 				     SplashColorPtr blend,
662 				     SplashColorMode cm) {
663   int hs, ss, vs, hd, sd, vd;
664 #if SPLASH_CMYK
665   Guchar r, g, b;
666 #endif
667 
668   switch (cm) {
669   case splashModeMono1:
670   case splashModeMono8:
671     blend[0] = dest[0];
672     break;
673   case splashModeXBGR8:
674     src[3] = 255;
675   case splashModeRGB8:
676   case splashModeBGR8:
677     cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
678     cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
679     cvtHSVToRGB(hd, ss, vd, &blend[0], &blend[1], &blend[2]);
680     break;
681 #if SPLASH_CMYK
682   case splashModeCMYK8:
683     //~ (0xff - ...) should be clipped
684     cvtRGBToHSV(0xff - (src[0] + src[3]),
685 		0xff - (src[1] + src[3]),
686 		0xff - (src[2] + src[3]), &hs, &ss, &vs);
687     cvtRGBToHSV(0xff - (dest[0] + dest[3]),
688 		0xff - (dest[1] + dest[3]),
689 		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
690     cvtHSVToRGB(hd, ss, vd, &r, &g, &b);
691     //~ should do black generation
692     blend[0] = 0xff - r;
693     blend[1] = 0xff - g;
694     blend[2] = 0xff - b;
695     blend[3] = 0;
696     break;
697 #endif
698   }
699 }
700 
splashOutBlendColor(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)701 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
702 				SplashColorPtr blend, SplashColorMode cm) {
703   int hs, ss, vs, hd, sd, vd;
704 
705   switch (cm) {
706   case splashModeMono1:
707   case splashModeMono8:
708     blend[0] = dest[0];
709     break;
710   case splashModeXBGR8:
711     src[3] = 255;
712   case splashModeRGB8:
713   case splashModeBGR8:
714     cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
715     cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
716     cvtHSVToRGB(hs, ss, vd, &blend[0], &blend[1], &blend[2]);
717     break;
718 #if SPLASH_CMYK
719   case splashModeCMYK8:
720     SplashColor rgbSrc;
721     SplashColor rgbDest;
722     SplashColor rgbBlend;
723     cmykToRGB(src, rgbSrc);
724     cmykToRGB(dest, rgbDest);
725     cvtRGBToHSV(rgbSrc[0], rgbSrc[1], rgbSrc[2], &hs, &ss, &vs);
726     cvtRGBToHSV(rgbDest[0], rgbDest[1], rgbDest[2], &hd, &sd, &vd);
727     cvtHSVToRGB(hs, ss, vd, &rgbBlend[0], &rgbBlend[1], &rgbBlend[2]);
728 	rgbToCMYK(rgbBlend, blend);
729     break;
730 #endif
731   }
732 }
733 
splashOutBlendLuminosity(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)734 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
735 				     SplashColorPtr blend,
736 				     SplashColorMode cm) {
737   int hs, ss, vs, hd, sd, vd;
738 #if SPLASH_CMYK
739   Guchar r, g, b;
740 #endif
741 
742   switch (cm) {
743   case splashModeMono1:
744   case splashModeMono8:
745     blend[0] = dest[0];
746     break;
747   case splashModeXBGR8:
748     src[3] = 255;
749   case splashModeRGB8:
750   case splashModeBGR8:
751     cvtRGBToHSV(src[0], src[1], src[2], &hs, &ss, &vs);
752     cvtRGBToHSV(dest[0], dest[1], dest[2], &hd, &sd, &vd);
753     cvtHSVToRGB(hd, sd, vs, &blend[0], &blend[1], &blend[2]);
754     break;
755 #if SPLASH_CMYK
756   case splashModeCMYK8:
757     //~ (0xff - ...) should be clipped
758     cvtRGBToHSV(0xff - (src[0] + src[3]),
759 		0xff - (src[1] + src[3]),
760 		0xff - (src[2] + src[3]), &hs, &ss, &vs);
761     cvtRGBToHSV(0xff - (dest[0] + dest[3]),
762 		0xff - (dest[1] + dest[3]),
763 		0xff - (dest[2] + dest[3]), &hd, &sd, &vd);
764     cvtHSVToRGB(hd, sd, vs, &r, &g, &b);
765     //~ should do black generation
766     blend[0] = 0xff - r;
767     blend[1] = 0xff - g;
768     blend[2] = 0xff - b;
769     blend[3] = 0;
770     break;
771 #endif
772   }
773 }
774 
775 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
776 static const SplashBlendFunc splashOutBlendFuncs[] = {
777   NULL,
778   &splashOutBlendMultiply,
779   &splashOutBlendScreen,
780   &splashOutBlendOverlay,
781   &splashOutBlendDarken,
782   &splashOutBlendLighten,
783   &splashOutBlendColorDodge,
784   &splashOutBlendColorBurn,
785   &splashOutBlendHardLight,
786   &splashOutBlendSoftLight,
787   &splashOutBlendDifference,
788   &splashOutBlendExclusion,
789   &splashOutBlendHue,
790   &splashOutBlendSaturation,
791   &splashOutBlendColor,
792   &splashOutBlendLuminosity
793 };
794 
795 //------------------------------------------------------------------------
796 // SplashOutFontFileID
797 //------------------------------------------------------------------------
798 
799 class SplashOutFontFileID: public SplashFontFileID {
800 public:
801 
SplashOutFontFileID(Ref * rA)802   SplashOutFontFileID(Ref *rA) { r = *rA; }
803 
~SplashOutFontFileID()804   ~SplashOutFontFileID() {}
805 
matches(SplashFontFileID * id)806   GBool matches(SplashFontFileID *id) {
807     return ((SplashOutFontFileID *)id)->r.num == r.num &&
808            ((SplashOutFontFileID *)id)->r.gen == r.gen;
809   }
810 
811 private:
812 
813   Ref r;
814 };
815 
816 //------------------------------------------------------------------------
817 // T3FontCache
818 //------------------------------------------------------------------------
819 
820 struct T3FontCacheTag {
821   Gushort code;
822   Gushort mru;			// valid bit (0x8000) and MRU index
823 };
824 
825 class T3FontCache {
826 public:
827 
828   T3FontCache(Ref *fontID, double m11A, double m12A,
829 	      double m21A, double m22A,
830 	      int glyphXA, int glyphYA, int glyphWA, int glyphHA,
831 	      GBool aa, GBool validBBoxA);
832   ~T3FontCache();
matches(Ref * idA,double m11A,double m12A,double m21A,double m22A)833   GBool matches(Ref *idA, double m11A, double m12A,
834 		double m21A, double m22A)
835     { return fontID.num == idA->num && fontID.gen == idA->gen &&
836 	     m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
837 
838   Ref fontID;			// PDF font ID
839   double m11, m12, m21, m22;	// transform matrix
840   int glyphX, glyphY;		// pixel offset of glyph bitmaps
841   int glyphW, glyphH;		// size of glyph bitmaps, in pixels
842   GBool validBBox;		// false if the bbox was [0 0 0 0]
843   int glyphSize;		// size of glyph bitmaps, in bytes
844   int cacheSets;		// number of sets in cache
845   int cacheAssoc;		// cache associativity (glyphs per set)
846   Guchar *cacheData;		// glyph pixmap cache
847   T3FontCacheTag *cacheTags;	// cache tags, i.e., char codes
848 };
849 
T3FontCache(Ref * fontIDA,double m11A,double m12A,double m21A,double m22A,int glyphXA,int glyphYA,int glyphWA,int glyphHA,GBool validBBoxA,GBool aa)850 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
851 			 double m21A, double m22A,
852 			 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
853 			 GBool validBBoxA, GBool aa) {
854   int i;
855 
856   fontID = *fontIDA;
857   m11 = m11A;
858   m12 = m12A;
859   m21 = m21A;
860   m22 = m22A;
861   glyphX = glyphXA;
862   glyphY = glyphYA;
863   glyphW = glyphWA;
864   glyphH = glyphHA;
865   validBBox = validBBoxA;
866   if (aa) {
867     glyphSize = glyphW * glyphH;
868   } else {
869     glyphSize = ((glyphW + 7) >> 3) * glyphH;
870   }
871   cacheAssoc = 8;
872   if (glyphSize <= 256) {
873     cacheSets = 8;
874   } else if (glyphSize <= 512) {
875     cacheSets = 4;
876   } else if (glyphSize <= 1024) {
877     cacheSets = 2;
878   } else if (glyphSize <= 2048) {
879     cacheSets = 1;
880     cacheAssoc = 4;
881   } else if (glyphSize <= 4096) {
882     cacheSets = 1;
883     cacheAssoc = 2;
884   } else {
885     cacheSets = 1;
886     cacheAssoc = 1;
887   }
888   if (glyphSize < 10485760 / cacheAssoc / cacheSets) {
889     cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
890   } else {
891     error(-1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n"
892               "       This could teoretically result in wrong rendering,\n"
893               "       but most probably the document is bogus.\n"
894               "       Please report a bug if you think the rendering may be wrong because of this.");
895     cacheData = NULL;
896   }
897   if (cacheData != NULL)
898   {
899     cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
900 					 sizeof(T3FontCacheTag));
901     for (i = 0; i < cacheSets * cacheAssoc; ++i) {
902       cacheTags[i].mru = i & (cacheAssoc - 1);
903     }
904   }
905   else
906   {
907     cacheTags = NULL;
908   }
909 }
910 
~T3FontCache()911 T3FontCache::~T3FontCache() {
912   gfree(cacheData);
913   gfree(cacheTags);
914 }
915 
916 struct T3GlyphStack {
917   Gushort code;			// character code
918 
919   //----- cache info
920   T3FontCache *cache;		// font cache for the current font
921   T3FontCacheTag *cacheTag;	// pointer to cache tag for the glyph
922   Guchar *cacheData;		// pointer to cache data for the glyph
923 
924   //----- saved state
925   SplashBitmap *origBitmap;
926   Splash *origSplash;
927   double origCTM4, origCTM5;
928 
929   T3GlyphStack *next;		// next object on stack
930 };
931 
932 //------------------------------------------------------------------------
933 // SplashTransparencyGroup
934 //------------------------------------------------------------------------
935 
936 struct SplashTransparencyGroup {
937   int tx, ty;			// translation coordinates
938   SplashBitmap *tBitmap;	// bitmap for transparency group
939   GfxColorSpace *blendingColorSpace;
940   GBool isolated;
941 
942   //----- saved state
943   SplashBitmap *origBitmap;
944   Splash *origSplash;
945 
946   SplashTransparencyGroup *next;
947 };
948 
949 //------------------------------------------------------------------------
950 // SplashOutputDev
951 //------------------------------------------------------------------------
952 
SplashOutputDev(SplashColorMode colorModeA,int bitmapRowPadA,GBool reverseVideoA,SplashColorPtr paperColorA,GBool bitmapTopDownA,GBool allowAntialiasA)953 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
954 				 int bitmapRowPadA,
955 				 GBool reverseVideoA,
956 				 SplashColorPtr paperColorA,
957 				 GBool bitmapTopDownA,
958 				 GBool allowAntialiasA) {
959   colorMode = colorModeA;
960   bitmapRowPad = bitmapRowPadA;
961   bitmapTopDown = bitmapTopDownA;
962   allowAntialias = allowAntialiasA;
963   vectorAntialias = allowAntialias &&
964 		      globalParams->getVectorAntialias() &&
965 		      colorMode != splashModeMono1;
966   enableFreeTypeHinting = gFalse;
967   setupScreenParams(72.0, 72.0);
968   reverseVideo = reverseVideoA;
969   if (paperColorA != NULL) {
970     splashColorCopy(paperColor, paperColorA);
971   } else {
972     splashClearColor(paperColor);
973   }
974   keepAlphaChannel = paperColorA == NULL;
975 
976   xref = NULL;
977 
978   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
979 			    colorMode != splashModeMono1, bitmapTopDown);
980   splash = new Splash(bitmap, vectorAntialias, &screenParams);
981   splash->clear(paperColor, 0);
982 
983   fontEngine = NULL;
984 
985   nT3Fonts = 0;
986   t3GlyphStack = NULL;
987 
988   font = NULL;
989   needFontUpdate = gFalse;
990   textClipPath = NULL;
991   haveCSPattern = gFalse;
992   transpGroupStack = NULL;
993 }
994 
setupScreenParams(double hDPI,double vDPI)995 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
996   screenParams.size = globalParams->getScreenSize();
997   screenParams.dotRadius = globalParams->getScreenDotRadius();
998   screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
999   screenParams.blackThreshold =
1000       (SplashCoord)globalParams->getScreenBlackThreshold();
1001   screenParams.whiteThreshold =
1002       (SplashCoord)globalParams->getScreenWhiteThreshold();
1003   switch (globalParams->getScreenType()) {
1004   case screenDispersed:
1005     screenParams.type = splashScreenDispersed;
1006     if (screenParams.size < 0) {
1007       screenParams.size = 4;
1008     }
1009     break;
1010   case screenClustered:
1011     screenParams.type = splashScreenClustered;
1012     if (screenParams.size < 0) {
1013       screenParams.size = 10;
1014     }
1015     break;
1016   case screenStochasticClustered:
1017     screenParams.type = splashScreenStochasticClustered;
1018     if (screenParams.size < 0) {
1019       screenParams.size = 100;
1020     }
1021     if (screenParams.dotRadius < 0) {
1022       screenParams.dotRadius = 2;
1023     }
1024     break;
1025   case screenUnset:
1026   default:
1027     // use clustered dithering for resolution >= 300 dpi
1028     // (compare to 299.9 to avoid floating point issues)
1029     if (hDPI > 299.9 && vDPI > 299.9) {
1030       screenParams.type = splashScreenStochasticClustered;
1031       if (screenParams.size < 0) {
1032 	screenParams.size = 100;
1033       }
1034       if (screenParams.dotRadius < 0) {
1035 	screenParams.dotRadius = 2;
1036       }
1037     } else {
1038       screenParams.type = splashScreenDispersed;
1039       if (screenParams.size < 0) {
1040 	screenParams.size = 4;
1041       }
1042     }
1043   }
1044 }
1045 
~SplashOutputDev()1046 SplashOutputDev::~SplashOutputDev() {
1047   int i;
1048 
1049   for (i = 0; i < nT3Fonts; ++i) {
1050     delete t3FontCache[i];
1051   }
1052   if (fontEngine) {
1053     delete fontEngine;
1054   }
1055   if (splash) {
1056     delete splash;
1057   }
1058   if (bitmap) {
1059     delete bitmap;
1060   }
1061 }
1062 
startDoc(XRef * xrefA)1063 void SplashOutputDev::startDoc(XRef *xrefA) {
1064   int i;
1065 
1066   xref = xrefA;
1067   if (fontEngine) {
1068     delete fontEngine;
1069   }
1070   fontEngine = new SplashFontEngine(
1071 #if HAVE_T1LIB_H
1072 				    globalParams->getEnableT1lib(),
1073 #endif
1074 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
1075 				    globalParams->getEnableFreeType(),
1076 				    enableFreeTypeHinting,
1077 #endif
1078 				    allowAntialias &&
1079 				      globalParams->getAntialias() &&
1080 				      colorMode != splashModeMono1);
1081   for (i = 0; i < nT3Fonts; ++i) {
1082     delete t3FontCache[i];
1083   }
1084   nT3Fonts = 0;
1085 }
1086 
startPage(int pageNum,GfxState * state)1087 void SplashOutputDev::startPage(int pageNum, GfxState *state) {
1088   int w, h;
1089   double *ctm;
1090   SplashCoord mat[6];
1091   SplashColor color;
1092 
1093   if (state) {
1094     setupScreenParams(state->getHDPI(), state->getVDPI());
1095     w = (int)(state->getPageWidth() + 0.5);
1096     if (w <= 0) {
1097       w = 1;
1098     }
1099     h = (int)(state->getPageHeight() + 0.5);
1100     if (h <= 0) {
1101       h = 1;
1102     }
1103   } else {
1104     w = h = 1;
1105   }
1106   if (splash) {
1107     delete splash;
1108   }
1109   if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
1110     if (bitmap) {
1111       delete bitmap;
1112     }
1113     bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1114 			      colorMode != splashModeMono1, bitmapTopDown);
1115   }
1116   splash = new Splash(bitmap, vectorAntialias, &screenParams);
1117   if (state) {
1118     ctm = state->getCTM();
1119     mat[0] = (SplashCoord)ctm[0];
1120     mat[1] = (SplashCoord)ctm[1];
1121     mat[2] = (SplashCoord)ctm[2];
1122     mat[3] = (SplashCoord)ctm[3];
1123     mat[4] = (SplashCoord)ctm[4];
1124     mat[5] = (SplashCoord)ctm[5];
1125     splash->setMatrix(mat);
1126   }
1127   switch (colorMode) {
1128   case splashModeMono1:
1129   case splashModeMono8:
1130     color[0] = 0;
1131     break;
1132   case splashModeXBGR8:
1133     color[3] = 255;
1134   case splashModeRGB8:
1135   case splashModeBGR8:
1136     color[0] = color[1] = color[2] = 0;
1137     break;
1138 #if SPLASH_CMYK
1139   case splashModeCMYK8:
1140     color[0] = color[1] = color[2] = color[3] = 0;
1141     break;
1142 #endif
1143   }
1144   splash->setStrokePattern(new SplashSolidColor(color));
1145   splash->setFillPattern(new SplashSolidColor(color));
1146   splash->setLineCap(splashLineCapButt);
1147   splash->setLineJoin(splashLineJoinMiter);
1148   splash->setLineDash(NULL, 0, 0);
1149   splash->setMiterLimit(10);
1150   splash->setFlatness(1);
1151   // the SA parameter supposedly defaults to false, but Acrobat
1152   // apparently hardwires it to true
1153   splash->setStrokeAdjust(globalParams->getStrokeAdjust());
1154   splash->clear(paperColor, 0);
1155 }
1156 
endPage()1157 void SplashOutputDev::endPage() {
1158   if (colorMode != splashModeMono1 && !keepAlphaChannel) {
1159     splash->compositeBackground(paperColor);
1160   }
1161 }
1162 
saveState(GfxState * state)1163 void SplashOutputDev::saveState(GfxState *state) {
1164   splash->saveState();
1165 }
1166 
restoreState(GfxState * state)1167 void SplashOutputDev::restoreState(GfxState *state) {
1168   splash->restoreState();
1169   needFontUpdate = gTrue;
1170 }
1171 
updateAll(GfxState * state)1172 void SplashOutputDev::updateAll(GfxState *state) {
1173   updateLineDash(state);
1174   updateLineJoin(state);
1175   updateLineCap(state);
1176   updateLineWidth(state);
1177   updateFlatness(state);
1178   updateMiterLimit(state);
1179   updateStrokeAdjust(state);
1180   updateFillColor(state);
1181   updateStrokeColor(state);
1182   needFontUpdate = gTrue;
1183 }
1184 
updateCTM(GfxState * state,double m11,double m12,double m21,double m22,double m31,double m32)1185 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
1186 				double m21, double m22,
1187 				double m31, double m32) {
1188   double *ctm;
1189   SplashCoord mat[6];
1190 
1191   ctm = state->getCTM();
1192   mat[0] = (SplashCoord)ctm[0];
1193   mat[1] = (SplashCoord)ctm[1];
1194   mat[2] = (SplashCoord)ctm[2];
1195   mat[3] = (SplashCoord)ctm[3];
1196   mat[4] = (SplashCoord)ctm[4];
1197   mat[5] = (SplashCoord)ctm[5];
1198   splash->setMatrix(mat);
1199 }
1200 
updateLineDash(GfxState * state)1201 void SplashOutputDev::updateLineDash(GfxState *state) {
1202   double *dashPattern;
1203   int dashLength;
1204   double dashStart;
1205   SplashCoord dash[20];
1206   int i;
1207 
1208   state->getLineDash(&dashPattern, &dashLength, &dashStart);
1209   if (dashLength > 20) {
1210     dashLength = 20;
1211   }
1212   for (i = 0; i < dashLength; ++i) {
1213     dash[i] = (SplashCoord)dashPattern[i];
1214     if (dash[i] < 0) {
1215       dash[i] = 0;
1216     }
1217   }
1218   splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
1219 }
1220 
updateFlatness(GfxState * state)1221 void SplashOutputDev::updateFlatness(GfxState *state) {
1222   splash->setFlatness(state->getFlatness());
1223 }
1224 
updateLineJoin(GfxState * state)1225 void SplashOutputDev::updateLineJoin(GfxState *state) {
1226   splash->setLineJoin(state->getLineJoin());
1227 }
1228 
updateLineCap(GfxState * state)1229 void SplashOutputDev::updateLineCap(GfxState *state) {
1230   splash->setLineCap(state->getLineCap());
1231 }
1232 
updateMiterLimit(GfxState * state)1233 void SplashOutputDev::updateMiterLimit(GfxState *state) {
1234   splash->setMiterLimit(state->getMiterLimit());
1235 }
1236 
updateLineWidth(GfxState * state)1237 void SplashOutputDev::updateLineWidth(GfxState *state) {
1238   splash->setLineWidth(state->getLineWidth());
1239 }
1240 
updateStrokeAdjust(GfxState *)1241 void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) {
1242 #if 0 // the SA parameter supposedly defaults to false, but Acrobat
1243       // apparently hardwires it to true
1244   splash->setStrokeAdjust(state->getStrokeAdjust());
1245 #endif
1246 }
1247 
updateFillColor(GfxState * state)1248 void SplashOutputDev::updateFillColor(GfxState *state) {
1249   GfxGray gray;
1250   GfxRGB rgb;
1251 #if SPLASH_CMYK
1252   GfxCMYK cmyk;
1253 #endif
1254 
1255   state->getFillGray(&gray);
1256   state->getFillRGB(&rgb);
1257 #if SPLASH_CMYK
1258   state->getFillCMYK(&cmyk);
1259   splash->setFillPattern(getColor(gray, &rgb, &cmyk));
1260 #else
1261   splash->setFillPattern(getColor(gray, &rgb));
1262 #endif
1263 }
1264 
updateStrokeColor(GfxState * state)1265 void SplashOutputDev::updateStrokeColor(GfxState *state) {
1266   GfxGray gray;
1267   GfxRGB rgb;
1268 #if SPLASH_CMYK
1269   GfxCMYK cmyk;
1270 #endif
1271 
1272   state->getStrokeGray(&gray);
1273   state->getStrokeRGB(&rgb);
1274 #if SPLASH_CMYK
1275   state->getStrokeCMYK(&cmyk);
1276   splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
1277 #else
1278   splash->setStrokePattern(getColor(gray, &rgb));
1279 #endif
1280 }
1281 
1282 #if SPLASH_CMYK
getColor(GfxGray gray,GfxRGB * rgb,GfxCMYK * cmyk)1283 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,
1284 					 GfxCMYK *cmyk) {
1285 #else
1286 SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
1287 #endif
1288   SplashPattern *pattern;
1289   SplashColor color;
1290   GfxColorComp r, g, b;
1291 
1292   if (reverseVideo) {
1293     gray = gfxColorComp1 - gray;
1294     r = gfxColorComp1 - rgb->r;
1295     g = gfxColorComp1 - rgb->g;
1296     b = gfxColorComp1 - rgb->b;
1297   } else {
1298     r = rgb->r;
1299     g = rgb->g;
1300     b = rgb->b;
1301   }
1302 
1303   pattern = NULL; // make gcc happy
1304   switch (colorMode) {
1305   case splashModeMono1:
1306   case splashModeMono8:
1307     color[0] = colToByte(gray);
1308     pattern = new SplashSolidColor(color);
1309     break;
1310   case splashModeXBGR8:
1311     color[3] = 255;
1312   case splashModeRGB8:
1313   case splashModeBGR8:
1314     color[0] = colToByte(r);
1315     color[1] = colToByte(g);
1316     color[2] = colToByte(b);
1317     pattern = new SplashSolidColor(color);
1318     break;
1319 #if SPLASH_CMYK
1320   case splashModeCMYK8:
1321     color[0] = colToByte(cmyk->c);
1322     color[1] = colToByte(cmyk->m);
1323     color[2] = colToByte(cmyk->y);
1324     color[3] = colToByte(cmyk->k);
1325     pattern = new SplashSolidColor(color);
1326     break;
1327 #endif
1328   }
1329 
1330   return pattern;
1331 }
1332 
1333 void SplashOutputDev::updateBlendMode(GfxState *state) {
1334   splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1335 }
1336 
1337 void SplashOutputDev::updateFillOpacity(GfxState *state) {
1338   splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1339 }
1340 
1341 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1342   splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1343 }
1344 
1345 void SplashOutputDev::updateFont(GfxState * /*state*/) {
1346   needFontUpdate = gTrue;
1347 }
1348 
1349 void SplashOutputDev::doUpdateFont(GfxState *state) {
1350   GfxFont *gfxFont;
1351   GfxFontType fontType;
1352   SplashOutFontFileID *id;
1353   SplashFontFile *fontFile;
1354   SplashFontSrc *fontsrc = NULL;
1355   FoFiTrueType *ff;
1356   Ref embRef;
1357   Object refObj, strObj;
1358   GooString *fileName;
1359   char *tmpBuf;
1360   int tmpBufLen;
1361   Gushort *codeToGID;
1362   DisplayFontParam *dfp;
1363   double *textMat;
1364   double m11, m12, m21, m22, fontSize;
1365   SplashCoord mat[4];
1366   int substIdx, n;
1367   int faceIndex = 0;
1368   GBool recreateFont = gFalse;
1369   GBool doAdjustFontMatrix = gFalse;
1370 
1371   needFontUpdate = gFalse;
1372   font = NULL;
1373   fileName = NULL;
1374   tmpBuf = NULL;
1375   substIdx = -1;
1376   dfp = NULL;
1377 
1378   if (!(gfxFont = state->getFont())) {
1379     goto err1;
1380   }
1381   fontType = gfxFont->getType();
1382   if (fontType == fontType3) {
1383     goto err1;
1384   }
1385 
1386   // check the font file cache
1387   id = new SplashOutFontFileID(gfxFont->getID());
1388   if ((fontFile = fontEngine->getFontFile(id))) {
1389     delete id;
1390 
1391   } else {
1392 
1393     // if there is an embedded font, write it to disk
1394     if (gfxFont->getEmbeddedFontID(&embRef)) {
1395       tmpBuf = gfxFont->readEmbFontFile(xref, &tmpBufLen);
1396       if (! tmpBuf)
1397 	goto err2;
1398 
1399     // if there is an external font file, use it
1400     } else if (!(fileName = gfxFont->getExtFontFile())) {
1401 
1402       // look for a display font mapping or a substitute font
1403       dfp = NULL;
1404       if (gfxFont->getName()) {
1405         dfp = globalParams->getDisplayFont(gfxFont);
1406       }
1407       if (!dfp) {
1408 	error(-1, "Couldn't find a font for '%s'",
1409 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1410 	                         : "(unnamed)");
1411 	goto err2;
1412       }
1413       switch (dfp->kind) {
1414       case displayFontT1:
1415 	fileName = dfp->t1.fileName;
1416 	fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
1417 	break;
1418       case displayFontTT:
1419 	fileName = dfp->tt.fileName;
1420 	fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
1421 	faceIndex = dfp->tt.faceIndex;
1422 	break;
1423       }
1424       doAdjustFontMatrix = gTrue;
1425     }
1426 
1427     fontsrc = new SplashFontSrc;
1428     if (fileName)
1429       fontsrc->setFile(fileName, gFalse);
1430     else
1431       fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue);
1432 
1433     // load the font file
1434     switch (fontType) {
1435     case fontType1:
1436       if (!(fontFile = fontEngine->loadType1Font(
1437 			   id,
1438 			   fontsrc,
1439 			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1440 	error(-1, "Couldn't create a font for '%s'",
1441 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1442 	                         : "(unnamed)");
1443 	goto err2;
1444       }
1445       break;
1446     case fontType1C:
1447       if (!(fontFile = fontEngine->loadType1CFont(
1448 			   id,
1449 			   fontsrc,
1450 			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1451 	error(-1, "Couldn't create a font for '%s'",
1452 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1453 	                         : "(unnamed)");
1454 	goto err2;
1455       }
1456       break;
1457     case fontType1COT:
1458       if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
1459 			   id,
1460 			   fontsrc,
1461 			   ((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1462 	error(-1, "Couldn't create a font for '%s'",
1463 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1464 	                         : "(unnamed)");
1465 	goto err2;
1466       }
1467       break;
1468     case fontTrueType:
1469     case fontTrueTypeOT:
1470 	if (fileName)
1471 	 ff = FoFiTrueType::load(fileName->getCString());
1472 	else
1473 	ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
1474       if (ff) {
1475 	codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1476 	n = 256;
1477 	delete ff;
1478       } else {
1479 	codeToGID = NULL;
1480 	n = 0;
1481       }
1482       if (!(fontFile = fontEngine->loadTrueTypeFont(
1483 			   id,
1484 			   fontsrc,
1485 			   codeToGID, n))) {
1486 	error(-1, "Couldn't create a font for '%s'",
1487 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1488 	                         : "(unnamed)");
1489 	goto err2;
1490       }
1491       break;
1492     case fontCIDType0:
1493     case fontCIDType0C:
1494       if (!(fontFile = fontEngine->loadCIDFont(
1495 			   id,
1496 			   fontsrc))) {
1497 	error(-1, "Couldn't create a font for '%s'",
1498 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1499 	                         : "(unnamed)");
1500 	goto err2;
1501       }
1502       break;
1503     case fontCIDType0COT:
1504       if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
1505 			   id,
1506 			   fontsrc))) {
1507 	error(-1, "Couldn't create a font for '%s'",
1508 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1509 	                         : "(unnamed)");
1510 	goto err2;
1511       }
1512       break;
1513     case fontCIDType2:
1514     case fontCIDType2OT:
1515       codeToGID = NULL;
1516       n = 0;
1517       if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1518 	n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1519 	if (n) {
1520 	  codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1521 	  memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1522 		  n * sizeof(Gushort));
1523 	}
1524       } else {
1525 	if (fileName)
1526 	  ff = FoFiTrueType::load(fileName->getCString());
1527 	else
1528 	  ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
1529 	if (! ff)
1530 	{
1531 	  error(-1, "Couldn't create a font for '%s'",
1532 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1533 	                         : "(unnamed)");
1534 	  goto err2;
1535 	}
1536 	codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
1537 	delete ff;
1538       }
1539       if (!(fontFile = fontEngine->loadTrueTypeFont(
1540 			   id,
1541 			   fontsrc,
1542 			   codeToGID, n, faceIndex))) {
1543 	error(-1, "Couldn't create a font for '%s'",
1544 	      gfxFont->getName() ? gfxFont->getName()->getCString()
1545 	                         : "(unnamed)");
1546 	goto err2;
1547       }
1548       break;
1549     default:
1550       // this shouldn't happen
1551       goto err2;
1552     }
1553     fontFile->doAdjustMatrix = doAdjustFontMatrix;
1554   }
1555 
1556   // get the font matrix
1557   textMat = state->getTextMat();
1558   fontSize = state->getFontSize();
1559   m11 = textMat[0] * fontSize * state->getHorizScaling();
1560   m12 = textMat[1] * fontSize * state->getHorizScaling();
1561   m21 = textMat[2] * fontSize;
1562   m22 = textMat[3] * fontSize;
1563 
1564   // create the scaled font
1565   mat[0] = m11;  mat[1] = m12;
1566   mat[2] = m21;  mat[3] = m22;
1567   font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1568 
1569   // for substituted fonts: adjust the font matrix -- compare the
1570   // width of 'm' in the original font and the substituted font
1571   if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) {
1572     double w1, w2;
1573     CharCode code;
1574     char *name;
1575     for (code = 0; code < 256; ++code) {
1576       if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1577           name[0] == 'm' && name[1] == '\0') {
1578         break;
1579       }
1580     }
1581     if (code < 256) {
1582       w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1583       w2 = font->getGlyphAdvance(code);
1584       if (!gfxFont->isSymbolic() && w2 > 0) {
1585         // if real font is substantially narrower than substituted
1586         // font, reduce the font size accordingly
1587         if (w1 > 0.01 && w1 < 0.9 * w2) {
1588           w1 /= w2;
1589           m11 *= w1;
1590           m21 *= w1;
1591           recreateFont = gTrue;
1592         }
1593       }
1594     }
1595   }
1596 
1597   if (recreateFont)
1598   {
1599     mat[0] = m11;  mat[1] = m12;
1600     mat[2] = m21;  mat[3] = m22;
1601     font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1602   }
1603 
1604   if (fontsrc && !fontsrc->isFile)
1605       fontsrc->unref();
1606   return;
1607 
1608  err2:
1609   delete id;
1610  err1:
1611   if (fontsrc && !fontsrc->isFile)
1612       fontsrc->unref();
1613   return;
1614 }
1615 
1616 void SplashOutputDev::stroke(GfxState *state) {
1617   SplashPath *path;
1618 
1619   if (state->getStrokeColorSpace()->isNonMarking()) {
1620     return;
1621   }
1622   path = convertPath(state, state->getPath());
1623   splash->stroke(path);
1624   delete path;
1625 }
1626 
1627 void SplashOutputDev::fill(GfxState *state) {
1628   SplashPath *path;
1629 
1630   if (state->getFillColorSpace()->isNonMarking()) {
1631     return;
1632   }
1633   path = convertPath(state, state->getPath());
1634   splash->fill(path, gFalse);
1635   delete path;
1636 }
1637 
1638 void SplashOutputDev::eoFill(GfxState *state) {
1639   SplashPath *path;
1640 
1641   if (state->getFillColorSpace()->isNonMarking()) {
1642     return;
1643   }
1644   path = convertPath(state, state->getPath());
1645   splash->fill(path, gTrue);
1646   delete path;
1647 }
1648 
1649 void SplashOutputDev::clip(GfxState *state) {
1650   SplashPath *path;
1651 
1652   path = convertPath(state, state->getPath());
1653   splash->clipToPath(path, gFalse);
1654   delete path;
1655 }
1656 
1657 void SplashOutputDev::eoClip(GfxState *state) {
1658   SplashPath *path;
1659 
1660   path = convertPath(state, state->getPath());
1661   splash->clipToPath(path, gTrue);
1662   delete path;
1663 }
1664 
1665 void SplashOutputDev::clipToStrokePath(GfxState *state) {
1666   SplashPath *path, *path2;
1667 
1668   path = convertPath(state, state->getPath());
1669   path2 = splash->makeStrokePath(path);
1670   delete path;
1671   splash->clipToPath(path2, gFalse);
1672   delete path2;
1673 }
1674 
1675 SplashPath *SplashOutputDev::convertPath(GfxState * /*state*/, GfxPath *path) {
1676   SplashPath *sPath;
1677   GfxSubpath *subpath;
1678   int i, j;
1679 
1680   sPath = new SplashPath();
1681   for (i = 0; i < path->getNumSubpaths(); ++i) {
1682     subpath = path->getSubpath(i);
1683     if (subpath->getNumPoints() > 0) {
1684       sPath->moveTo((SplashCoord)subpath->getX(0),
1685 		    (SplashCoord)subpath->getY(0));
1686       j = 1;
1687       while (j < subpath->getNumPoints()) {
1688 	if (subpath->getCurve(j)) {
1689 	  sPath->curveTo((SplashCoord)subpath->getX(j),
1690 			 (SplashCoord)subpath->getY(j),
1691 			 (SplashCoord)subpath->getX(j+1),
1692 			 (SplashCoord)subpath->getY(j+1),
1693 			 (SplashCoord)subpath->getX(j+2),
1694 			 (SplashCoord)subpath->getY(j+2));
1695 	  j += 3;
1696 	} else {
1697 	  sPath->lineTo((SplashCoord)subpath->getX(j),
1698 			(SplashCoord)subpath->getY(j));
1699 	  ++j;
1700 	}
1701       }
1702       if (subpath->isClosed()) {
1703 	sPath->close();
1704       }
1705     }
1706   }
1707   return sPath;
1708 }
1709 
1710 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1711 			       double dx, double dy,
1712 			       double originX, double originY,
1713 			       CharCode code, int nBytes,
1714 			       Unicode *u, int uLen) {
1715   SplashPath *path;
1716   int render;
1717 
1718   // check for invisible text -- this is used by Acrobat Capture
1719   render = state->getRender();
1720   if (render == 3) {
1721     return;
1722   }
1723 
1724   if (needFontUpdate) {
1725     doUpdateFont(state);
1726   }
1727   if (!font) {
1728     return;
1729   }
1730 
1731   x -= originX;
1732   y -= originY;
1733 
1734   // fill
1735   if (!(render & 1)) {
1736     if (!haveCSPattern && !state->getFillColorSpace()->isNonMarking()) {
1737       splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
1738     }
1739   }
1740 
1741   // stroke
1742   if ((render & 3) == 1 || (render & 3) == 2) {
1743     if (!state->getStrokeColorSpace()->isNonMarking()) {
1744       if ((path = font->getGlyphPath(code))) {
1745 	path->offset((SplashCoord)x, (SplashCoord)y);
1746 	splash->stroke(path);
1747 	delete path;
1748       }
1749     }
1750   }
1751 
1752   // clip
1753   if (haveCSPattern || (render & 4)) {
1754     if ((path = font->getGlyphPath(code))) {
1755       path->offset((SplashCoord)x, (SplashCoord)y);
1756       if (textClipPath) {
1757 	textClipPath->append(path);
1758 	delete path;
1759       } else {
1760 	textClipPath = path;
1761       }
1762     }
1763   }
1764 }
1765 
1766 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1767 				      double dx, double dy,
1768 				      CharCode code, Unicode *u, int uLen) {
1769   GfxFont *gfxFont;
1770   Ref *fontID;
1771   double *ctm, *bbox;
1772   T3FontCache *t3Font;
1773   T3GlyphStack *t3gs;
1774   GBool validBBox;
1775   double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1776   int i, j;
1777 
1778   if (!(gfxFont = state->getFont())) {
1779     return gFalse;
1780   }
1781   fontID = gfxFont->getID();
1782   ctm = state->getCTM();
1783   state->transform(0, 0, &xt, &yt);
1784 
1785   // is it the first (MRU) font in the cache?
1786   if (!(nT3Fonts > 0 &&
1787 	t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1788 
1789     // is the font elsewhere in the cache?
1790     for (i = 1; i < nT3Fonts; ++i) {
1791       if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1792 	t3Font = t3FontCache[i];
1793 	for (j = i; j > 0; --j) {
1794 	  t3FontCache[j] = t3FontCache[j - 1];
1795 	}
1796 	t3FontCache[0] = t3Font;
1797 	break;
1798       }
1799     }
1800     if (i >= nT3Fonts) {
1801 
1802       // create new entry in the font cache
1803       if (nT3Fonts == splashOutT3FontCacheSize) {
1804 	delete t3FontCache[nT3Fonts - 1];
1805 	--nT3Fonts;
1806       }
1807       for (j = nT3Fonts; j > 0; --j) {
1808 	t3FontCache[j] = t3FontCache[j - 1];
1809       }
1810       ++nT3Fonts;
1811       bbox = gfxFont->getFontBBox();
1812       if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1813 	// unspecified bounding box -- just take a guess
1814 	xMin = xt - 5;
1815 	xMax = xMin + 30;
1816 	yMax = yt + 15;
1817 	yMin = yMax - 45;
1818 	validBBox = gFalse;
1819       } else {
1820 	state->transform(bbox[0], bbox[1], &x1, &y1);
1821 	xMin = xMax = x1;
1822 	yMin = yMax = y1;
1823 	state->transform(bbox[0], bbox[3], &x1, &y1);
1824 	if (x1 < xMin) {
1825 	  xMin = x1;
1826 	} else if (x1 > xMax) {
1827 	  xMax = x1;
1828 	}
1829 	if (y1 < yMin) {
1830 	  yMin = y1;
1831 	} else if (y1 > yMax) {
1832 	  yMax = y1;
1833 	}
1834 	state->transform(bbox[2], bbox[1], &x1, &y1);
1835 	if (x1 < xMin) {
1836 	  xMin = x1;
1837 	} else if (x1 > xMax) {
1838 	  xMax = x1;
1839 	}
1840 	if (y1 < yMin) {
1841 	  yMin = y1;
1842 	} else if (y1 > yMax) {
1843 	  yMax = y1;
1844 	}
1845 	state->transform(bbox[2], bbox[3], &x1, &y1);
1846 	if (x1 < xMin) {
1847 	  xMin = x1;
1848 	} else if (x1 > xMax) {
1849 	  xMax = x1;
1850 	}
1851 	if (y1 < yMin) {
1852 	  yMin = y1;
1853 	} else if (y1 > yMax) {
1854 	  yMax = y1;
1855 	}
1856 	validBBox = gTrue;
1857       }
1858       t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
1859 	                               (int)floor(xMin - xt),
1860 				       (int)floor(yMin - yt),
1861 				       (int)ceil(xMax) - (int)floor(xMin) + 3,
1862 				       (int)ceil(yMax) - (int)floor(yMin) + 3,
1863 				       validBBox,
1864 				       colorMode != splashModeMono1);
1865     }
1866   }
1867   t3Font = t3FontCache[0];
1868 
1869   // is the glyph in the cache?
1870   i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1871   for (j = 0; j < t3Font->cacheAssoc; ++j) {
1872     if (t3Font->cacheTags != NULL) {
1873       if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
1874 	t3Font->cacheTags[i+j].code == code) {
1875         drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
1876 		     t3Font->cacheData + (i+j) * t3Font->glyphSize);
1877         return gTrue;
1878       }
1879     }
1880   }
1881 
1882   // push a new Type 3 glyph record
1883   t3gs = new T3GlyphStack();
1884   t3gs->next = t3GlyphStack;
1885   t3GlyphStack = t3gs;
1886   t3GlyphStack->code = code;
1887   t3GlyphStack->cache = t3Font;
1888   t3GlyphStack->cacheTag = NULL;
1889   t3GlyphStack->cacheData = NULL;
1890 
1891   return gFalse;
1892 }
1893 
1894 void SplashOutputDev::endType3Char(GfxState *state) {
1895   T3GlyphStack *t3gs;
1896   double *ctm;
1897 
1898   if (t3GlyphStack->cacheTag) {
1899     memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
1900 	   t3GlyphStack->cache->glyphSize);
1901     delete bitmap;
1902     delete splash;
1903     bitmap = t3GlyphStack->origBitmap;
1904     splash = t3GlyphStack->origSplash;
1905     ctm = state->getCTM();
1906     state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1907 		  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
1908     updateCTM(state, 0, 0, 0, 0, 0, 0);
1909     drawType3Glyph(t3GlyphStack->cache,
1910 		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
1911   }
1912   t3gs = t3GlyphStack;
1913   t3GlyphStack = t3gs->next;
1914   delete t3gs;
1915 }
1916 
1917 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1918 }
1919 
1920 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1921 			      double llx, double lly, double urx, double ury) {
1922   double *ctm;
1923   T3FontCache *t3Font;
1924   SplashColor color;
1925   double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1926   int i, j;
1927 
1928   if (unlikely(t3GlyphStack == NULL)) {
1929     error(-1, "t3GlyphStack was null in SplashOutputDev::type3D1");
1930     return;
1931   }
1932 
1933   if (unlikely(t3GlyphStack->origBitmap != NULL)) {
1934     error(-1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1");
1935     return;
1936   }
1937 
1938   if (unlikely(t3GlyphStack->origSplash != NULL)) {
1939     error(-1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1");
1940     return;
1941   }
1942 
1943   t3Font = t3GlyphStack->cache;
1944 
1945   // check for a valid bbox
1946   state->transform(0, 0, &xt, &yt);
1947   state->transform(llx, lly, &x1, &y1);
1948   xMin = xMax = x1;
1949   yMin = yMax = y1;
1950   state->transform(llx, ury, &x1, &y1);
1951   if (x1 < xMin) {
1952     xMin = x1;
1953   } else if (x1 > xMax) {
1954     xMax = x1;
1955   }
1956   if (y1 < yMin) {
1957     yMin = y1;
1958   } else if (y1 > yMax) {
1959     yMax = y1;
1960   }
1961   state->transform(urx, lly, &x1, &y1);
1962   if (x1 < xMin) {
1963     xMin = x1;
1964   } else if (x1 > xMax) {
1965     xMax = x1;
1966   }
1967   if (y1 < yMin) {
1968     yMin = y1;
1969   } else if (y1 > yMax) {
1970     yMax = y1;
1971   }
1972   state->transform(urx, ury, &x1, &y1);
1973   if (x1 < xMin) {
1974     xMin = x1;
1975   } else if (x1 > xMax) {
1976     xMax = x1;
1977   }
1978   if (y1 < yMin) {
1979     yMin = y1;
1980   } else if (y1 > yMax) {
1981     yMax = y1;
1982   }
1983   if (xMin - xt < t3Font->glyphX ||
1984       yMin - yt < t3Font->glyphY ||
1985       xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1986       yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1987     if (t3Font->validBBox) {
1988       error(-1, "Bad bounding box in Type 3 glyph");
1989     }
1990     return;
1991   }
1992 
1993   if (t3Font->cacheTags == NULL)
1994     return;
1995 
1996   // allocate a cache entry
1997   i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1998   for (j = 0; j < t3Font->cacheAssoc; ++j) {
1999     if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2000       t3Font->cacheTags[i+j].mru = 0x8000;
2001       t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2002       t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2003       t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2004     } else {
2005       ++t3Font->cacheTags[i+j].mru;
2006     }
2007   }
2008 
2009   // save state
2010   t3GlyphStack->origBitmap = bitmap;
2011   t3GlyphStack->origSplash = splash;
2012   ctm = state->getCTM();
2013   t3GlyphStack->origCTM4 = ctm[4];
2014   t3GlyphStack->origCTM5 = ctm[5];
2015 
2016   // create the temporary bitmap
2017   if (colorMode == splashModeMono1) {
2018     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2019 			      splashModeMono1, gFalse);
2020     splash = new Splash(bitmap, gFalse,
2021 			t3GlyphStack->origSplash->getScreen());
2022     color[0] = 0;
2023     splash->clear(color);
2024     color[0] = 1;
2025   } else {
2026     bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2027 			      splashModeMono8, gFalse);
2028     splash = new Splash(bitmap, vectorAntialias,
2029 			t3GlyphStack->origSplash->getScreen());
2030     color[0] = 0x00;
2031     splash->clear(color);
2032     color[0] = 0xff;
2033   }
2034   splash->setFillPattern(new SplashSolidColor(color));
2035   splash->setStrokePattern(new SplashSolidColor(color));
2036   //~ this should copy other state from t3GlyphStack->origSplash?
2037   state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2038 		-t3Font->glyphX, -t3Font->glyphY);
2039   updateCTM(state, 0, 0, 0, 0, 0, 0);
2040 }
2041 
2042 void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
2043 				     T3FontCacheTag * /*tag*/, Guchar *data) {
2044   SplashGlyphBitmap glyph;
2045 
2046   glyph.x = -t3Font->glyphX;
2047   glyph.y = -t3Font->glyphY;
2048   glyph.w = t3Font->glyphW;
2049   glyph.h = t3Font->glyphH;
2050   glyph.aa = colorMode != splashModeMono1;
2051   glyph.data = data;
2052   glyph.freeData = gFalse;
2053   splash->fillGlyph(0, 0, &glyph);
2054 }
2055 
2056 void SplashOutputDev::beginTextObject(GfxState *state) {
2057   if (!(state->getRender() & 4) && state->getFillColorSpace()->getMode() == csPattern) {
2058     haveCSPattern = gTrue;
2059     saveState(state);
2060   }
2061 }
2062 
2063 void SplashOutputDev::endTextObject(GfxState *state) {
2064   if (haveCSPattern) {
2065     haveCSPattern = gFalse;
2066     if (state->getFillColorSpace()->getMode() != csPattern) {
2067       if (textClipPath) {
2068         splash->fill(textClipPath, gTrue);
2069         delete textClipPath;
2070         textClipPath = NULL;
2071       }
2072       restoreState(state);
2073       updateFillColor(state);
2074     }
2075   }
2076   if (textClipPath) {
2077     splash->clipToPath(textClipPath, gFalse);
2078     delete textClipPath;
2079     textClipPath = NULL;
2080   }
2081 }
2082 
2083 struct SplashOutImageMaskData {
2084   ImageStream *imgStr;
2085   GBool invert;
2086   int width, height, y;
2087 };
2088 
2089 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
2090   SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2091   Guchar *p;
2092   SplashColorPtr q;
2093   int x;
2094 
2095   if (imgMaskData->y == imgMaskData->height) {
2096     return gFalse;
2097   }
2098   for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
2099        x < imgMaskData->width;
2100        ++x) {
2101     *q++ = *p++ ^ imgMaskData->invert;
2102   }
2103   ++imgMaskData->y;
2104   return gTrue;
2105 }
2106 
2107 void SplashOutputDev::endMaskClip(GfxState * state) {
2108   double bbox[4] = {0,0,1,1}; // dummy
2109   /* transfer mask to alpha channel! */
2110   // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
2111   // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
2112   int c;
2113   Guchar *dest = bitmap->getAlphaPtr();
2114   Guchar *src = maskBitmap->getDataPtr();
2115   for (c= 0; c < maskBitmap->getRowSize() * maskBitmap->getHeight(); c++) {
2116     dest[c] = src[c];
2117   }
2118   delete maskBitmap;
2119   maskBitmap = NULL;
2120   endTransparencyGroup(state);
2121   paintTransparencyGroup(state, bbox);
2122 }
2123 
2124 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2125 				    int width, int height, GBool invert,
2126 				    GBool interpolate, GBool inlineImg) {
2127   double *ctm;
2128   SplashCoord mat[6];
2129   SplashOutImageMaskData imgMaskData;
2130 
2131   if (state->getFillColorSpace()->isNonMarking()) {
2132     return;
2133   }
2134 
2135   ctm = state->getCTM();
2136   for (int i = 0; i < 6; ++i) {
2137     if (!isfinite(ctm[i])) return;
2138   }
2139   mat[0] = ctm[0];
2140   mat[1] = ctm[1];
2141   mat[2] = -ctm[2];
2142   mat[3] = -ctm[3];
2143   mat[4] = ctm[2] + ctm[4];
2144   mat[5] = ctm[3] + ctm[5];
2145 
2146   imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2147   imgMaskData.imgStr->reset();
2148   imgMaskData.invert = invert ? 0 : 1;
2149   imgMaskData.width = width;
2150   imgMaskData.height = height;
2151   imgMaskData.y = 0;
2152 
2153   if (state->getFillColorSpace()->getMode() == csPattern) {
2154     Splash *maskSplash;
2155     SplashColor maskColor;
2156 
2157     /* from beginTransparencyGroup: */
2158     // push a new stack entry
2159     SplashTransparencyGroup *transpGroup = new SplashTransparencyGroup();
2160     transpGroup->tx = 0;
2161     transpGroup->ty = 0;
2162     transpGroup->blendingColorSpace = NULL;
2163     transpGroup->isolated = gFalse;
2164     transpGroup->next = transpGroupStack;
2165     transpGroupStack = transpGroup;
2166     // save state
2167     transpGroup->origBitmap = bitmap;
2168     transpGroup->origSplash = splash;
2169     //~ this ignores the blendingColorSpace arg
2170     // create the temporary bitmap
2171     bitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), bitmapRowPad, colorMode, gTrue,
2172                               bitmapTopDown);
2173     splash = new Splash(bitmap, vectorAntialias,
2174                         transpGroup->origSplash->getScreen());
2175     splash->blitTransparent(transpGroup->origBitmap, 0, 0, 0, 0, bitmap->getWidth(), bitmap->getHeight());
2176     splash->setInNonIsolatedGroup(transpGroup->origBitmap, 0, 0);
2177     transpGroup->tBitmap = bitmap;
2178 
2179     maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
2180     maskSplash = new Splash(maskBitmap, vectorAntialias);
2181     maskColor[0] = 0;
2182     maskSplash->clear(maskColor);
2183     maskColor[0] = 0xff;
2184     maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2185     maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,  width, height, mat, t3GlyphStack != NULL);
2186     delete maskSplash;
2187   } else {
2188     splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2189     if (inlineImg) {
2190       while (imgMaskData.y < height) {
2191         imgMaskData.imgStr->getLine();
2192         ++imgMaskData.y;
2193       }
2194     }
2195   }
2196 
2197   delete imgMaskData.imgStr;
2198   str->close();
2199 }
2200 
2201 struct SplashOutImageData {
2202   ImageStream *imgStr;
2203   GfxImageColorMap *colorMap;
2204   SplashColorPtr lookup;
2205   int *maskColors;
2206   SplashColorMode colorMode;
2207   int width, height, y;
2208 };
2209 
2210 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2211 				Guchar * /*alphaLine*/) {
2212   SplashOutImageData *imgData = (SplashOutImageData *)data;
2213   Guchar *p;
2214   SplashColorPtr q, col;
2215   GfxGray gray;
2216 #if SPLASH_CMYK
2217   GfxCMYK cmyk;
2218 #endif
2219   int nComps, x;
2220 
2221   if (imgData->y == imgData->height) {
2222     return gFalse;
2223   }
2224 
2225   nComps = imgData->colorMap->getNumPixelComps();
2226 
2227   if (imgData->lookup) {
2228     switch (imgData->colorMode) {
2229   case splashModeMono1:
2230   case splashModeMono8:
2231       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2232 	   x < imgData->width;
2233 	   ++x, ++p) {
2234 	*q++ = imgData->lookup[*p];
2235       }
2236     break;
2237   case splashModeRGB8:
2238   case splashModeBGR8:
2239       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2240 	   x < imgData->width;
2241 	   ++x, ++p) {
2242 	col = &imgData->lookup[3 * *p];
2243 	*q++ = col[0];
2244 	*q++ = col[1];
2245 	*q++ = col[2];
2246       }
2247     break;
2248   case splashModeXBGR8:
2249       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2250 	   x < imgData->width;
2251 	   ++x, ++p) {
2252 	col = &imgData->lookup[4 * *p];
2253 	*q++ = col[0];
2254 	*q++ = col[1];
2255 	*q++ = col[2];
2256 	*q++ = col[3];
2257       }
2258       break;
2259 #if SPLASH_CMYK
2260     case splashModeCMYK8:
2261       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2262 	   x < imgData->width;
2263 	   ++x, ++p) {
2264 	col = &imgData->lookup[4 * *p];
2265 	*q++ = col[0];
2266 	*q++ = col[1];
2267 	*q++ = col[2];
2268 	*q++ = col[3];
2269       }
2270       break;
2271 #endif
2272   }
2273   } else {
2274     switch (imgData->colorMode) {
2275     case splashModeMono1:
2276     case splashModeMono8:
2277       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2278 	   x < imgData->width;
2279 	   ++x, p += nComps) {
2280 	imgData->colorMap->getGray(p, &gray);
2281 	*q++ = colToByte(gray);
2282       }
2283 	break;
2284 	case splashModeXBGR8:
2285     case splashModeRGB8:
2286     case splashModeBGR8:
2287       if (!imgData->colorMap->useRGBLine())
2288       {
2289         GfxRGB rgb;
2290         for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2291            x < imgData->width;
2292            ++x, p += nComps) {
2293           imgData->colorMap->getRGB(p, &rgb);
2294           *q++ = colToByte(rgb.r);
2295           *q++ = colToByte(rgb.g);
2296           *q++ = colToByte(rgb.b);
2297           if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
2298         }
2299       }
2300       else
2301       {
2302         p = imgData->imgStr->getLine();
2303         q = colorLine;
2304         unsigned int* line = (unsigned int *)gmallocn(imgData->width, sizeof(unsigned int));
2305 
2306         imgData->colorMap->getRGBLine(p, line, imgData->width);
2307         for (x = 0; x < imgData->width; ++x) {
2308             *q++ = (line[x] >> 16) & 255;
2309             *q++ = (line[x] >> 8) & 255;
2310             *q++ = (line[x]) & 255;
2311             if (imgData->colorMode == splashModeXBGR8) {
2312                 *q++ = 255;
2313             }
2314         }
2315         gfree(line);
2316       }
2317       break;
2318 #if SPLASH_CMYK
2319     case splashModeCMYK8:
2320       for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
2321 	   x < imgData->width;
2322 	   ++x, p += nComps) {
2323 	imgData->colorMap->getCMYK(p, &cmyk);
2324 	*q++ = colToByte(cmyk.c);
2325 	*q++ = colToByte(cmyk.m);
2326 	*q++ = colToByte(cmyk.y);
2327 	*q++ = colToByte(cmyk.k);
2328       }
2329       break;
2330 #endif
2331     }
2332   }
2333 
2334   ++imgData->y;
2335   return gTrue;
2336 }
2337 
2338 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
2339 				     Guchar *alphaLine) {
2340   SplashOutImageData *imgData = (SplashOutImageData *)data;
2341   Guchar *p, *aq;
2342   SplashColorPtr q, col;
2343   GfxRGB rgb;
2344   GfxGray gray;
2345 #if SPLASH_CMYK
2346   GfxCMYK cmyk;
2347 #endif
2348   Guchar alpha;
2349   int nComps, x, i;
2350 
2351   if (imgData->y == imgData->height) {
2352     return gFalse;
2353   }
2354 
2355   nComps = imgData->colorMap->getNumPixelComps();
2356 
2357   for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
2358        x < imgData->width;
2359        ++x, p += nComps) {
2360     alpha = 0;
2361     for (i = 0; i < nComps; ++i) {
2362       if (p[i] < imgData->maskColors[2*i] ||
2363 	  p[i] > imgData->maskColors[2*i+1]) {
2364 	alpha = 0xff;
2365 	break;
2366       }
2367     }
2368     if (imgData->lookup) {
2369       switch (imgData->colorMode) {
2370       case splashModeMono1:
2371       case splashModeMono8:
2372 	*q++ = imgData->lookup[*p];
2373 	*aq++ = alpha;
2374 	break;
2375       case splashModeRGB8:
2376       case splashModeBGR8:
2377 	col = &imgData->lookup[3 * *p];
2378 	*q++ = col[0];
2379 	*q++ = col[1];
2380 	*q++ = col[2];
2381 	*aq++ = alpha;
2382 	break;
2383       case splashModeXBGR8:
2384 	col = &imgData->lookup[4 * *p];
2385 	*q++ = col[0];
2386 	*q++ = col[1];
2387 	*q++ = col[2];
2388 	*q++ = 255;
2389 	*aq++ = alpha;
2390 	break;
2391 #if SPLASH_CMYK
2392       case splashModeCMYK8:
2393 	col = &imgData->lookup[4 * *p];
2394 	*q++ = col[0];
2395 	*q++ = col[1];
2396 	*q++ = col[2];
2397 	*q++ = col[3];
2398 	*aq++ = alpha;
2399 	break;
2400 #endif
2401       }
2402     } else {
2403       switch (imgData->colorMode) {
2404       case splashModeMono1:
2405       case splashModeMono8:
2406 	imgData->colorMap->getGray(p, &gray);
2407 	*q++ = colToByte(gray);
2408 	*aq++ = alpha;
2409 	break;
2410       case splashModeXBGR8:
2411       case splashModeRGB8:
2412       case splashModeBGR8:
2413 	imgData->colorMap->getRGB(p, &rgb);
2414 	*q++ = colToByte(rgb.r);
2415 	*q++ = colToByte(rgb.g);
2416 	*q++ = colToByte(rgb.b);
2417 	if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
2418 	*aq++ = alpha;
2419 	break;
2420 #if SPLASH_CMYK
2421       case splashModeCMYK8:
2422 	imgData->colorMap->getCMYK(p, &cmyk);
2423 	*q++ = colToByte(cmyk.c);
2424 	*q++ = colToByte(cmyk.m);
2425 	*q++ = colToByte(cmyk.y);
2426 	*q++ = colToByte(cmyk.k);
2427 	*aq++ = alpha;
2428 	break;
2429 #endif
2430       }
2431     }
2432   }
2433 
2434   ++imgData->y;
2435   return gTrue;
2436 }
2437 
2438 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2439 				int width, int height,
2440 				GfxImageColorMap *colorMap,
2441 				GBool interpolate,
2442 				int *maskColors, GBool inlineImg) {
2443   double *ctm;
2444   SplashCoord mat[6];
2445   SplashOutImageData imgData;
2446   SplashColorMode srcMode;
2447   SplashImageSource src;
2448   GfxGray gray;
2449   GfxRGB rgb;
2450 #if SPLASH_CMYK
2451   GfxCMYK cmyk;
2452 #endif
2453   Guchar pix;
2454   int n, i;
2455 
2456   ctm = state->getCTM();
2457   for (i = 0; i < 6; ++i) {
2458     if (!isfinite(ctm[i])) return;
2459   }
2460   mat[0] = ctm[0];
2461   mat[1] = ctm[1];
2462   mat[2] = -ctm[2];
2463   mat[3] = -ctm[3];
2464   mat[4] = ctm[2] + ctm[4];
2465   mat[5] = ctm[3] + ctm[5];
2466 
2467   imgData.imgStr = new ImageStream(str, width,
2468 				   colorMap->getNumPixelComps(),
2469 				   colorMap->getBits());
2470   imgData.imgStr->reset();
2471   imgData.colorMap = colorMap;
2472   imgData.maskColors = maskColors;
2473   imgData.colorMode = colorMode;
2474   imgData.width = width;
2475   imgData.height = height;
2476   imgData.y = 0;
2477 
2478   // special case for one-channel (monochrome/gray/separation) images:
2479   // build a lookup table here
2480   imgData.lookup = NULL;
2481   if (colorMap->getNumPixelComps() == 1) {
2482     n = 1 << colorMap->getBits();
2483     switch (colorMode) {
2484     case splashModeMono1:
2485     case splashModeMono8:
2486       imgData.lookup = (SplashColorPtr)gmalloc(n);
2487       for (i = 0; i < n; ++i) {
2488 	pix = (Guchar)i;
2489 	colorMap->getGray(&pix, &gray);
2490 	imgData.lookup[i] = colToByte(gray);
2491       }
2492       break;
2493     case splashModeRGB8:
2494     case splashModeBGR8:
2495       imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2496       for (i = 0; i < n; ++i) {
2497 	pix = (Guchar)i;
2498 	colorMap->getRGB(&pix, &rgb);
2499 	imgData.lookup[3*i] = colToByte(rgb.r);
2500 	imgData.lookup[3*i+1] = colToByte(rgb.g);
2501 	imgData.lookup[3*i+2] = colToByte(rgb.b);
2502       }
2503       break;
2504     case splashModeXBGR8:
2505       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2506       for (i = 0; i < n; ++i) {
2507 	pix = (Guchar)i;
2508 	colorMap->getRGB(&pix, &rgb);
2509 	imgData.lookup[4*i] = colToByte(rgb.r);
2510 	imgData.lookup[4*i+1] = colToByte(rgb.g);
2511 	imgData.lookup[4*i+2] = colToByte(rgb.b);
2512 	imgData.lookup[4*i+3] = 255;
2513       }
2514       break;
2515 #if SPLASH_CMYK
2516     case splashModeCMYK8:
2517       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2518       for (i = 0; i < n; ++i) {
2519 	pix = (Guchar)i;
2520 	colorMap->getCMYK(&pix, &cmyk);
2521 	imgData.lookup[4*i] = colToByte(cmyk.c);
2522 	imgData.lookup[4*i+1] = colToByte(cmyk.m);
2523 	imgData.lookup[4*i+2] = colToByte(cmyk.y);
2524 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
2525       }
2526       break;
2527 #endif
2528       break;
2529     }
2530   }
2531 
2532   if (colorMode == splashModeMono1) {
2533     srcMode = splashModeMono8;
2534   } else {
2535     srcMode = colorMode;
2536   }
2537   src = maskColors ? &alphaImageSrc : &imageSrc;
2538   splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
2539 		    width, height, mat);
2540   if (inlineImg) {
2541     while (imgData.y < height) {
2542       imgData.imgStr->getLine();
2543       ++imgData.y;
2544     }
2545   }
2546 
2547   gfree(imgData.lookup);
2548   delete imgData.imgStr;
2549   str->close();
2550 }
2551 
2552 struct SplashOutMaskedImageData {
2553   ImageStream *imgStr;
2554   GfxImageColorMap *colorMap;
2555   SplashBitmap *mask;
2556   SplashColorPtr lookup;
2557   SplashColorMode colorMode;
2558   int width, height, y;
2559 };
2560 
2561 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
2562 				      Guchar *alphaLine) {
2563   SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2564   Guchar *p, *aq;
2565   SplashColor maskColor;
2566   SplashColorPtr q, col;
2567   GfxRGB rgb;
2568   GfxGray gray;
2569 #if SPLASH_CMYK
2570   GfxCMYK cmyk;
2571 #endif
2572   Guchar alpha;
2573   int nComps, x;
2574 
2575   if (imgData->y == imgData->height) {
2576     return gFalse;
2577   }
2578 
2579   nComps = imgData->colorMap->getNumPixelComps();
2580 
2581   for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
2582        x < imgData->width;
2583        ++x, p += nComps) {
2584     imgData->mask->getPixel(x, imgData->y, maskColor);
2585     alpha = maskColor[0] ? 0xff : 0x00;
2586     if (imgData->lookup) {
2587       switch (imgData->colorMode) {
2588       case splashModeMono1:
2589       case splashModeMono8:
2590 	*q++ = imgData->lookup[*p];
2591 	*aq++ = alpha;
2592 	break;
2593       case splashModeRGB8:
2594       case splashModeBGR8:
2595 	col = &imgData->lookup[3 * *p];
2596 	*q++ = col[0];
2597 	*q++ = col[1];
2598 	*q++ = col[2];
2599 	*aq++ = alpha;
2600 	break;
2601       case splashModeXBGR8:
2602 	col = &imgData->lookup[4 * *p];
2603 	*q++ = col[0];
2604 	*q++ = col[1];
2605 	*q++ = col[2];
2606 	*q++ = 255;
2607 	*aq++ = alpha;
2608 	break;
2609 #if SPLASH_CMYK
2610       case splashModeCMYK8:
2611 	col = &imgData->lookup[4 * *p];
2612 	*q++ = col[0];
2613 	*q++ = col[1];
2614 	*q++ = col[2];
2615 	*q++ = col[3];
2616 	*aq++ = alpha;
2617 	break;
2618 #endif
2619       }
2620     } else {
2621       switch (imgData->colorMode) {
2622       case splashModeMono1:
2623       case splashModeMono8:
2624 	imgData->colorMap->getGray(p, &gray);
2625 	*q++ = colToByte(gray);
2626 	*aq++ = alpha;
2627 	break;
2628       case splashModeXBGR8:
2629       case splashModeRGB8:
2630       case splashModeBGR8:
2631 	imgData->colorMap->getRGB(p, &rgb);
2632 	*q++ = colToByte(rgb.r);
2633 	*q++ = colToByte(rgb.g);
2634 	*q++ = colToByte(rgb.b);
2635 	if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
2636 	*aq++ = alpha;
2637 	break;
2638 #if SPLASH_CMYK
2639       case splashModeCMYK8:
2640 	imgData->colorMap->getCMYK(p, &cmyk);
2641 	*q++ = colToByte(cmyk.c);
2642 	*q++ = colToByte(cmyk.m);
2643 	*q++ = colToByte(cmyk.y);
2644 	*q++ = colToByte(cmyk.k);
2645 	*aq++ = alpha;
2646 	break;
2647 #endif
2648       }
2649     }
2650   }
2651 
2652   ++imgData->y;
2653   return gTrue;
2654 }
2655 
2656 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2657 				      Stream *str, int width, int height,
2658 				      GfxImageColorMap *colorMap,
2659 				      GBool interpolate,
2660 				      Stream *maskStr, int maskWidth,
2661 				      int maskHeight, GBool maskInvert,
2662 				      GBool maskInterpolate) {
2663   GfxImageColorMap *maskColorMap;
2664   Object maskDecode, decodeLow, decodeHigh;
2665   double *ctm;
2666   SplashCoord mat[6];
2667   SplashOutMaskedImageData imgData;
2668   SplashOutImageMaskData imgMaskData;
2669   SplashColorMode srcMode;
2670   SplashBitmap *maskBitmap;
2671   Splash *maskSplash;
2672   SplashColor maskColor;
2673   GfxGray gray;
2674   GfxRGB rgb;
2675 #if SPLASH_CMYK
2676   GfxCMYK cmyk;
2677 #endif
2678   Guchar pix;
2679   int n, i;
2680 
2681   // If the mask is higher resolution than the image, use
2682   // drawSoftMaskedImage() instead.
2683   if (maskWidth > width || maskHeight > height) {
2684     decodeLow.initInt(maskInvert ? 0 : 1);
2685     decodeHigh.initInt(maskInvert ? 1 : 0);
2686     maskDecode.initArray(xref);
2687     maskDecode.arrayAdd(&decodeLow);
2688     maskDecode.arrayAdd(&decodeHigh);
2689     maskColorMap = new GfxImageColorMap(1, &maskDecode,
2690 					new GfxDeviceGrayColorSpace());
2691     maskDecode.free();
2692     drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
2693 			maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
2694     delete maskColorMap;
2695 
2696   } else {
2697 
2698     //----- scale the mask image to the same size as the source image
2699 
2700     mat[0] = (SplashCoord)width;
2701     mat[1] = 0;
2702     mat[2] = 0;
2703     mat[3] = (SplashCoord)height;
2704     mat[4] = 0;
2705     mat[5] = 0;
2706     imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2707     imgMaskData.imgStr->reset();
2708     imgMaskData.invert = maskInvert ? 0 : 1;
2709     imgMaskData.width = maskWidth;
2710     imgMaskData.height = maskHeight;
2711     imgMaskData.y = 0;
2712     maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
2713     maskSplash = new Splash(maskBitmap, gFalse);
2714     maskColor[0] = 0;
2715     maskSplash->clear(maskColor);
2716     maskColor[0] = 0xff;
2717     maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2718     maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2719 			      maskWidth, maskHeight, mat, gFalse);
2720     delete imgMaskData.imgStr;
2721     maskStr->close();
2722     delete maskSplash;
2723 
2724     //----- draw the source image
2725 
2726     ctm = state->getCTM();
2727     for (i = 0; i < 6; ++i) {
2728       if (!isfinite(ctm[i])) {
2729         delete maskBitmap;
2730         return;
2731       }
2732     }
2733     mat[0] = ctm[0];
2734     mat[1] = ctm[1];
2735     mat[2] = -ctm[2];
2736     mat[3] = -ctm[3];
2737     mat[4] = ctm[2] + ctm[4];
2738     mat[5] = ctm[3] + ctm[5];
2739 
2740     imgData.imgStr = new ImageStream(str, width,
2741 				     colorMap->getNumPixelComps(),
2742 				     colorMap->getBits());
2743     imgData.imgStr->reset();
2744     imgData.colorMap = colorMap;
2745     imgData.mask = maskBitmap;
2746     imgData.colorMode = colorMode;
2747     imgData.width = width;
2748     imgData.height = height;
2749     imgData.y = 0;
2750 
2751     // special case for one-channel (monochrome/gray/separation) images:
2752     // build a lookup table here
2753     imgData.lookup = NULL;
2754     if (colorMap->getNumPixelComps() == 1) {
2755       n = 1 << colorMap->getBits();
2756       switch (colorMode) {
2757       case splashModeMono1:
2758       case splashModeMono8:
2759 	imgData.lookup = (SplashColorPtr)gmalloc(n);
2760 	for (i = 0; i < n; ++i) {
2761 	  pix = (Guchar)i;
2762 	  colorMap->getGray(&pix, &gray);
2763 	  imgData.lookup[i] = colToByte(gray);
2764 	}
2765 	break;
2766       case splashModeRGB8:
2767       case splashModeBGR8:
2768 	imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2769 	for (i = 0; i < n; ++i) {
2770 	  pix = (Guchar)i;
2771 	  colorMap->getRGB(&pix, &rgb);
2772 	  imgData.lookup[3*i] = colToByte(rgb.r);
2773 	  imgData.lookup[3*i+1] = colToByte(rgb.g);
2774 	  imgData.lookup[3*i+2] = colToByte(rgb.b);
2775 	}
2776 	break;
2777       case splashModeXBGR8:
2778 	imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2779 	for (i = 0; i < n; ++i) {
2780 	  pix = (Guchar)i;
2781 	  colorMap->getRGB(&pix, &rgb);
2782 	  imgData.lookup[4*i] = colToByte(rgb.r);
2783 	  imgData.lookup[4*i+1] = colToByte(rgb.g);
2784 	  imgData.lookup[4*i+2] = colToByte(rgb.b);
2785 	  imgData.lookup[4*i+3] = 255;
2786 	}
2787 	break;
2788 #if SPLASH_CMYK
2789       case splashModeCMYK8:
2790 	imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2791 	for (i = 0; i < n; ++i) {
2792 	  pix = (Guchar)i;
2793 	  colorMap->getCMYK(&pix, &cmyk);
2794 	  imgData.lookup[4*i] = colToByte(cmyk.c);
2795 	  imgData.lookup[4*i+1] = colToByte(cmyk.m);
2796 	  imgData.lookup[4*i+2] = colToByte(cmyk.y);
2797 	  imgData.lookup[4*i+3] = colToByte(cmyk.k);
2798 	}
2799 	break;
2800 #endif
2801       }
2802     }
2803 
2804     if (colorMode == splashModeMono1) {
2805       srcMode = splashModeMono8;
2806     } else {
2807       srcMode = colorMode;
2808     }
2809     splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
2810 		      width, height, mat);
2811 
2812     delete maskBitmap;
2813     gfree(imgData.lookup);
2814     delete imgData.imgStr;
2815     str->close();
2816   }
2817 }
2818 
2819 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2820 					  Stream *str, int width, int height,
2821 					  GfxImageColorMap *colorMap,
2822 					  GBool interpolate,
2823 					  Stream *maskStr,
2824 					  int maskWidth, int maskHeight,
2825 					  GfxImageColorMap *maskColorMap,
2826 					  GBool maskInterpolate) {
2827   double *ctm;
2828   SplashCoord mat[6];
2829   SplashOutImageData imgData;
2830   SplashOutImageData imgMaskData;
2831   SplashColorMode srcMode;
2832   SplashBitmap *maskBitmap;
2833   Splash *maskSplash;
2834   SplashColor maskColor;
2835   GfxGray gray;
2836   GfxRGB rgb;
2837 #if SPLASH_CMYK
2838   GfxCMYK cmyk;
2839 #endif
2840   Guchar pix;
2841   int n, i;
2842 
2843   ctm = state->getCTM();
2844   for (i = 0; i < 6; ++i) {
2845     if (!isfinite(ctm[i])) return;
2846   }
2847   mat[0] = ctm[0];
2848   mat[1] = ctm[1];
2849   mat[2] = -ctm[2];
2850   mat[3] = -ctm[3];
2851   mat[4] = ctm[2] + ctm[4];
2852   mat[5] = ctm[3] + ctm[5];
2853 
2854   //----- set up the soft mask
2855 
2856   imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2857 				       maskColorMap->getNumPixelComps(),
2858 				       maskColorMap->getBits());
2859   imgMaskData.imgStr->reset();
2860   imgMaskData.colorMap = maskColorMap;
2861   imgMaskData.maskColors = NULL;
2862   imgMaskData.colorMode = splashModeMono8;
2863   imgMaskData.width = maskWidth;
2864   imgMaskData.height = maskHeight;
2865   imgMaskData.y = 0;
2866   n = 1 << maskColorMap->getBits();
2867   imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2868   for (i = 0; i < n; ++i) {
2869     pix = (Guchar)i;
2870     maskColorMap->getGray(&pix, &gray);
2871     imgMaskData.lookup[i] = colToByte(gray);
2872   }
2873   maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2874 				1, splashModeMono8, gFalse);
2875   maskSplash = new Splash(maskBitmap, vectorAntialias);
2876   maskColor[0] = 0;
2877   maskSplash->clear(maskColor);
2878   maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
2879 			maskWidth, maskHeight, mat);
2880   delete imgMaskData.imgStr;
2881   maskStr->close();
2882   gfree(imgMaskData.lookup);
2883   delete maskSplash;
2884   splash->setSoftMask(maskBitmap);
2885 
2886   //----- draw the source image
2887 
2888   imgData.imgStr = new ImageStream(str, width,
2889 				   colorMap->getNumPixelComps(),
2890 				   colorMap->getBits());
2891   imgData.imgStr->reset();
2892   imgData.colorMap = colorMap;
2893   imgData.maskColors = NULL;
2894   imgData.colorMode = colorMode;
2895   imgData.width = width;
2896   imgData.height = height;
2897   imgData.y = 0;
2898 
2899   // special case for one-channel (monochrome/gray/separation) images:
2900   // build a lookup table here
2901   imgData.lookup = NULL;
2902   if (colorMap->getNumPixelComps() == 1) {
2903     n = 1 << colorMap->getBits();
2904     switch (colorMode) {
2905     case splashModeMono1:
2906     case splashModeMono8:
2907       imgData.lookup = (SplashColorPtr)gmalloc(n);
2908       for (i = 0; i < n; ++i) {
2909 	pix = (Guchar)i;
2910 	colorMap->getGray(&pix, &gray);
2911 	imgData.lookup[i] = colToByte(gray);
2912       }
2913       break;
2914     case splashModeRGB8:
2915     case splashModeBGR8:
2916       imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
2917       for (i = 0; i < n; ++i) {
2918 	pix = (Guchar)i;
2919 	colorMap->getRGB(&pix, &rgb);
2920 	imgData.lookup[3*i] = colToByte(rgb.r);
2921 	imgData.lookup[3*i+1] = colToByte(rgb.g);
2922 	imgData.lookup[3*i+2] = colToByte(rgb.b);
2923       }
2924       break;
2925     case splashModeXBGR8:
2926       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2927       for (i = 0; i < n; ++i) {
2928 	pix = (Guchar)i;
2929 	colorMap->getRGB(&pix, &rgb);
2930 	imgData.lookup[4*i] = colToByte(rgb.r);
2931 	imgData.lookup[4*i+1] = colToByte(rgb.g);
2932 	imgData.lookup[4*i+2] = colToByte(rgb.b);
2933 	imgData.lookup[4*i+3] = 255;
2934       }
2935       break;
2936 #if SPLASH_CMYK
2937     case splashModeCMYK8:
2938       imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
2939       for (i = 0; i < n; ++i) {
2940 	pix = (Guchar)i;
2941 	colorMap->getCMYK(&pix, &cmyk);
2942 	imgData.lookup[4*i] = colToByte(cmyk.c);
2943 	imgData.lookup[4*i+1] = colToByte(cmyk.m);
2944 	imgData.lookup[4*i+2] = colToByte(cmyk.y);
2945 	imgData.lookup[4*i+3] = colToByte(cmyk.k);
2946       }
2947       break;
2948 #endif
2949     }
2950   }
2951 
2952   if (colorMode == splashModeMono1) {
2953     srcMode = splashModeMono8;
2954   } else {
2955     srcMode = colorMode;
2956   }
2957   splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
2958 
2959   splash->setSoftMask(NULL);
2960   gfree(imgData.lookup);
2961   delete imgData.imgStr;
2962   str->close();
2963 }
2964 
2965 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2966 					     GfxColorSpace *blendingColorSpace,
2967 					     GBool isolated, GBool /*knockout*/,
2968 					     GBool /*forSoftMask*/) {
2969   SplashTransparencyGroup *transpGroup;
2970   SplashColor color;
2971   double xMin, yMin, xMax, yMax, x, y;
2972   int tx, ty, w, h;
2973 
2974   // transform the bbox
2975   state->transform(bbox[0], bbox[1], &x, &y);
2976   xMin = xMax = x;
2977   yMin = yMax = y;
2978   state->transform(bbox[0], bbox[3], &x, &y);
2979   if (x < xMin) {
2980     xMin = x;
2981   } else if (x > xMax) {
2982     xMax = x;
2983   }
2984   if (y < yMin) {
2985     yMin = y;
2986   } else if (y > yMax) {
2987     yMax = y;
2988   }
2989   state->transform(bbox[2], bbox[1], &x, &y);
2990   if (x < xMin) {
2991     xMin = x;
2992   } else if (x > xMax) {
2993     xMax = x;
2994   }
2995   if (y < yMin) {
2996     yMin = y;
2997   } else if (y > yMax) {
2998     yMax = y;
2999   }
3000   state->transform(bbox[2], bbox[3], &x, &y);
3001   if (x < xMin) {
3002     xMin = x;
3003   } else if (x > xMax) {
3004     xMax = x;
3005   }
3006   if (y < yMin) {
3007     yMin = y;
3008   } else if (y > yMax) {
3009     yMax = y;
3010   }
3011   tx = (int)floor(xMin);
3012   if (tx < 0) {
3013     tx = 0;
3014   } else if (tx >= bitmap->getWidth()) {
3015     tx = bitmap->getWidth() - 1;
3016   }
3017   ty = (int)floor(yMin);
3018   if (ty < 0) {
3019     ty = 0;
3020   } else if (ty >= bitmap->getHeight()) {
3021     ty = bitmap->getHeight() - 1;
3022   }
3023   w = (int)ceil(xMax) - tx + 1;
3024   if (tx + w > bitmap->getWidth()) {
3025     w = bitmap->getWidth() - tx;
3026   }
3027   if (w < 1) {
3028     w = 1;
3029   }
3030   h = (int)ceil(yMax) - ty + 1;
3031   if (ty + h > bitmap->getHeight()) {
3032     h = bitmap->getHeight() - ty;
3033   }
3034   if (h < 1) {
3035     h = 1;
3036   }
3037 
3038   // push a new stack entry
3039   transpGroup = new SplashTransparencyGroup();
3040   transpGroup->tx = tx;
3041   transpGroup->ty = ty;
3042   transpGroup->blendingColorSpace = blendingColorSpace;
3043   transpGroup->isolated = isolated;
3044   transpGroup->next = transpGroupStack;
3045   transpGroupStack = transpGroup;
3046 
3047   // save state
3048   transpGroup->origBitmap = bitmap;
3049   transpGroup->origSplash = splash;
3050 
3051   //~ this ignores the blendingColorSpace arg
3052 
3053   // create the temporary bitmap
3054   bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
3055 			    bitmapTopDown);
3056   splash = new Splash(bitmap, vectorAntialias,
3057 		      transpGroup->origSplash->getScreen());
3058   if (isolated) {
3059     switch (colorMode) {
3060     case splashModeMono1:
3061     case splashModeMono8:
3062       color[0] = 0;
3063       break;
3064     case splashModeXBGR8:
3065       color[3] = 255;
3066     case splashModeRGB8:
3067     case splashModeBGR8:
3068       color[0] = color[1] = color[2] = 0;
3069       break;
3070 #if SPLASH_CMYK
3071     case splashModeCMYK8:
3072       color[0] = color[1] = color[2] = color[3] = 0;
3073       break;
3074 #endif
3075     default:
3076       // make gcc happy
3077       break;
3078     }
3079     splash->clear(color, 0);
3080   } else {
3081     splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
3082     splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
3083   }
3084   transpGroup->tBitmap = bitmap;
3085   state->shiftCTM(-tx, -ty);
3086   updateCTM(state, 0, 0, 0, 0, 0, 0);
3087 }
3088 
3089 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
3090   double *ctm;
3091 
3092   // restore state
3093   delete splash;
3094   bitmap = transpGroupStack->origBitmap;
3095   splash = transpGroupStack->origSplash;
3096   ctm = state->getCTM();
3097   state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
3098   updateCTM(state, 0, 0, 0, 0, 0, 0);
3099 }
3100 
3101 void SplashOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
3102   SplashBitmap *tBitmap;
3103   SplashTransparencyGroup *transpGroup;
3104   GBool isolated;
3105   int tx, ty;
3106 
3107   tx = transpGroupStack->tx;
3108   ty = transpGroupStack->ty;
3109   tBitmap = transpGroupStack->tBitmap;
3110   isolated = transpGroupStack->isolated;
3111 
3112   // paint the transparency group onto the parent bitmap
3113   // - the clip path was set in the parent's state)
3114   splash->composite(tBitmap, 0, 0, tx, ty,
3115 		    tBitmap->getWidth(), tBitmap->getHeight(),
3116 		    gFalse, !isolated);
3117 
3118   // pop the stack
3119   transpGroup = transpGroupStack;
3120   transpGroupStack = transpGroup->next;
3121   delete transpGroup;
3122 
3123   delete tBitmap;
3124 }
3125 
3126 void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
3127 				  GBool alpha, Function *transferFunc,
3128 				  GfxColor *backdropColor) {
3129   SplashBitmap *softMask, *tBitmap;
3130   Splash *tSplash;
3131   SplashTransparencyGroup *transpGroup;
3132   SplashColor color;
3133   SplashColorPtr p;
3134   GfxGray gray;
3135   GfxRGB rgb;
3136 #if SPLASH_CMYK
3137   GfxCMYK cmyk;
3138 #endif
3139   double lum, lum2;
3140   int tx, ty, x, y;
3141 
3142   tx = transpGroupStack->tx;
3143   ty = transpGroupStack->ty;
3144   tBitmap = transpGroupStack->tBitmap;
3145 
3146   // composite with backdrop color
3147   if (!alpha && colorMode != splashModeMono1) {
3148     //~ need to correctly handle the case where no blending color
3149     //~ space is given
3150     if (transpGroupStack->blendingColorSpace) {
3151       tSplash = new Splash(tBitmap, vectorAntialias,
3152 			   transpGroupStack->origSplash->getScreen());
3153       switch (colorMode) {
3154       case splashModeMono1:
3155 	// transparency is not supported in mono1 mode
3156 	break;
3157       case splashModeMono8:
3158 	transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
3159 	color[0] = colToByte(gray);
3160 	tSplash->compositeBackground(color);
3161 	break;
3162       case splashModeXBGR8:
3163 	color[3] = 255;
3164       case splashModeRGB8:
3165       case splashModeBGR8:
3166 	transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
3167 	color[0] = colToByte(rgb.r);
3168 	color[1] = colToByte(rgb.g);
3169 	color[2] = colToByte(rgb.b);
3170 	tSplash->compositeBackground(color);
3171 	break;
3172 #if SPLASH_CMYK
3173       case splashModeCMYK8:
3174 	transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
3175 	color[0] = colToByte(cmyk.c);
3176 	color[1] = colToByte(cmyk.m);
3177 	color[2] = colToByte(cmyk.y);
3178 	color[3] = colToByte(cmyk.k);
3179 	tSplash->compositeBackground(color);
3180 	break;
3181 #endif
3182       }
3183       delete tSplash;
3184     }
3185   }
3186 
3187   softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3188 			      1, splashModeMono8, gFalse);
3189   unsigned char fill = 0;
3190   if (transpGroupStack->blendingColorSpace) {
3191 	transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
3192 	fill = colToByte(gray);
3193   }
3194   memset(softMask->getDataPtr(), fill,
3195 	 softMask->getRowSize() * softMask->getHeight());
3196   p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
3197   int xMax = tBitmap->getWidth();
3198   int yMax = tBitmap->getHeight();
3199   if (xMax + tx > bitmap->getWidth()) xMax = bitmap->getWidth() - tx;
3200   if (yMax + ty > bitmap->getHeight()) yMax = bitmap->getHeight() - ty;
3201   for (y = 0; y < yMax; ++y) {
3202     for (x = 0; x < xMax; ++x) {
3203       if (alpha) {
3204 	p[x] = tBitmap->getAlpha(x, y);
3205       } else {
3206 	tBitmap->getPixel(x, y, color);
3207 	// convert to luminosity
3208 	switch (colorMode) {
3209 	case splashModeMono1:
3210 	case splashModeMono8:
3211 	  lum = color[0] / 255.0;
3212 	  break;
3213 	case splashModeXBGR8:
3214 	case splashModeRGB8:
3215 	case splashModeBGR8:
3216 	  lum = (0.3 / 255.0) * color[0] +
3217 	        (0.59 / 255.0) * color[1] +
3218 	        (0.11 / 255.0) * color[2];
3219 	  break;
3220 #if SPLASH_CMYK
3221 	case splashModeCMYK8:
3222 	  lum = (1 - color[3] / 255.0)
3223 	        - (0.3 / 255.0) * color[0]
3224 	        - (0.59 / 255.0) * color[1]
3225 	        - (0.11 / 255.0) * color[2];
3226 	  if (lum < 0) {
3227 	    lum = 0;
3228 	  }
3229 	  break;
3230 #endif
3231 	}
3232 	if (transferFunc) {
3233 	  transferFunc->transform(&lum, &lum2);
3234 	} else {
3235 	  lum2 = lum;
3236 	}
3237 	p[x] = (int)(lum2 * 255.0 + 0.5);
3238       }
3239     }
3240     p += softMask->getRowSize();
3241   }
3242   splash->setSoftMask(softMask);
3243 
3244   // pop the stack
3245   transpGroup = transpGroupStack;
3246   transpGroupStack = transpGroup->next;
3247   delete transpGroup;
3248 
3249   delete tBitmap;
3250 }
3251 
3252 void SplashOutputDev::clearSoftMask(GfxState * /*state*/) {
3253   splash->setSoftMask(NULL);
3254 }
3255 
3256 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
3257   splashColorCopy(paperColor, paperColorA);
3258 }
3259 
3260 int SplashOutputDev::getBitmapWidth() {
3261   return bitmap->getWidth();
3262 }
3263 
3264 int SplashOutputDev::getBitmapHeight() {
3265   return bitmap->getHeight();
3266 }
3267 
3268 SplashBitmap *SplashOutputDev::takeBitmap() {
3269   SplashBitmap *ret;
3270 
3271   ret = bitmap;
3272   bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
3273 			    colorMode != splashModeMono1, bitmapTopDown);
3274   return ret;
3275 }
3276 
3277 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
3278 				   int *xMax, int *yMax) {
3279   splash->getModRegion(xMin, yMin, xMax, yMax);
3280 }
3281 
3282 void SplashOutputDev::clearModRegion() {
3283   splash->clearModRegion();
3284 }
3285 
3286 void SplashOutputDev::setFillColor(int r, int g, int b) {
3287   GfxRGB rgb;
3288   GfxGray gray;
3289 #if SPLASH_CMYK
3290   GfxCMYK cmyk;
3291 #endif
3292 
3293   rgb.r = byteToCol(r);
3294   rgb.g = byteToCol(g);
3295   rgb.b = byteToCol(b);
3296   gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5);
3297   if (gray > gfxColorComp1) {
3298     gray = gfxColorComp1;
3299   }
3300 #if SPLASH_CMYK
3301   cmyk.c = gfxColorComp1 - rgb.r;
3302   cmyk.m = gfxColorComp1 - rgb.g;
3303   cmyk.y = gfxColorComp1 - rgb.b;
3304   cmyk.k = 0;
3305   splash->setFillPattern(getColor(gray, &rgb, &cmyk));
3306 #else
3307   splash->setFillPattern(getColor(gray, &rgb));
3308 #endif
3309 }
3310 
3311 #if 1 //~tmp: turn off anti-aliasing temporarily
3312 GBool SplashOutputDev::getVectorAntialias() {
3313   return splash->getVectorAntialias();
3314 }
3315 
3316 void SplashOutputDev::setVectorAntialias(GBool vaa) {
3317   splash->setVectorAntialias(vaa);
3318 }
3319 #endif
3320 
3321 void SplashOutputDev::setFreeTypeHinting(GBool enable)
3322 {
3323   enableFreeTypeHinting = enable;
3324 }
3325 
3326 GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
3327 {
3328   GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode();
3329   GBool bDirectColorTranslation = gFalse; // triggers an optimization.
3330   switch (colorMode) {
3331     case splashModeRGB8:
3332       bDirectColorTranslation = (shadingMode == csDeviceRGB);
3333     break;
3334 #if SPLASH_CMYK
3335     case splashModeCMYK8:
3336       bDirectColorTranslation = (shadingMode == csDeviceCMYK);
3337     break;
3338 #endif
3339     default:
3340     break;
3341   }
3342   SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading);
3343   // restore vector antialias because we support it here
3344   if (shading->isParameterized()) {
3345     GBool vaa = getVectorAntialias();
3346     GBool retVal = gFalse;
3347     setVectorAntialias(gTrue);
3348     retVal = splash->gouraudTriangleShadedFill(splashShading);
3349     setVectorAntialias(vaa);
3350     return retVal;
3351   }
3352   return gFalse;
3353 }
3354 
3355 GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
3356   double xMin, yMin, xMax, yMax;
3357   SplashPath *path;
3358 
3359   GBool vaa = getVectorAntialias();
3360   GBool retVal = gFalse;
3361   // restore vector antialias because we support it here
3362   setVectorAntialias(gTrue);
3363   // get the clip region bbox
3364   state->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
3365 
3366   // fill the region
3367   state->moveTo(xMin, yMin);
3368   state->lineTo(xMax, yMin);
3369   state->lineTo(xMax, yMax);
3370   state->lineTo(xMin, yMax);
3371   state->closePath();
3372   path = convertPath(state, state->getPath());
3373 
3374   SplashPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
3375   retVal = (splash->shadedFill(path, shading->getHasBBox(), pattern) == splashOk);
3376   setVectorAntialias(vaa);
3377   state->clearPath();
3378   delete pattern;
3379   delete path;
3380 
3381   return retVal;
3382 }
3383