root/middleware-offline/trunk/_src/eidmw/pteid-poppler/poppler/SplashOutputDev.cc @ 226

Revision 226, 112.1 KB (checked in by vsilva, 8 years ago)

Improve PDF native Signature

Line 
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-2012 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-2012 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// Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
31// Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
32// Copyright (C) 2011 Adrian Johnson <ajohnson@redneon.com>
33//
34// To see a description of the changes please see the Changelog file that
35// came with your tarball or type make ChangeLog if you are building from git
36//
37//========================================================================
38
39#include <config.h>
40
41#ifdef USE_GCC_PRAGMAS
42#pragma implementation
43#endif
44
45#include <string.h>
46#include <math.h>
47#include "goo/gfile.h"
48#include "GlobalParams.h"
49#include "Error.h"
50#include "Object.h"
51#include "Gfx.h"
52#include "GfxFont.h"
53#include "Page.h"
54#include "PDFDoc.h"
55#include "Link.h"
56#include "FontEncodingTables.h"
57#include "fofi/FoFiTrueType.h"
58#include "splash/SplashBitmap.h"
59#include "splash/SplashGlyphBitmap.h"
60#include "splash/SplashPattern.h"
61#include "splash/SplashScreen.h"
62#include "splash/SplashPath.h"
63#include "splash/SplashState.h"
64#include "splash/SplashErrorCodes.h"
65#include "splash/SplashFontEngine.h"
66#include "splash/SplashFont.h"
67#include "splash/SplashFontFile.h"
68#include "splash/SplashFontFileID.h"
69#include "splash/Splash.h"
70#include "SplashOutputDev.h"
71
72#ifdef VMS
73#if (__VMS_VER < 70000000)
74extern "C" int unlink(char *filename);
75#endif
76#endif
77
78#ifdef _MSC_VER
79#include <float.h>
80#define isfinite(x) _finite(x)
81#endif
82
83#ifdef __sun
84#include <ieeefp.h>
85#define isfinite(x) finite(x)
86#endif
87
88static inline void convertGfxColor(SplashColorPtr dest,
89                                   SplashColorMode colorMode,
90                                   GfxColorSpace *colorSpace,
91                                   GfxColor *src) {
92  SplashColor color;
93  GfxGray gray;
94  GfxRGB rgb;
95#if SPLASH_CMYK
96  GfxCMYK cmyk;
97#endif
98
99  // make gcc happy
100  color[0] = color[1] = color[2] = 0;
101#if SPLASH_CMYK
102  color[3] = 0;
103#endif
104  switch (colorMode) {
105    case splashModeMono1:
106    case splashModeMono8:
107      colorSpace->getGray(src, &gray);
108      color[0] = colToByte(gray);
109    break;
110    case splashModeXBGR8:
111      color[3] = 255;
112    case splashModeBGR8:
113    case splashModeRGB8:
114      colorSpace->getRGB(src, &rgb);
115      color[0] = colToByte(rgb.r);
116      color[1] = colToByte(rgb.g);
117      color[2] = colToByte(rgb.b);
118    break;
119#if SPLASH_CMYK
120    case splashModeCMYK8:
121      colorSpace->getCMYK(src, &cmyk);
122      color[0] = colToByte(cmyk.c);
123      color[1] = colToByte(cmyk.m);
124      color[2] = colToByte(cmyk.y);
125      color[3] = colToByte(cmyk.k);
126    break;
127#endif
128  }
129  splashColorCopy(dest, color);
130}
131
132//------------------------------------------------------------------------
133// SplashGouraudPattern
134//------------------------------------------------------------------------
135SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA,
136                                           GfxState *stateA, GfxGouraudTriangleShading *shadingA, SplashColorMode modeA) {
137  SplashColor defaultColor;
138  GfxColor srcColor;
139  state = stateA;
140  shading = shadingA;
141  mode = modeA;
142  bDirectColorTranslation = bDirectColorTranslationA;
143  shadingA->getColorSpace()->getDefaultColor(&srcColor);
144  convertGfxColor(defaultColor, mode, shadingA->getColorSpace(), &srcColor);
145}
146
147SplashGouraudPattern::~SplashGouraudPattern() {
148}
149
150void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
151  GfxColor src;
152  GfxColorSpace* srcColorSpace = shading->getColorSpace();
153  int colorComps = 3;
154#if SPLASH_CMYK
155  if (mode == splashModeCMYK8)
156    colorComps=4;
157#endif
158
159  shading->getParameterizedColor(colorinterp, &src);
160
161  if (bDirectColorTranslation) {
162    for (int m = 0; m < colorComps; ++m)
163      dest[m] = colToByte(src.c[m]);
164  } else {
165    convertGfxColor(dest, mode, srcColorSpace, &src);
166  }
167}
168
169//------------------------------------------------------------------------
170// SplashUnivariatePattern
171//------------------------------------------------------------------------
172
173SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) {
174  Matrix ctm;
175  double xMin, yMin, xMax, yMax;
176
177  shading = shadingA;
178  state = stateA;
179  colorMode = colorModeA;
180
181  state->getCTM(&ctm);
182  ctm.invertTo(&ictm);
183
184  // get the function domain
185  t0 = shading->getDomain0();
186  t1 = shading->getDomain1();
187  dt = t1 - t0;
188
189  stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
190  shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax);
191}
192
193SplashUnivariatePattern::~SplashUnivariatePattern() {
194}
195
196GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) {
197  GfxColor gfxColor;
198  double xc, yc, t;
199
200  ictm.transform(x, y, &xc, &yc);
201  if (! getParameter (xc, yc, &t))
202      return gFalse;
203
204  shading->getColor(t, &gfxColor);
205  convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor);
206  return gTrue;
207}
208
209GBool SplashUnivariatePattern::testPosition(int x, int y) {
210  double xc, yc, t;
211
212  ictm.transform(x, y, &xc, &yc);
213  if (! getParameter (xc, yc, &t))
214      return gFalse;
215  return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0);
216}
217
218
219//------------------------------------------------------------------------
220// SplashRadialPattern
221//------------------------------------------------------------------------
222#define RADIAL_EPSILON (1. / 1024 / 1024)
223
224SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA):
225  SplashUnivariatePattern(colorModeA, stateA, shadingA)
226{
227  SplashColor defaultColor;
228  GfxColor srcColor;
229
230  shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr);
231  dx -= x0;
232  dy -= y0;
233  dr -= r0;
234  a = dx*dx + dy*dy - dr*dr;
235  if (fabs(a) > RADIAL_EPSILON)
236    inva = 1.0 / a;
237  shadingA->getColorSpace()->getDefaultColor(&srcColor);
238  convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
239}
240
241SplashRadialPattern::~SplashRadialPattern() {
242}
243
244GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) {
245  double b, c, s0, s1;
246
247  // We want to solve this system of equations:
248  //
249  // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2
250  // 2. xc(s) = x0 + s * (x1 - xo)
251  // 3. yc(s) = y0 + s * (y1 - yo)
252  // 4. rc(s) = r0 + s * (r1 - ro)
253  //
254  // To simplify the system a little, we translate
255  // our coordinates to have the origin in (x0,y0)
256
257  xs -= x0;
258  ys -= y0;
259
260  // Then we have to solve the equation:
261  //   A*s^2 - 2*B*s + C = 0
262  // where
263  //   A = dx^2  + dy^2  - dr^2
264  //   B = xs*dx + ys*dy + r0*dr
265  //   C = xs^2  + ys^2  - r0^2
266
267  b = xs*dx + ys*dy + r0*dr;
268  c = xs*xs + ys*ys - r0*r0;
269
270  if (fabs(a) <= RADIAL_EPSILON) {
271    // A is 0, thus the equation simplifies to:
272    //   -2*B*s + C = 0
273    // If B is 0, we can either have no solution or an indeterminate
274    // equation, thus we behave as if we had an invalid solution
275    if (fabs(b) <= RADIAL_EPSILON)
276      return gFalse;
277
278    s0 = s1 = 0.5 * c / b;
279  } else {
280    double d;
281
282    d = b*b - a*c;
283    if (d < 0)
284      return gFalse;
285
286    d = sqrt (d);
287    s0 = b + d;
288    s1 = b - d;
289
290    // If A < 0, one of the two solutions will have negative radius,
291    // thus it will be ignored. Otherwise we know that s1 <= s0
292    // (because d >=0 implies b - d <= b + d), so if both are valid it
293    // will be the true solution.
294    s0 *= inva;
295    s1 *= inva;
296  }
297
298  if (r0 + s0 * dr >= 0) {
299    if (0 <= s0 && s0 <= 1) {
300      *t = t0 + dt * s0;
301      return gTrue;
302    } else if (s0 < 0 && shading->getExtend0()) {
303      *t = t0;
304      return gTrue;
305    } else if (s0 > 1 && shading->getExtend1()) {
306      *t = t1;
307      return gTrue;
308    }
309  }
310
311  if (r0 + s1 * dr >= 0) {
312    if (0 <= s1 && s1 <= 1) {
313      *t = t0 + dt * s1;
314      return gTrue;
315    } else if (s1 < 0 && shading->getExtend0()) {
316      *t = t0;
317      return gTrue;
318    } else if (s1 > 1 && shading->getExtend1()) {
319      *t = t1;
320      return gTrue;
321    }
322  }
323
324  return gFalse;
325}
326
327#undef RADIAL_EPSILON
328
329//------------------------------------------------------------------------
330// SplashAxialPattern
331//------------------------------------------------------------------------
332
333SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA):
334    SplashUnivariatePattern(colorModeA, stateA, shadingA)
335{
336  SplashColor defaultColor;
337  GfxColor srcColor;
338
339  shadingA->getCoords(&x0, &y0, &x1, &y1);
340  dx = x1 - x0;
341  dy = y1 - y0;
342  mul = 1 / (dx * dx + dy * dy);
343  shadingA->getColorSpace()->getDefaultColor(&srcColor);
344  convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
345}
346
347SplashAxialPattern::~SplashAxialPattern() {
348}
349
350GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
351  double s;
352
353  xc -= x0;
354  yc -= y0;
355
356  s = (xc * dx + yc * dy) * mul;
357  if (0 <= s && s <= 1) {
358    *t = t0 + dt * s;
359  } else if (s < 0 && shading->getExtend0()) {
360    *t = t0;
361  } else if (s > 1 && shading->getExtend1()) {
362    *t = t1;
363  } else {
364    return gFalse;
365  }
366
367  return gTrue;
368}
369
370//------------------------------------------------------------------------
371// Type 3 font cache size parameters
372#define type3FontCacheAssoc   8
373#define type3FontCacheMaxSets 8
374#define type3FontCacheSize    (128*1024)
375
376//------------------------------------------------------------------------
377// Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
378static inline Guchar div255(int x) {
379  return (Guchar)((x + (x >> 8) + 0x80) >> 8);
380}
381
382#if SPLASH_CMYK
383
384#include "GfxState_helpers.h"
385
386//-------------------------------------------------------------------------
387// helper for Blend functions (convert CMYK to RGB, do blend, convert back)
388//-------------------------------------------------------------------------
389
390// based in GfxState.cc
391
392static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) {
393  double c, m, y, k, c1, m1, y1, k1, r, g, b;
394
395  c = colToDbl(byteToCol(cmyk[0]));
396  m = colToDbl(byteToCol(cmyk[1]));
397  y = colToDbl(byteToCol(cmyk[2]));
398  k = colToDbl(byteToCol(cmyk[3]));
399  c1 = 1 - c;
400  m1 = 1 - m;
401  y1 = 1 - y;
402  k1 = 1 - k;
403  cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
404  rgb[0] = colToByte(clip01(dblToCol(r)));
405  rgb[1] = colToByte(clip01(dblToCol(g)));
406  rgb[2] = colToByte(clip01(dblToCol(b)));
407}
408
409static void rgbToCMYK(SplashColor rgb, SplashColorPtr cmyk) {
410  GfxColorComp c, m, y, k;
411
412  c = clip01(gfxColorComp1 - byteToCol(rgb[0]));
413  m = clip01(gfxColorComp1 - byteToCol(rgb[1]));
414  y = clip01(gfxColorComp1 - byteToCol(rgb[2]));
415  k = c;
416  if (m < k) {
417    k = m;
418  }
419  if (y < k) {
420    k = y;
421  }
422  cmyk[0] = colToByte(c - k);
423  cmyk[1] = colToByte(m - k);
424  cmyk[2] = colToByte(y - k);
425  cmyk[3] = colToByte(k);
426}
427
428#endif
429
430//------------------------------------------------------------------------
431// Blend functions
432//------------------------------------------------------------------------
433
434static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
435                                   SplashColorPtr blend, SplashColorMode cm) {
436  int i;
437
438#if SPLASH_CMYK
439  if (cm == splashModeCMYK8) {
440    SplashColor rgbSrc;
441    SplashColor rgbDest;
442    SplashColor rgbBlend;
443    cmykToRGB(src, rgbSrc);
444    cmykToRGB(dest, rgbDest);
445    for (i = 0; i < 3; ++i) {
446      rgbBlend[i] = (rgbDest[i] * rgbSrc[i]) / 255;
447    }
448    rgbToCMYK(rgbBlend, blend);
449  } else
450#endif
451  {
452    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
453      blend[i] = (dest[i] * src[i]) / 255;
454    }
455  }
456}
457
458static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
459                                 SplashColorPtr blend, SplashColorMode cm) {
460  int i;
461
462#if SPLASH_CMYK
463  if (cm == splashModeCMYK8) {
464    SplashColor rgbSrc;
465    SplashColor rgbDest;
466    SplashColor rgbBlend;
467    cmykToRGB(src, rgbSrc);
468    cmykToRGB(dest, rgbDest);
469    for (i = 0; i < 3; ++i) {
470      rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (rgbDest[i] * rgbSrc[i]) / 255;
471    }
472    rgbToCMYK(rgbBlend, blend);
473  } else
474#endif
475  {
476    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
477      blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
478    }
479  }
480}
481
482static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
483                                  SplashColorPtr blend, SplashColorMode cm) {
484  int i;
485
486#if SPLASH_CMYK
487  if (cm == splashModeCMYK8) {
488    SplashColor rgbSrc;
489    SplashColor rgbDest;
490    SplashColor rgbBlend;
491    cmykToRGB(src, rgbSrc);
492    cmykToRGB(dest, rgbDest);
493    for (i = 0; i < 3; ++i) {
494      rgbBlend[i] = rgbDest[i] < 0x80
495                      ? (rgbSrc[i] * 2 * rgbDest[i]) / 255
496                      : 255 - 2 * ((255 - rgbSrc[i]) * (255 - rgbDest[i])) / 255;
497    }
498    rgbToCMYK(rgbBlend, blend);
499  } else
500#endif
501  {
502    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
503      blend[i] = dest[i] < 0x80
504                   ? (src[i] * 2 * dest[i]) / 255
505                   : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
506    }
507  }
508}
509
510static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
511                                 SplashColorPtr blend, SplashColorMode cm) {
512  int i;
513
514#if SPLASH_CMYK
515  if (cm == splashModeCMYK8) {
516    SplashColor rgbSrc;
517    SplashColor rgbDest;
518    SplashColor rgbBlend;
519    cmykToRGB(src, rgbSrc);
520    cmykToRGB(dest, rgbDest);
521    for (i = 0; i < 3; ++i) {
522      rgbBlend[i] = rgbDest[i] < rgbSrc[i] ? rgbDest[i] : rgbSrc[i];
523    }
524    rgbToCMYK(rgbBlend, blend);
525  } else
526#endif
527  {
528    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
529      blend[i] = dest[i] < src[i] ? dest[i] : src[i];
530    }
531  }
532}
533
534static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
535                                  SplashColorPtr blend, SplashColorMode cm) {
536  int i;
537
538#if SPLASH_CMYK
539  if (cm == splashModeCMYK8) {
540    SplashColor rgbSrc;
541    SplashColor rgbDest;
542    SplashColor rgbBlend;
543    cmykToRGB(src, rgbSrc);
544    cmykToRGB(dest, rgbDest);
545    for (i = 0; i < 3; ++i) {
546      rgbBlend[i] = rgbDest[i] > rgbSrc[i] ? rgbDest[i] : rgbSrc[i];
547    }
548    rgbToCMYK(rgbBlend, blend);
549  } else
550#endif
551  {
552    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
553      blend[i] = dest[i] > src[i] ? dest[i] : src[i];
554    }
555  }
556}
557
558static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
559                                     SplashColorPtr blend,
560                                     SplashColorMode cm) {
561  int i, x;
562
563#if SPLASH_CMYK
564  if (cm == splashModeCMYK8) {
565    SplashColor rgbSrc;
566    SplashColor rgbDest;
567    SplashColor rgbBlend;
568    cmykToRGB(src, rgbSrc);
569    cmykToRGB(dest, rgbDest);
570    for (i = 0; i < 3; ++i) {
571      if (rgbSrc[i] == 255) {
572        rgbBlend[i] = 255;
573      } else {
574        x = (rgbDest[i] * 255) / (255 - rgbSrc[i]);
575        rgbBlend[i] = x <= 255 ? x : 255;
576      }
577    }
578    rgbToCMYK(rgbBlend, blend);
579  } else
580#endif
581  {
582    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
583      if (src[i] == 255) {
584        blend[i] = 255;
585      } else {
586        x = (dest[i] * 255) / (255 - src[i]);
587        blend[i] = x <= 255 ? x : 255;
588      }
589    }
590  }
591}
592
593static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
594                                    SplashColorPtr blend, SplashColorMode cm) {
595  int i, x;
596
597#if SPLASH_CMYK
598  if (cm == splashModeCMYK8) {
599    SplashColor rgbSrc;
600    SplashColor rgbDest;
601    SplashColor rgbBlend;
602    cmykToRGB(src, rgbSrc);
603    cmykToRGB(dest, rgbDest);
604    for (i = 0; i < 3; ++i) {
605      if (rgbSrc[i] == 0) {
606        rgbBlend[i] = 0;
607      } else {
608        x = ((255 - rgbDest[i]) * 255) / rgbSrc[i];
609        rgbBlend[i] = x <= 255 ? 255 - x : 0;
610      }
611    }
612    rgbToCMYK(rgbBlend, blend);
613  } else
614#endif
615  {
616    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
617      if (src[i] == 0) {
618        blend[i] = 0;
619      } else {
620        x = ((255 - dest[i]) * 255) / src[i];
621        blend[i] = x <= 255 ? 255 - x : 0;
622      }
623    }
624  }
625}
626
627static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
628                                    SplashColorPtr blend, SplashColorMode cm) {
629  int i;
630
631#if SPLASH_CMYK
632  if (cm == splashModeCMYK8) {
633    SplashColor rgbSrc;
634    SplashColor rgbDest;
635    SplashColor rgbBlend;
636    cmykToRGB(src, rgbSrc);
637    cmykToRGB(dest, rgbDest);
638    for (i = 0; i < 3; ++i) {
639      rgbBlend[i] = rgbSrc[i] < 0x80
640                      ? (rgbDest[i] * 2 * rgbSrc[i]) / 255
641                      : 255 - 2 * ((255 - rgbDest[i]) * (255 - rgbSrc[i])) / 255;
642    }
643    rgbToCMYK(rgbBlend, blend);
644  } else
645#endif
646  {
647    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
648      blend[i] = src[i] < 0x80
649                   ? (dest[i] * 2 * src[i]) / 255
650                   : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
651    }
652  }
653}
654
655static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
656                                    SplashColorPtr blend, SplashColorMode cm) {
657  int i, x;
658
659#if SPLASH_CMYK
660  if (cm == splashModeCMYK8) {
661    SplashColor rgbSrc;
662    SplashColor rgbDest;
663    SplashColor rgbBlend;
664    for (i = 0; i < 4; i++) {
665      // convert back to subtractive (s. Splash.cc)
666      src[i] = 0xff - src[i];
667      dest[i] = 0xff - dest[i];
668    }
669    cmykToRGB(src, rgbSrc);
670    cmykToRGB(dest, rgbDest);
671    for (i = 0; i < 3; ++i) {
672      if (rgbSrc[i] < 0x80) {
673        rgbBlend[i] = rgbDest[i] - (255 - 2 * rgbSrc[i]) * rgbDest[i] * (255 - rgbDest[i]) / (255 * 255);
674      } else {
675        if (rgbDest[i] < 0x40) {
676          x = (((((16 * rgbDest[i] - 12 * 255) * rgbDest[i]) / 255) + 4 * 255) * rgbDest[i]) / 255;
677        } else {
678          x = (int)sqrt(255.0 * rgbDest[i]);
679        }
680        rgbBlend[i] = rgbDest[i] + (2 * rgbSrc[i] - 255) * (x - rgbDest[i]) / 255;
681      }
682    }
683    rgbToCMYK(rgbBlend, blend);
684    for (i = 0; i < 4; i++) {
685      // convert back to additive (s. Splash.cc)
686      blend[i] = 0xff - blend[i];
687    }
688  } else
689#endif
690  {
691    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
692      if (src[i] < 0x80) {
693        blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255);
694      } else {
695        if (dest[i] < 0x40) {
696          x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255;
697        } else {
698          x = (int)sqrt(255.0 * dest[i]);
699        }
700        blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
701      }
702    }
703  }
704}
705
706static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
707                                     SplashColorPtr blend,
708                                     SplashColorMode cm) {
709  int i;
710
711#if SPLASH_CMYK
712  if (cm == splashModeCMYK8) {
713    SplashColor rgbSrc;
714    SplashColor rgbDest;
715    SplashColor rgbBlend;
716    cmykToRGB(src, rgbSrc);
717    cmykToRGB(dest, rgbDest);
718    for (i = 0; i < 3; ++i) {
719      rgbBlend[i] = rgbDest[i] < rgbSrc[i] ? rgbSrc[i] - rgbDest[i] : rgbDest[i] - rgbSrc[i];
720    }
721    rgbToCMYK(rgbBlend, blend);
722  } else
723#endif
724  {
725    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
726      blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
727    }
728  }
729}
730
731static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
732                                    SplashColorPtr blend, SplashColorMode cm) {
733  int i;
734
735#if SPLASH_CMYK
736  if (cm == splashModeCMYK8) {
737    SplashColor rgbSrc;
738    SplashColor rgbDest;
739    SplashColor rgbBlend;
740    cmykToRGB(src, rgbSrc);
741    cmykToRGB(dest, rgbDest);
742    for (i = 0; i < 3; ++i) {
743      rgbBlend[i] = rgbDest[i] + rgbSrc[i] - (2 * rgbDest[i] * rgbSrc[i]) / 255;
744    }
745    rgbToCMYK(rgbBlend, blend);
746  } else
747#endif
748  {
749    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
750      blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
751    }
752  }
753}
754
755static int getLum(int r, int g, int b) {
756  return (int)(0.3 * r + 0.59 * g + 0.11 * b);
757}
758
759static int getSat(int r, int g, int b) {
760  int rgbMin, rgbMax;
761
762  rgbMin = rgbMax = r;
763  if (g < rgbMin) {
764    rgbMin = g;
765  } else if (g > rgbMax) {
766    rgbMax = g;
767  }
768  if (b < rgbMin) {
769    rgbMin = b;
770  } else if (b > rgbMax) {
771    rgbMax = b;
772  }
773  return rgbMax - rgbMin;
774}
775
776static void clipColor(int rIn, int gIn, int bIn,
777                      Guchar *rOut, Guchar *gOut, Guchar *bOut) {
778  int lum, rgbMin, rgbMax;
779
780  lum = getLum(rIn, gIn, bIn);
781  rgbMin = rgbMax = rIn;
782  if (gIn < rgbMin) {
783    rgbMin = gIn;
784  } else if (gIn > rgbMax) {
785    rgbMax = gIn;
786  }
787  if (bIn < rgbMin) {
788    rgbMin = bIn;
789  } else if (bIn > rgbMax) {
790    rgbMax = bIn;
791  }
792  if (rgbMin < 0) {
793    *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin));
794    *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin));
795    *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin));
796  } else if (rgbMax > 255) {
797    *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum));
798    *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum));
799    *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum));
800  } else {
801    *rOut = rIn;
802    *gOut = gIn;
803    *bOut = bIn;
804  }
805}
806
807static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
808                   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
809  int d;
810
811  d = lum - getLum(rIn, gIn, bIn);
812  clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
813}
814
815static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
816                   Guchar *rOut, Guchar *gOut, Guchar *bOut) {
817  int rgbMin, rgbMid, rgbMax;
818  Guchar *minOut, *midOut, *maxOut;
819
820  if (rIn < gIn) {
821    rgbMin = rIn;  minOut = rOut;
822    rgbMid = gIn;  midOut = gOut;
823  } else {
824    rgbMin = gIn;  minOut = gOut;
825    rgbMid = rIn;  midOut = rOut;
826  }
827  if (bIn > rgbMid) {
828    rgbMax = bIn;  maxOut = bOut;
829  } else if (bIn > rgbMin) {
830    rgbMax = rgbMid;  maxOut = midOut;
831    rgbMid = bIn;     midOut = bOut;
832  } else {
833    rgbMax = rgbMid;  maxOut = midOut;
834    rgbMid = rgbMin;  midOut = minOut;
835    rgbMin = bIn;     minOut = bOut;
836  }
837  if (rgbMax > rgbMin) {
838    *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin);
839    *maxOut = (Guchar)sat;
840  } else {
841    *midOut = *maxOut = 0;
842  }
843  *minOut = 0;
844}
845
846static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
847                              SplashColorPtr blend, SplashColorMode cm) {
848  Guchar r0, g0, b0;
849#ifdef SPLASH_CMYK
850  Guchar r1, g1, b1;
851#endif
852
853  switch (cm) {
854  case splashModeMono1:
855  case splashModeMono8:
856    blend[0] = dest[0];
857    break;
858  case splashModeXBGR8:
859    src[3] = 255;
860  case splashModeRGB8:
861  case splashModeBGR8:
862    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
863           &r0, &g0, &b0);
864    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
865           &blend[0], &blend[1], &blend[2]);
866    break;
867#if SPLASH_CMYK
868  case splashModeCMYK8:
869    // NB: inputs have already been converted to additive mode
870    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
871           &r0, &g0, &b0);
872    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
873           &r1, &g1, &b1);
874    blend[0] = r1;
875    blend[1] = g1;
876    blend[2] = b1;
877    blend[3] = dest[3];
878    break;
879#endif
880  }
881}
882
883static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
884                                     SplashColorPtr blend,
885                                     SplashColorMode cm) {
886  Guchar r0, g0, b0;
887#ifdef SPLASH_CMYK
888  Guchar r1, g1, b1;
889#endif
890
891  switch (cm) {
892  case splashModeMono1:
893  case splashModeMono8:
894    blend[0] = dest[0];
895    break;
896  case splashModeXBGR8:
897    src[3] = 255;
898  case splashModeRGB8:
899  case splashModeBGR8:
900    setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
901           &r0, &g0, &b0);
902    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
903           &blend[0], &blend[1], &blend[2]);
904    break;
905#if SPLASH_CMYK
906  case splashModeCMYK8:
907    // NB: inputs have already been converted to additive mode
908    setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
909           &r0, &g0, &b0);
910    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
911           &r1, &g1, &b1);
912    blend[0] = r1;
913    blend[1] = g1;
914    blend[2] = b1;
915    blend[3] = dest[3];
916    break;
917#endif
918  }
919}
920
921static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
922                                SplashColorPtr blend, SplashColorMode cm) {
923#if SPLASH_CMYK
924  Guchar r, g, b;
925#endif
926
927  switch (cm) {
928  case splashModeMono1:
929  case splashModeMono8:
930    blend[0] = dest[0];
931    break;
932  case splashModeXBGR8:
933    src[3] = 255;
934  case splashModeRGB8:
935  case splashModeBGR8:
936    setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
937           &blend[0], &blend[1], &blend[2]);
938    break;
939#if SPLASH_CMYK
940  case splashModeCMYK8:
941    // NB: inputs have already been converted to additive mode
942    setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
943           &r, &g, &b);
944    blend[0] = r;
945    blend[1] = g;
946    blend[2] = b;
947    blend[3] = dest[3];
948    break;
949#endif
950  }
951}
952
953static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
954                                     SplashColorPtr blend,
955                                     SplashColorMode cm) {
956#if SPLASH_CMYK
957  Guchar r, g, b;
958#endif
959
960  switch (cm) {
961  case splashModeMono1:
962  case splashModeMono8:
963    blend[0] = dest[0];
964    break;
965  case splashModeXBGR8:
966    src[3] = 255;
967  case splashModeRGB8:
968  case splashModeBGR8:
969    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
970           &blend[0], &blend[1], &blend[2]);
971    break;
972#if SPLASH_CMYK
973  case splashModeCMYK8:
974    // NB: inputs have already been converted to additive mode
975    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
976           &r, &g, &b);
977    blend[0] = r;
978    blend[1] = g;
979    blend[2] = b;
980    blend[3] = src[3];
981    break;
982#endif
983  }
984}
985
986// NB: This must match the GfxBlendMode enum defined in GfxState.h.
987static const SplashBlendFunc splashOutBlendFuncs[] = {
988  NULL,
989  &splashOutBlendMultiply,
990  &splashOutBlendScreen,
991  &splashOutBlendOverlay,
992  &splashOutBlendDarken,
993  &splashOutBlendLighten,
994  &splashOutBlendColorDodge,
995  &splashOutBlendColorBurn,
996  &splashOutBlendHardLight,
997  &splashOutBlendSoftLight,
998  &splashOutBlendDifference,
999  &splashOutBlendExclusion,
1000  &splashOutBlendHue,
1001  &splashOutBlendSaturation,
1002  &splashOutBlendColor,
1003  &splashOutBlendLuminosity
1004};
1005
1006//------------------------------------------------------------------------
1007// SplashOutFontFileID
1008//------------------------------------------------------------------------
1009
1010class SplashOutFontFileID: public SplashFontFileID {
1011public:
1012
1013  SplashOutFontFileID(Ref *rA) { r = *rA; }
1014
1015  ~SplashOutFontFileID() {}
1016
1017  GBool matches(SplashFontFileID *id) {
1018    return ((SplashOutFontFileID *)id)->r.num == r.num &&
1019           ((SplashOutFontFileID *)id)->r.gen == r.gen;
1020  }
1021
1022private:
1023
1024  Ref r;
1025};
1026
1027//------------------------------------------------------------------------
1028// T3FontCache
1029//------------------------------------------------------------------------
1030
1031struct T3FontCacheTag {
1032  Gushort code;
1033  Gushort mru;                  // valid bit (0x8000) and MRU index
1034};
1035
1036class T3FontCache {
1037public:
1038
1039  T3FontCache(Ref *fontID, double m11A, double m12A,
1040              double m21A, double m22A,
1041              int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1042              GBool aa, GBool validBBoxA);
1043  ~T3FontCache();
1044  GBool matches(Ref *idA, double m11A, double m12A,
1045                double m21A, double m22A)
1046    { return fontID.num == idA->num && fontID.gen == idA->gen &&
1047             m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1048
1049  Ref fontID;                   // PDF font ID
1050  double m11, m12, m21, m22;    // transform matrix
1051  int glyphX, glyphY;           // pixel offset of glyph bitmaps
1052  int glyphW, glyphH;           // size of glyph bitmaps, in pixels
1053  GBool validBBox;              // false if the bbox was [0 0 0 0]
1054  int glyphSize;                // size of glyph bitmaps, in bytes
1055  int cacheSets;                // number of sets in cache
1056  int cacheAssoc;               // cache associativity (glyphs per set)
1057  Guchar *cacheData;            // glyph pixmap cache
1058  T3FontCacheTag *cacheTags;    // cache tags, i.e., char codes
1059};
1060
1061T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1062                         double m21A, double m22A,
1063                         int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1064                         GBool validBBoxA, GBool aa) {
1065  int i;
1066
1067  fontID = *fontIDA;
1068  m11 = m11A;
1069  m12 = m12A;
1070  m21 = m21A;
1071  m22 = m22A;
1072  glyphX = glyphXA;
1073  glyphY = glyphYA;
1074  glyphW = glyphWA;
1075  glyphH = glyphHA;
1076  validBBox = validBBoxA;
1077  // sanity check for excessively large glyphs (which most likely
1078  // indicate an incorrect BBox)
1079  i = glyphW * glyphH;
1080  if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
1081    glyphW = glyphH = 100;
1082    validBBox = gFalse;
1083  }
1084  if (aa) {
1085    glyphSize = glyphW * glyphH;
1086  } else {
1087    glyphSize = ((glyphW + 7) >> 3) * glyphH;
1088  }
1089  cacheAssoc = type3FontCacheAssoc;
1090  for (cacheSets = type3FontCacheMaxSets;
1091       cacheSets > 1 &&
1092         cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
1093       cacheSets >>= 1) ;
1094  if (glyphSize < 10485760 / cacheAssoc / cacheSets) {
1095    cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
1096  } else {
1097    error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n"
1098              "       This could teoretically result in wrong rendering,\n"
1099              "       but most probably the document is bogus.\n"
1100              "       Please report a bug if you think the rendering may be wrong because of this.");
1101    cacheData = NULL;
1102  }
1103  if (cacheData != NULL)
1104  {
1105    cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
1106                                         sizeof(T3FontCacheTag));
1107    for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1108      cacheTags[i].mru = i & (cacheAssoc - 1);
1109    }
1110  }
1111  else
1112  {
1113    cacheTags = NULL;
1114  }
1115}
1116
1117T3FontCache::~T3FontCache() {
1118  gfree(cacheData);
1119  gfree(cacheTags);
1120}
1121
1122struct T3GlyphStack {
1123  Gushort code;                 // character code
1124
1125  //----- cache info
1126  T3FontCache *cache;           // font cache for the current font
1127  T3FontCacheTag *cacheTag;     // pointer to cache tag for the glyph
1128  Guchar *cacheData;            // pointer to cache data for the glyph
1129
1130  //----- saved state
1131  SplashBitmap *origBitmap;
1132  Splash *origSplash;
1133  double origCTM4, origCTM5;
1134
1135  T3GlyphStack *next;           // next object on stack
1136};
1137
1138//------------------------------------------------------------------------
1139// SplashTransparencyGroup
1140//------------------------------------------------------------------------
1141
1142struct SplashTransparencyGroup {
1143  int tx, ty;                   // translation coordinates
1144  SplashBitmap *tBitmap;        // bitmap for transparency group
1145  GfxColorSpace *blendingColorSpace;
1146  GBool isolated;
1147
1148  //----- for knockout
1149  SplashBitmap *shape;
1150  GBool knockout;
1151  SplashCoord knockoutOpacity;
1152
1153  //----- saved state
1154  SplashBitmap *origBitmap;
1155  Splash *origSplash;
1156
1157  SplashTransparencyGroup *next;
1158};
1159
1160//------------------------------------------------------------------------
1161// SplashOutputDev
1162//------------------------------------------------------------------------
1163
1164SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
1165                                 int bitmapRowPadA,
1166                                 GBool reverseVideoA,
1167                                 SplashColorPtr paperColorA,
1168                                 GBool bitmapTopDownA,
1169                                 GBool allowAntialiasA) {
1170  colorMode = colorModeA;
1171  bitmapRowPad = bitmapRowPadA;
1172  bitmapTopDown = bitmapTopDownA;
1173  bitmapUpsideDown = gFalse;
1174  allowAntialias = allowAntialiasA;
1175  vectorAntialias = allowAntialias &&
1176                      globalParams->getVectorAntialias() &&
1177                      colorMode != splashModeMono1;
1178  enableFreeTypeHinting = gFalse;
1179  enableSlightHinting = gFalse;
1180  setupScreenParams(72.0, 72.0);
1181  reverseVideo = reverseVideoA;
1182  if (paperColorA != NULL) {
1183    splashColorCopy(paperColor, paperColorA);
1184  } else {
1185    splashClearColor(paperColor);
1186  }
1187  skipHorizText = gFalse;
1188  skipRotatedText = gFalse;
1189  keepAlphaChannel = paperColorA == NULL;
1190
1191  doc = NULL;
1192
1193  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
1194                            colorMode != splashModeMono1, bitmapTopDown);
1195  splash = new Splash(bitmap, vectorAntialias, &screenParams);
1196  splash->setMinLineWidth(globalParams->getMinLineWidth());
1197  splash->clear(paperColor, 0);
1198
1199  fontEngine = NULL;
1200
1201  nT3Fonts = 0;
1202  t3GlyphStack = NULL;
1203
1204  font = NULL;
1205  needFontUpdate = gFalse;
1206  textClipPath = NULL;
1207  transpGroupStack = NULL;
1208  nestCount = 0;
1209}
1210
1211void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
1212  screenParams.size = globalParams->getScreenSize();
1213  screenParams.dotRadius = globalParams->getScreenDotRadius();
1214  screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
1215  screenParams.blackThreshold =
1216      (SplashCoord)globalParams->getScreenBlackThreshold();
1217  screenParams.whiteThreshold =
1218      (SplashCoord)globalParams->getScreenWhiteThreshold();
1219  switch (globalParams->getScreenType()) {
1220  case screenDispersed:
1221    screenParams.type = splashScreenDispersed;
1222    if (screenParams.size < 0) {
1223      screenParams.size = 4;
1224    }
1225    break;
1226  case screenClustered:
1227    screenParams.type = splashScreenClustered;
1228    if (screenParams.size < 0) {
1229      screenParams.size = 10;
1230    }
1231    break;
1232  case screenStochasticClustered:
1233    screenParams.type = splashScreenStochasticClustered;
1234    if (screenParams.size < 0) {
1235      screenParams.size = 64;
1236    }
1237    if (screenParams.dotRadius < 0) {
1238      screenParams.dotRadius = 2;
1239    }
1240    break;
1241  case screenUnset:
1242  default:
1243    // use clustered dithering for resolution >= 300 dpi
1244    // (compare to 299.9 to avoid floating point issues)
1245    if (hDPI > 299.9 && vDPI > 299.9) {
1246      screenParams.type = splashScreenStochasticClustered;
1247      if (screenParams.size < 0) {
1248        screenParams.size = 64;
1249      }
1250      if (screenParams.dotRadius < 0) {
1251        screenParams.dotRadius = 2;
1252      }
1253    } else {
1254      screenParams.type = splashScreenDispersed;
1255      if (screenParams.size < 0) {
1256        screenParams.size = 4;
1257      }
1258    }
1259  }
1260}
1261
1262SplashOutputDev::~SplashOutputDev() {
1263  int i;
1264
1265  for (i = 0; i < nT3Fonts; ++i) {
1266    delete t3FontCache[i];
1267  }
1268  if (fontEngine) {
1269    delete fontEngine;
1270  }
1271  if (splash) {
1272    delete splash;
1273  }
1274  if (bitmap) {
1275    delete bitmap;
1276  }
1277}
1278
1279void SplashOutputDev::startDoc(PDFDoc *docA) {
1280  int i;
1281
1282  doc = docA;
1283  if (fontEngine) {
1284    delete fontEngine;
1285  }
1286  fontEngine = new SplashFontEngine(
1287#if HAVE_T1LIB_H
1288                                    globalParams->getEnableT1lib(),
1289#endif
1290#if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
1291                                    globalParams->getEnableFreeType(),
1292                                    enableFreeTypeHinting,
1293                                    enableSlightHinting,
1294#endif
1295                                    allowAntialias &&
1296                                      globalParams->getAntialias() &&
1297                                      colorMode != splashModeMono1);
1298  for (i = 0; i < nT3Fonts; ++i) {
1299    delete t3FontCache[i];
1300  }
1301  nT3Fonts = 0;
1302}
1303
1304void SplashOutputDev::startPage(int pageNum, GfxState *state) {
1305  int w, h;
1306  double *ctm;
1307  SplashCoord mat[6];
1308  SplashColor color;
1309
1310  if (state) {
1311    setupScreenParams(state->getHDPI(), state->getVDPI());
1312    w = (int)(state->getPageWidth() + 0.5);
1313    if (w <= 0) {
1314      w = 1;
1315    }
1316    h = (int)(state->getPageHeight() + 0.5);
1317    if (h <= 0) {
1318      h = 1;
1319    }
1320  } else {
1321    w = h = 1;
1322  }
1323  if (splash) {
1324    delete splash;
1325    splash = NULL;
1326  }
1327  if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
1328    if (bitmap) {
1329      delete bitmap;
1330      bitmap = NULL;
1331    }
1332    bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1333                              colorMode != splashModeMono1, bitmapTopDown);
1334  }
1335  splash = new Splash(bitmap, vectorAntialias, &screenParams);
1336  splash->setMinLineWidth(globalParams->getMinLineWidth());
1337  if (state) {
1338    ctm = state->getCTM();
1339    mat[0] = (SplashCoord)ctm[0];
1340    mat[1] = (SplashCoord)ctm[1];
1341    mat[2] = (SplashCoord)ctm[2];
1342    mat[3] = (SplashCoord)ctm[3];
1343    mat[4] = (SplashCoord)ctm[4];
1344    mat[5] = (SplashCoord)ctm[5];
1345    splash->setMatrix(mat);
1346  }
1347  switch (colorMode) {
1348  case splashModeMono1:
1349  case splashModeMono8:
1350    color[0] = 0;
1351    break;
1352  case splashModeXBGR8:
1353    color[3] = 255;
1354  case splashModeRGB8:
1355  case splashModeBGR8:
1356    color[0] = color[1] = color[2] = 0;
1357    break;
1358#if SPLASH_CMYK
1359  case splashModeCMYK8:
1360    color[0] = color[1] = color[2] = color[3] = 0;
1361    break;
1362#endif
1363  }
1364  splash->setStrokePattern(new SplashSolidColor(color));
1365  splash->setFillPattern(new SplashSolidColor(color));
1366  splash->setLineCap(splashLineCapButt);
1367  splash->setLineJoin(splashLineJoinMiter);
1368  splash->setLineDash(NULL, 0, 0);
1369  splash->setMiterLimit(10);
1370  splash->setFlatness(1);
1371  // the SA parameter supposedly defaults to false, but Acrobat
1372  // apparently hardwires it to true
1373  splash->setStrokeAdjust(globalParams->getStrokeAdjust());
1374  splash->clear(paperColor, 0);
1375}
1376
1377void SplashOutputDev::endPage() {
1378  if (colorMode != splashModeMono1 && !keepAlphaChannel) {
1379    splash->compositeBackground(paperColor);
1380  }
1381}
1382
1383void SplashOutputDev::saveState(GfxState *state) {
1384  splash->saveState();
1385}
1386
1387void SplashOutputDev::restoreState(GfxState *state) {
1388  splash->restoreState();
1389  needFontUpdate = gTrue;
1390}
1391
1392void SplashOutputDev::updateAll(GfxState *state) {
1393  updateLineDash(state);
1394  updateLineJoin(state);
1395  updateLineCap(state);
1396  updateLineWidth(state);
1397  updateFlatness(state);
1398  updateMiterLimit(state);
1399  updateStrokeAdjust(state);
1400  updateFillColor(state);
1401  updateStrokeColor(state);
1402  needFontUpdate = gTrue;
1403}
1404
1405void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
1406                                double m21, double m22,
1407                                double m31, double m32) {
1408  double *ctm;
1409  SplashCoord mat[6];
1410
1411  ctm = state->getCTM();
1412  mat[0] = (SplashCoord)ctm[0];
1413  mat[1] = (SplashCoord)ctm[1];
1414  mat[2] = (SplashCoord)ctm[2];
1415  mat[3] = (SplashCoord)ctm[3];
1416  mat[4] = (SplashCoord)ctm[4];
1417  mat[5] = (SplashCoord)ctm[5];
1418  splash->setMatrix(mat);
1419}
1420
1421void SplashOutputDev::updateLineDash(GfxState *state) {
1422  double *dashPattern;
1423  int dashLength;
1424  double dashStart;
1425  SplashCoord dash[20];
1426  int i;
1427
1428  state->getLineDash(&dashPattern, &dashLength, &dashStart);
1429  if (dashLength > 20) {
1430    dashLength = 20;
1431  }
1432  for (i = 0; i < dashLength; ++i) {
1433    dash[i] = (SplashCoord)dashPattern[i];
1434    if (dash[i] < 0) {
1435      dash[i] = 0;
1436    }
1437  }
1438  splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
1439}
1440
1441void SplashOutputDev::updateFlatness(GfxState *state) {
1442#if 0 // Acrobat ignores the flatness setting, and always renders curves
1443      // with a fairly small flatness value
1444   splash->setFlatness(state->getFlatness());
1445#endif
1446}
1447
1448void SplashOutputDev::updateLineJoin(GfxState *state) {
1449  splash->setLineJoin(state->getLineJoin());
1450}
1451
1452void SplashOutputDev::updateLineCap(GfxState *state) {
1453  splash->setLineCap(state->getLineCap());
1454}
1455
1456void SplashOutputDev::updateMiterLimit(GfxState *state) {
1457  splash->setMiterLimit(state->getMiterLimit());
1458}
1459
1460void SplashOutputDev::updateLineWidth(GfxState *state) {
1461  splash->setLineWidth(state->getLineWidth());
1462}
1463
1464void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) {
1465#if 0 // the SA parameter supposedly defaults to false, but Acrobat
1466      // apparently hardwires it to true
1467  splash->setStrokeAdjust(state->getStrokeAdjust());
1468#endif
1469}
1470
1471void SplashOutputDev::updateFillColor(GfxState *state) {
1472  GfxGray gray;
1473  GfxRGB rgb;
1474#if SPLASH_CMYK
1475  GfxCMYK cmyk;
1476#endif
1477
1478  switch (colorMode) {
1479  case splashModeMono1:
1480  case splashModeMono8:
1481    state->getFillGray(&gray);
1482    splash->setFillPattern(getColor(gray));
1483    break;
1484  case splashModeXBGR8:
1485  case splashModeRGB8:
1486  case splashModeBGR8:
1487    state->getFillRGB(&rgb);
1488    splash->setFillPattern(getColor(&rgb));
1489    break;
1490#if SPLASH_CMYK
1491  case splashModeCMYK8:
1492    state->getFillCMYK(&cmyk);
1493    splash->setFillPattern(getColor(&cmyk));
1494    break;
1495#endif
1496  }
1497}
1498
1499void SplashOutputDev::updateStrokeColor(GfxState *state) {
1500  GfxGray gray;
1501  GfxRGB rgb;
1502#if SPLASH_CMYK
1503  GfxCMYK cmyk;
1504#endif
1505
1506  switch (colorMode) {
1507  case splashModeMono1:
1508  case splashModeMono8:
1509    state->getStrokeGray(&gray);
1510    splash->setStrokePattern(getColor(gray));
1511    break;
1512  case splashModeXBGR8:
1513  case splashModeRGB8:
1514  case splashModeBGR8:
1515    state->getStrokeRGB(&rgb);
1516    splash->setStrokePattern(getColor(&rgb));
1517    break;
1518#if SPLASH_CMYK
1519  case splashModeCMYK8:
1520    state->getStrokeCMYK(&cmyk);
1521    splash->setStrokePattern(getColor(&cmyk));
1522    break;
1523#endif
1524  }
1525}
1526
1527SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
1528  SplashColor color;
1529
1530  if (reverseVideo) {
1531    gray = gfxColorComp1 - gray;
1532  }
1533  color[0] = colToByte(gray);
1534  return new SplashSolidColor(color);
1535}
1536
1537SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
1538  GfxColorComp r, g, b;
1539  SplashColor color;
1540
1541  if (reverseVideo) {
1542    r = gfxColorComp1 - rgb->r;
1543    g = gfxColorComp1 - rgb->g;
1544    b = gfxColorComp1 - rgb->b;
1545  } else {
1546    r = rgb->r;
1547    g = rgb->g;
1548    b = rgb->b;
1549  }
1550  color[0] = colToByte(r);
1551  color[1] = colToByte(g);
1552  color[2] = colToByte(b);
1553  if (colorMode == splashModeXBGR8) color[3] = 255;
1554  return new SplashSolidColor(color);
1555}
1556
1557#if SPLASH_CMYK
1558SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
1559  SplashColor color;
1560
1561  color[0] = colToByte(cmyk->c);
1562  color[1] = colToByte(cmyk->m);
1563  color[2] = colToByte(cmyk->y);
1564  color[3] = colToByte(cmyk->k);
1565  return new SplashSolidColor(color);
1566}
1567#endif
1568
1569void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
1570                                       GBool overprintFlag,
1571                                       int overprintMode,
1572                                       GfxColor *singleColor,
1573                                       GBool grayIndexed) {
1574#if SPLASH_CMYK
1575  Guint mask;
1576  GfxCMYK cmyk;
1577  GBool additive = gFalse;
1578  int i;
1579
1580  if (colorSpace->getMode() == csIndexed) {
1581    setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(),
1582                     overprintFlag,
1583                     overprintMode,
1584                     singleColor,
1585                     grayIndexed);
1586                return;
1587        }
1588  if (overprintFlag && globalParams->getOverprintPreview()) {
1589    mask = colorSpace->getOverprintMask();
1590    if (singleColor && overprintMode &&
1591        colorSpace->getMode() == csDeviceCMYK) {
1592      colorSpace->getCMYK(singleColor, &cmyk);
1593      if (cmyk.c == 0) {
1594        mask &= ~1;
1595      }
1596      if (cmyk.m == 0) {
1597        mask &= ~2;
1598      }
1599      if (cmyk.y == 0) {
1600        mask &= ~4;
1601      }
1602      if (cmyk.k == 0) {
1603        mask &= ~8;
1604      }
1605    }
1606    if (grayIndexed) {
1607      mask &= ~7;
1608    } else if (colorSpace->getMode() == csSeparation) {
1609      GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace;
1610      additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking();
1611    } else if (colorSpace->getMode() == csDeviceN) {
1612      GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
1613      additive = mask == 0x0f && !deviceNCS->isNonMarking();
1614      for (i = 0; i < deviceNCS->getNComps() && additive; i++) {
1615        if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) {
1616          additive = gFalse;
1617        } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) {
1618          additive = gFalse;
1619        } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) {
1620          additive = gFalse;
1621        } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) {
1622          additive = gFalse;
1623        }
1624      }
1625    }
1626  } else {
1627    mask = 0xffffffff;
1628  }
1629  splash->setOverprintMask(mask, additive);
1630#endif
1631}
1632
1633void SplashOutputDev::updateBlendMode(GfxState *state) {
1634  splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1635}
1636
1637void SplashOutputDev::updateFillOpacity(GfxState *state) {
1638  splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1639  if (transpGroupStack != NULL && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) {
1640    transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity();
1641  }
1642}
1643
1644void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1645  splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1646  if (transpGroupStack != NULL && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) {
1647    transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity();
1648  }
1649}
1650
1651void SplashOutputDev::updateFillOverprint(GfxState *state) {
1652  splash->setFillOverprint(state->getFillOverprint());
1653}
1654
1655void SplashOutputDev::updateStrokeOverprint(GfxState *state) {
1656  splash->setStrokeOverprint(state->getStrokeOverprint());
1657}
1658
1659void SplashOutputDev::updateOverprintMode(GfxState *state) {
1660  splash->setOverprintMode(state->getOverprintMode());
1661}
1662
1663void SplashOutputDev::updateTransfer(GfxState *state) {
1664  Function **transfer;
1665  Guchar red[256], green[256], blue[256], gray[256];
1666  double x, y;
1667  int i;
1668
1669  transfer = state->getTransfer();
1670  if (transfer[0] &&
1671      transfer[0]->getInputSize() == 1 &&
1672      transfer[0]->getOutputSize() == 1) {
1673    if (transfer[1] &&
1674        transfer[1]->getInputSize() == 1 &&
1675        transfer[1]->getOutputSize() == 1 &&
1676        transfer[2] &&
1677        transfer[2]->getInputSize() == 1 &&
1678        transfer[2]->getOutputSize() == 1 &&
1679        transfer[3] &&
1680        transfer[3]->getInputSize() == 1 &&
1681        transfer[3]->getOutputSize() == 1) {
1682      for (i = 0; i < 256; ++i) {
1683        x = i / 255.0;
1684        transfer[0]->transform(&x, &y);
1685        red[i] = (Guchar)(y * 255.0 + 0.5);
1686        transfer[1]->transform(&x, &y);
1687        green[i] = (Guchar)(y * 255.0 + 0.5);
1688        transfer[2]->transform(&x, &y);
1689        blue[i] = (Guchar)(y * 255.0 + 0.5);
1690        transfer[3]->transform(&x, &y);
1691        gray[i] = (Guchar)(y * 255.0 + 0.5);
1692      }
1693    } else {
1694      for (i = 0; i < 256; ++i) {
1695        x = i / 255.0;
1696        transfer[0]->transform(&x, &y);
1697        red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1698      }
1699    }
1700  } else {
1701    for (i = 0; i < 256; ++i) {
1702      red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1703    }
1704  }
1705  splash->setTransfer(red, green, blue, gray);
1706}
1707
1708void SplashOutputDev::updateFont(GfxState * /*state*/) {
1709  needFontUpdate = gTrue;
1710}
1711
1712void SplashOutputDev::doUpdateFont(GfxState *state) {
1713  GfxFont *gfxFont;
1714  GfxFontLoc *fontLoc;
1715  GfxFontType fontType;
1716  SplashOutFontFileID *id;
1717  SplashFontFile *fontFile;
1718  SplashFontSrc *fontsrc = NULL;
1719  FoFiTrueType *ff;
1720  Object refObj, strObj;
1721  GooString *fileName;
1722  char *tmpBuf;
1723  int tmpBufLen;
1724  int *codeToGID;
1725  double *textMat;
1726  double m11, m12, m21, m22, fontSize;
1727  int faceIndex = 0;
1728  SplashCoord mat[4];
1729  int n, i;
1730  GBool recreateFont = gFalse;
1731  GBool doAdjustFontMatrix = gFalse;
1732
1733  needFontUpdate = gFalse;
1734  font = NULL;
1735  fileName = NULL;
1736  tmpBuf = NULL;
1737  fontLoc = NULL;
1738
1739  if (!(gfxFont = state->getFont())) {
1740    goto err1;
1741  }
1742  fontType = gfxFont->getType();
1743  if (fontType == fontType3) {
1744    goto err1;
1745  }
1746
1747  // sanity-check the font size - skip anything larger than 10 inches
1748  // (this avoids problems allocating memory for the font cache)
1749  if (state->getTransformedFontSize()
1750        > 10 * (state->getHDPI() + state->getVDPI())) {
1751    goto err1;
1752  }
1753
1754  // check the font file cache
1755  id = new SplashOutFontFileID(gfxFont->getID());
1756  if ((fontFile = fontEngine->getFontFile(id))) {
1757    delete id;
1758
1759  } else {
1760
1761    if (!(fontLoc = gfxFont->locateFont(doc->getXRef(), gFalse))) {
1762      error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1763            gfxFont->getName() ? gfxFont->getName()->getCString()
1764                               : "(unnamed)");
1765      goto err2;
1766    }
1767
1768    // embedded font
1769    if (fontLoc->locType == gfxFontLocEmbedded) {
1770      // if there is an embedded font, read it to memory
1771      tmpBuf = gfxFont->readEmbFontFile(doc->getXRef(), &tmpBufLen);
1772      if (! tmpBuf)
1773        goto err2;
1774
1775    // external font
1776    } else { // gfxFontLocExternal
1777      fileName = fontLoc->path;
1778      fontType = fontLoc->fontType;
1779      doAdjustFontMatrix = gTrue;
1780    }
1781
1782    fontsrc = new SplashFontSrc;
1783    if (fileName)
1784      fontsrc->setFile(fileName, gFalse);
1785    else
1786      fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue);
1787
1788    // load the font file
1789    switch (fontType) {
1790    case fontType1:
1791      if (!(fontFile = fontEngine->loadType1Font(
1792                           id,
1793                           fontsrc,
1794                           (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1795        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1796              gfxFont->getName() ? gfxFont->getName()->getCString()
1797                                 : "(unnamed)");
1798        goto err2;
1799      }
1800      break;
1801    case fontType1C:
1802      if (!(fontFile = fontEngine->loadType1CFont(
1803                           id,
1804                           fontsrc,
1805                           (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1806        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1807              gfxFont->getName() ? gfxFont->getName()->getCString()
1808                                 : "(unnamed)");
1809        goto err2;
1810      }
1811      break;
1812    case fontType1COT:
1813      if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
1814                           id,
1815                           fontsrc,
1816                           (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1817        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1818              gfxFont->getName() ? gfxFont->getName()->getCString()
1819                                 : "(unnamed)");
1820        goto err2;
1821      }
1822      break;
1823    case fontTrueType:
1824    case fontTrueTypeOT:
1825        if (fileName)
1826         ff = FoFiTrueType::load(fileName->getCString());
1827        else
1828        ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
1829      if (ff) {
1830        codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1831        n = 256;
1832        delete ff;
1833        // if we're substituting for a non-TrueType font, we need to mark
1834        // all notdef codes as "do not draw" (rather than drawing TrueType
1835        // notdef glyphs)
1836        if (gfxFont->getType() != fontTrueType &&
1837            gfxFont->getType() != fontTrueTypeOT) {
1838          for (i = 0; i < 256; ++i) {
1839            if (codeToGID[i] == 0) {
1840              codeToGID[i] = -1;
1841            }
1842          }
1843        }
1844      } else {
1845        codeToGID = NULL;
1846        n = 0;
1847      }
1848      if (!(fontFile = fontEngine->loadTrueTypeFont(
1849                           id,
1850                           fontsrc,
1851                           codeToGID, n))) {
1852        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1853              gfxFont->getName() ? gfxFont->getName()->getCString()
1854                                 : "(unnamed)");
1855        goto err2;
1856      }
1857      break;
1858    case fontCIDType0:
1859    case fontCIDType0C:
1860      if (!(fontFile = fontEngine->loadCIDFont(
1861                           id,
1862                           fontsrc))) {
1863        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1864              gfxFont->getName() ? gfxFont->getName()->getCString()
1865                                 : "(unnamed)");
1866        goto err2;
1867      }
1868      break;
1869    case fontCIDType0COT:
1870      if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1871        n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1872        codeToGID = (int *)gmallocn(n, sizeof(int));
1873        memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1874               n * sizeof(int));
1875      } else {
1876        codeToGID = NULL;
1877        n = 0;
1878      }
1879      if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
1880                           id,
1881                           fontsrc,
1882                           codeToGID, n))) {
1883        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1884              gfxFont->getName() ? gfxFont->getName()->getCString()
1885                                 : "(unnamed)");
1886        goto err2;
1887      }
1888      break;
1889    case fontCIDType2:
1890    case fontCIDType2OT:
1891      codeToGID = NULL;
1892      n = 0;
1893      if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1894        n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1895        if (n) {
1896          codeToGID = (int *)gmallocn(n, sizeof(int));
1897          memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1898                  n * sizeof(int));
1899        }
1900      } else {
1901        if (fileName)
1902          ff = FoFiTrueType::load(fileName->getCString());
1903        else
1904          ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
1905        if (! ff)
1906        {
1907        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1908              gfxFont->getName() ? gfxFont->getName()->getCString()
1909                                 : "(unnamed)");
1910          goto err2;
1911        }
1912        codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
1913        delete ff;
1914      }
1915      if (!(fontFile = fontEngine->loadTrueTypeFont(
1916                           id,
1917                           fontsrc,
1918                           codeToGID, n, faceIndex))) {
1919        error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1920              gfxFont->getName() ? gfxFont->getName()->getCString()
1921                                 : "(unnamed)");
1922        goto err2;
1923      }
1924      break;
1925    default:
1926      // this shouldn't happen
1927      goto err2;
1928    }
1929    fontFile->doAdjustMatrix = doAdjustFontMatrix;
1930  }
1931
1932  // get the font matrix
1933  textMat = state->getTextMat();
1934  fontSize = state->getFontSize();
1935  m11 = textMat[0] * fontSize * state->getHorizScaling();
1936  m12 = textMat[1] * fontSize * state->getHorizScaling();
1937  m21 = textMat[2] * fontSize;
1938  m22 = textMat[3] * fontSize;
1939
1940  // create the scaled font
1941  mat[0] = m11;  mat[1] = m12;
1942  mat[2] = m21;  mat[3] = m22;
1943  font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1944
1945  // for substituted fonts: adjust the font matrix -- compare the
1946  // width of 'm' in the original font and the substituted font
1947  if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) {
1948    double w1, w2;
1949    CharCode code;
1950    char *name;
1951    for (code = 0; code < 256; ++code) {
1952      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1953          name[0] == 'm' && name[1] == '\0') {
1954        break;
1955      }
1956    }
1957    if (code < 256) {
1958      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1959      w2 = font->getGlyphAdvance(code);
1960      if (!gfxFont->isSymbolic() && w2 > 0) {
1961        // if real font is substantially narrower than substituted
1962        // font, reduce the font size accordingly
1963        if (w1 > 0.01 && w1 < 0.9 * w2) {
1964          w1 /= w2;
1965          m11 *= w1;
1966          m21 *= w1;
1967          recreateFont = gTrue;
1968        }
1969      }
1970    }
1971  }
1972
1973  if (recreateFont)
1974  {
1975    mat[0] = m11;  mat[1] = m12;
1976    mat[2] = m21;  mat[3] = m22;
1977    font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1978  }
1979
1980  delete fontLoc;
1981  if (fontsrc && !fontsrc->isFile)
1982      fontsrc->unref();
1983  return;
1984
1985 err2:
1986  delete id;
1987  delete fontLoc;
1988 err1:
1989  if (fontsrc && !fontsrc->isFile)
1990      fontsrc->unref();
1991  return;
1992}
1993
1994void SplashOutputDev::stroke(GfxState *state) {
1995  SplashPath *path;
1996
1997  if (state->getStrokeColorSpace()->isNonMarking()) {
1998    return;
1999  }
2000  setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(),
2001                   state->getOverprintMode(), state->getStrokeColor());
2002  path = convertPath(state, state->getPath(), gFalse);
2003  splash->stroke(path);
2004  delete path;
2005}
2006
2007void SplashOutputDev::fill(GfxState *state) {
2008  SplashPath *path;
2009
2010  if (state->getFillColorSpace()->isNonMarking()) {
2011    return;
2012  }
2013  setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2014                   state->getOverprintMode(), state->getFillColor());
2015  path = convertPath(state, state->getPath(), gTrue);
2016  splash->fill(path, gFalse);
2017  delete path;
2018}
2019
2020void SplashOutputDev::eoFill(GfxState *state) {
2021  SplashPath *path;
2022
2023  if (state->getFillColorSpace()->isNonMarking()) {
2024    return;
2025  }
2026  setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2027                   state->getOverprintMode(), state->getFillColor());
2028  path = convertPath(state, state->getPath(), gTrue);
2029  splash->fill(path, gTrue);
2030  delete path;
2031}
2032
2033void SplashOutputDev::clip(GfxState *state) {
2034  SplashPath *path;
2035
2036  path = convertPath(state, state->getPath(), gTrue);
2037  splash->clipToPath(path, gFalse);
2038  delete path;
2039}
2040
2041void SplashOutputDev::eoClip(GfxState *state) {
2042  SplashPath *path;
2043
2044  path = convertPath(state, state->getPath(), gTrue);
2045  splash->clipToPath(path, gTrue);
2046  delete path;
2047}
2048
2049void SplashOutputDev::clipToStrokePath(GfxState *state) {
2050  SplashPath *path, *path2;
2051
2052  path = convertPath(state, state->getPath(), gFalse);
2053  path2 = splash->makeStrokePath(path, state->getLineWidth());
2054  delete path;
2055  splash->clipToPath(path2, gFalse);
2056  delete path2;
2057}
2058
2059SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
2060                                         GBool dropEmptySubpaths) {
2061  SplashPath *sPath;
2062  GfxSubpath *subpath;
2063  int n, i, j;
2064
2065  n = dropEmptySubpaths ? 1 : 0;
2066  sPath = new SplashPath();
2067  for (i = 0; i < path->getNumSubpaths(); ++i) {
2068    subpath = path->getSubpath(i);
2069    if (subpath->getNumPoints() > n) {
2070      sPath->moveTo((SplashCoord)subpath->getX(0),
2071                    (SplashCoord)subpath->getY(0));
2072      j = 1;
2073      while (j < subpath->getNumPoints()) {
2074        if (subpath->getCurve(j)) {
2075          sPath->curveTo((SplashCoord)subpath->getX(j),
2076                         (SplashCoord)subpath->getY(j),
2077                         (SplashCoord)subpath->getX(j+1),
2078                         (SplashCoord)subpath->getY(j+1),
2079                         (SplashCoord)subpath->getX(j+2),
2080                         (SplashCoord)subpath->getY(j+2));
2081          j += 3;
2082        } else {
2083          sPath->lineTo((SplashCoord)subpath->getX(j),
2084                        (SplashCoord)subpath->getY(j));
2085          ++j;
2086        }
2087      }
2088      if (subpath->isClosed()) {
2089        sPath->close();
2090      }
2091    }
2092  }
2093  return sPath;
2094}
2095
2096void SplashOutputDev::drawChar(GfxState *state, double x, double y,
2097                               double dx, double dy,
2098                               double originX, double originY,
2099                               CharCode code, int nBytes,
2100                               Unicode *u, int uLen) {
2101  SplashPath *path;
2102  int render;
2103  GBool doFill, doStroke, doClip, strokeAdjust;
2104  double m[4];
2105  GBool horiz;
2106
2107  if (skipHorizText || skipRotatedText) {
2108    state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2109    horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2110            fabs(m[2]) < 0.001 && m[3] < 0;
2111    if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2112      return;
2113    }
2114  }
2115
2116  // check for invisible text -- this is used by Acrobat Capture
2117  render = state->getRender();
2118  if (render == 3) {
2119    return;
2120  }
2121
2122  if (needFontUpdate) {
2123    doUpdateFont(state);
2124  }
2125  if (!font) {
2126    return;
2127  }
2128
2129  x -= originX;
2130  y -= originY;
2131
2132  doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
2133  doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
2134             !state->getStrokeColorSpace()->isNonMarking();
2135  doClip = render & 4;
2136
2137  path = NULL;
2138  if (doStroke || doClip) {
2139    if ((path = font->getGlyphPath(code))) {
2140      path->offset((SplashCoord)x, (SplashCoord)y);
2141    }
2142  }
2143
2144  // don't use stroke adjustment when stroking text -- the results
2145  // tend to be ugly (because characters with horizontal upper or
2146  // lower edges get misaligned relative to the other characters)
2147  strokeAdjust = gFalse; // make gcc happy
2148  if (doStroke) {
2149    strokeAdjust = splash->getStrokeAdjust();
2150    splash->setStrokeAdjust(gFalse);
2151  }
2152
2153  // fill and stroke
2154  if (doFill && doStroke) {
2155    if (path) {
2156      setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2157                       state->getOverprintMode(), state->getFillColor());
2158      splash->fill(path, gFalse);
2159      setOverprintMask(state->getStrokeColorSpace(),
2160                       state->getStrokeOverprint(),
2161                       state->getOverprintMode(),
2162                       state->getStrokeColor());
2163      splash->stroke(path);
2164    }
2165
2166  // fill
2167  } else if (doFill) {
2168    setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2169                     state->getOverprintMode(), state->getFillColor());
2170    splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
2171
2172  // stroke
2173  } else if (doStroke) {
2174    if (path) {
2175      setOverprintMask(state->getStrokeColorSpace(),
2176                       state->getStrokeOverprint(),
2177                       state->getOverprintMode(),
2178                       state->getStrokeColor());
2179      splash->stroke(path);
2180    }
2181  }
2182
2183  // clip
2184  if (doClip) {
2185    if (path) {
2186      if (textClipPath) {
2187        textClipPath->append(path);
2188      } else {
2189        textClipPath = path;
2190        path = NULL;
2191      }
2192    }
2193  }
2194
2195  if (doStroke) {
2196    splash->setStrokeAdjust(strokeAdjust);
2197  }
2198
2199  if (path) {
2200    delete path;
2201  }
2202}
2203
2204GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
2205                                      double dx, double dy,
2206                                      CharCode code, Unicode *u, int uLen) {
2207  GfxFont *gfxFont;
2208  Ref *fontID;
2209  double *ctm, *bbox;
2210  T3FontCache *t3Font;
2211  T3GlyphStack *t3gs;
2212  GBool validBBox;
2213  double m[4];
2214  GBool horiz;
2215  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2216  int i, j;
2217
2218  if (skipHorizText || skipRotatedText) {
2219    state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2220    horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2221            fabs(m[2]) < 0.001 && m[3] < 0;
2222    if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2223      return gTrue;
2224    }
2225  }
2226
2227  if (!(gfxFont = state->getFont())) {
2228    return gFalse;
2229  }
2230  fontID = gfxFont->getID();
2231  ctm = state->getCTM();
2232  state->transform(0, 0, &xt, &yt);
2233
2234  // is it the first (MRU) font in the cache?
2235  if (!(nT3Fonts > 0 &&
2236        t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2237
2238    // is the font elsewhere in the cache?
2239    for (i = 1; i < nT3Fonts; ++i) {
2240      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2241        t3Font = t3FontCache[i];
2242        for (j = i; j > 0; --j) {
2243          t3FontCache[j] = t3FontCache[j - 1];
2244        }
2245        t3FontCache[0] = t3Font;
2246        break;
2247      }
2248    }
2249    if (i >= nT3Fonts) {
2250
2251      // create new entry in the font cache
2252      if (nT3Fonts == splashOutT3FontCacheSize) {
2253        delete t3FontCache[nT3Fonts - 1];
2254        --nT3Fonts;
2255      }
2256      for (j = nT3Fonts; j > 0; --j) {
2257        t3FontCache[j] = t3FontCache[j - 1];
2258      }
2259      ++nT3Fonts;
2260      bbox = gfxFont->getFontBBox();
2261      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2262        // unspecified bounding box -- just take a guess
2263        xMin = xt - 5;
2264        xMax = xMin + 30;
2265        yMax = yt + 15;
2266        yMin = yMax - 45;
2267        validBBox = gFalse;
2268      } else {
2269        state->transform(bbox[0], bbox[1], &x1, &y1);
2270        xMin = xMax = x1;
2271        yMin = yMax = y1;
2272        state->transform(bbox[0], bbox[3], &x1, &y1);
2273        if (x1 < xMin) {
2274          xMin = x1;
2275        } else if (x1 > xMax) {
2276          xMax = x1;
2277        }
2278        if (y1 < yMin) {
2279          yMin = y1;
2280        } else if (y1 > yMax) {
2281          yMax = y1;
2282        }
2283        state->transform(bbox[2], bbox[1], &x1, &y1);
2284        if (x1 < xMin) {
2285          xMin = x1;
2286        } else if (x1 > xMax) {
2287          xMax = x1;
2288        }
2289        if (y1 < yMin) {
2290          yMin = y1;
2291        } else if (y1 > yMax) {
2292          yMax = y1;
2293        }
2294        state->transform(bbox[2], bbox[3], &x1, &y1);
2295        if (x1 < xMin) {
2296          xMin = x1;
2297        } else if (x1 > xMax) {
2298          xMax = x1;
2299        }
2300        if (y1 < yMin) {
2301          yMin = y1;
2302        } else if (y1 > yMax) {
2303          yMax = y1;
2304        }
2305        validBBox = gTrue;
2306      }
2307      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2308                                       (int)floor(xMin - xt) - 2,
2309                                       (int)floor(yMin - yt) - 2,
2310                                       (int)ceil(xMax) - (int)floor(xMin) + 4,
2311                                       (int)ceil(yMax) - (int)floor(yMin) + 4,
2312                                       validBBox,
2313                                       colorMode != splashModeMono1);
2314    }
2315  }
2316  t3Font = t3FontCache[0];
2317
2318  // is the glyph in the cache?
2319  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2320  for (j = 0; j < t3Font->cacheAssoc; ++j) {
2321    if (t3Font->cacheTags != NULL) {
2322      if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2323        t3Font->cacheTags[i+j].code == code) {
2324        drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2325                     t3Font->cacheData + (i+j) * t3Font->glyphSize);
2326        return gTrue;
2327      }
2328    }
2329  }
2330
2331  // push a new Type 3 glyph record
2332  t3gs = new T3GlyphStack();
2333  t3gs->next = t3GlyphStack;
2334  t3GlyphStack = t3gs;
2335  t3GlyphStack->code = code;
2336  t3GlyphStack->cache = t3Font;
2337  t3GlyphStack->cacheTag = NULL;
2338  t3GlyphStack->cacheData = NULL;
2339
2340  haveT3Dx = gFalse;
2341
2342  return gFalse;
2343}
2344
2345void SplashOutputDev::endType3Char(GfxState *state) {
2346  T3GlyphStack *t3gs;
2347  double *ctm;
2348
2349  if (t3GlyphStack->cacheTag) {
2350    --nestCount;
2351    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2352           t3GlyphStack->cache->glyphSize);
2353    delete bitmap;
2354    delete splash;
2355    bitmap = t3GlyphStack->origBitmap;
2356    splash = t3GlyphStack->origSplash;
2357    ctm = state->getCTM();
2358    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2359                  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2360    updateCTM(state, 0, 0, 0, 0, 0, 0);
2361    drawType3Glyph(state, t3GlyphStack->cache,
2362                   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2363  }
2364  t3gs = t3GlyphStack;
2365  t3GlyphStack = t3gs->next;
2366  delete t3gs;
2367}
2368
2369void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2370  haveT3Dx = gTrue;
2371}
2372
2373void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2374                              double llx, double lly, double urx, double ury) {
2375  double *ctm;
2376  T3FontCache *t3Font;
2377  SplashColor color;
2378  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2379  int i, j;
2380
2381  // ignore multiple d0/d1 operators
2382  if (haveT3Dx) {
2383    return;
2384  }
2385  haveT3Dx = gTrue;
2386
2387  if (unlikely(t3GlyphStack == NULL)) {
2388    error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1");
2389    return;
2390  }
2391
2392  if (unlikely(t3GlyphStack->origBitmap != NULL)) {
2393    error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1");
2394    return;
2395  }
2396
2397  if (unlikely(t3GlyphStack->origSplash != NULL)) {
2398    error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1");
2399    return;
2400  }
2401
2402  t3Font = t3GlyphStack->cache;
2403
2404  // check for a valid bbox
2405  state->transform(0, 0, &xt, &yt);
2406  state->transform(llx, lly, &x1, &y1);
2407  xMin = xMax = x1;
2408  yMin = yMax = y1;
2409  state->transform(llx, ury, &x1, &y1);
2410  if (x1 < xMin) {
2411    xMin = x1;
2412  } else if (x1 > xMax) {
2413    xMax = x1;
2414  }
2415  if (y1 < yMin) {
2416    yMin = y1;
2417  } else if (y1 > yMax) {
2418    yMax = y1;
2419  }
2420  state->transform(urx, lly, &x1, &y1);
2421  if (x1 < xMin) {
2422    xMin = x1;
2423  } else if (x1 > xMax) {
2424    xMax = x1;
2425  }
2426  if (y1 < yMin) {
2427    yMin = y1;
2428  } else if (y1 > yMax) {
2429    yMax = y1;
2430  }
2431  state->transform(urx, ury, &x1, &y1);
2432  if (x1 < xMin) {
2433    xMin = x1;
2434  } else if (x1 > xMax) {
2435    xMax = x1;
2436  }
2437  if (y1 < yMin) {
2438    yMin = y1;
2439  } else if (y1 > yMax) {
2440    yMax = y1;
2441  }
2442  if (xMin - xt < t3Font->glyphX ||
2443      yMin - yt < t3Font->glyphY ||
2444      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2445      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2446    if (t3Font->validBBox) {
2447      error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2448    }
2449    return;
2450  }
2451
2452  if (t3Font->cacheTags == NULL)
2453    return;
2454
2455  // allocate a cache entry
2456  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2457  for (j = 0; j < t3Font->cacheAssoc; ++j) {
2458    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2459      t3Font->cacheTags[i+j].mru = 0x8000;
2460      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2461      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2462      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2463    } else {
2464      ++t3Font->cacheTags[i+j].mru;
2465    }
2466  }
2467
2468  // save state
2469  t3GlyphStack->origBitmap = bitmap;
2470  t3GlyphStack->origSplash = splash;
2471  ctm = state->getCTM();
2472  t3GlyphStack->origCTM4 = ctm[4];
2473  t3GlyphStack->origCTM5 = ctm[5];
2474
2475  // create the temporary bitmap
2476  if (colorMode == splashModeMono1) {
2477    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2478                              splashModeMono1, gFalse);
2479    splash = new Splash(bitmap, gFalse,
2480                        t3GlyphStack->origSplash->getScreen());
2481    color[0] = 0;
2482    splash->clear(color);
2483    color[0] = 0xff;
2484  } else {
2485    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2486                              splashModeMono8, gFalse);
2487    splash = new Splash(bitmap, vectorAntialias,
2488                        t3GlyphStack->origSplash->getScreen());
2489    color[0] = 0x00;
2490    splash->clear(color);
2491    color[0] = 0xff;
2492  }
2493  splash->setMinLineWidth(globalParams->getMinLineWidth());
2494  splash->setFillPattern(new SplashSolidColor(color));
2495  splash->setStrokePattern(new SplashSolidColor(color));
2496  //~ this should copy other state from t3GlyphStack->origSplash?
2497  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2498                -t3Font->glyphX, -t3Font->glyphY);
2499  updateCTM(state, 0, 0, 0, 0, 0, 0);
2500  ++nestCount;
2501}
2502
2503void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2504                                     T3FontCacheTag * /*tag*/, Guchar *data) {
2505  SplashGlyphBitmap glyph;
2506
2507  setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2508                   state->getOverprintMode(), state->getFillColor());
2509  glyph.x = -t3Font->glyphX;
2510  glyph.y = -t3Font->glyphY;
2511  glyph.w = t3Font->glyphW;
2512  glyph.h = t3Font->glyphH;
2513  glyph.aa = colorMode != splashModeMono1;
2514  glyph.data = data;
2515  glyph.freeData = gFalse;
2516  splash->fillGlyph(0, 0, &glyph);
2517}
2518
2519void SplashOutputDev::beginTextObject(GfxState *state) {
2520}
2521
2522void SplashOutputDev::endTextObject(GfxState *state) {
2523  if (textClipPath) {
2524    splash->clipToPath(textClipPath, gFalse);
2525    delete textClipPath;
2526    textClipPath = NULL;
2527  }
2528}
2529
2530struct SplashOutImageMaskData {
2531  ImageStream *imgStr;
2532  GBool invert;
2533  int width, height, y;
2534};
2535
2536GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
2537  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2538  Guchar *p;
2539  SplashColorPtr q;
2540  int x;
2541
2542  if (imgMaskData->y == imgMaskData->height) {
2543    return gFalse;
2544  }
2545  if (!(p = imgMaskData->imgStr->getLine())) {
2546    return gFalse;
2547  }
2548  for (x = 0, q = line; x < imgMaskData->width; ++x) {
2549    *q++ = *p++ ^ imgMaskData->invert;
2550  }
2551  ++imgMaskData->y;
2552  return gTrue;
2553}
2554
2555void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2556                                    int width, int height, GBool invert,
2557                                    GBool interpolate, GBool inlineImg) {
2558  double *ctm;
2559  SplashCoord mat[6];
2560  SplashOutImageMaskData imgMaskData;
2561
2562  if (state->getFillColorSpace()->isNonMarking()) {
2563    return;
2564  }
2565  setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2566                   state->getOverprintMode(), state->getFillColor());
2567
2568  ctm = state->getCTM();
2569  for (int i = 0; i < 6; ++i) {
2570    if (!isfinite(ctm[i])) return;
2571  }
2572  mat[0] = ctm[0];
2573  mat[1] = ctm[1];
2574  mat[2] = -ctm[2];
2575  mat[3] = -ctm[3];
2576  mat[4] = ctm[2] + ctm[4];
2577  mat[5] = ctm[3] + ctm[5];
2578
2579  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2580  imgMaskData.imgStr->reset();
2581  imgMaskData.invert = invert ? 0 : 1;
2582  imgMaskData.width = width;
2583  imgMaskData.height = height;
2584  imgMaskData.y = 0;
2585
2586  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2587  if (inlineImg) {
2588    if (inlineImg) {
2589      while (imgMaskData.y < height) {
2590        imgMaskData.imgStr->getLine();
2591        ++imgMaskData.y;
2592      }
2593    }
2594  }
2595
2596  delete imgMaskData.imgStr;
2597  str->close();
2598}
2599
2600void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2601                                               Object *ref, Stream *str,
2602                                               int width, int height,
2603                                               GBool invert,
2604                                               GBool inlineImg, double *baseMatrix) {
2605  double *ctm;
2606  SplashCoord mat[6];
2607  SplashOutImageMaskData imgMaskData;
2608  Splash *maskSplash;
2609  SplashColor maskColor;
2610  double bbox[4] = {0, 0, 1, 1}; // default;
2611
2612  if (state->getFillColorSpace()->isNonMarking()) {
2613    return;
2614  }
2615
2616  ctm = state->getCTM();
2617  for (int i = 0; i < 6; ++i) {
2618    if (!isfinite(ctm[i])) return;
2619  }
2620 
2621  beginTransparencyGroup(state, bbox, NULL, gFalse, gFalse, gFalse);
2622  baseMatrix[4] -= transpGroupStack->tx;
2623  baseMatrix[5] -= transpGroupStack->ty;
2624
2625  ctm = state->getCTM();
2626  mat[0] = ctm[0];
2627  mat[1] = ctm[1];
2628  mat[2] = -ctm[2];
2629  mat[3] = -ctm[3];
2630  mat[4] = ctm[2] + ctm[4];
2631  mat[5] = ctm[3] + ctm[5];
2632  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2633  imgMaskData.imgStr->reset();
2634  imgMaskData.invert = invert ? 0 : 1;
2635  imgMaskData.width = width;
2636  imgMaskData.height = height;
2637  imgMaskData.y = 0;
2638
2639  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
2640  maskSplash = new Splash(maskBitmap, vectorAntialias);
2641  maskColor[0] = 0;
2642  maskSplash->clear(maskColor);
2643  maskColor[0] = 0xff;
2644  maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2645  maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,  width, height, mat, t3GlyphStack != NULL);
2646  delete maskSplash;
2647  delete imgMaskData.imgStr;
2648  str->close();
2649}
2650
2651void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {
2652  double bbox[4] = {0,0,1,1}; // dummy
2653
2654  /* transfer mask to alpha channel! */
2655  // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
2656  // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
2657  Guchar *dest = bitmap->getAlphaPtr();
2658  Guchar *src = maskBitmap->getDataPtr();
2659  for (int c= 0; c < maskBitmap->getRowSize() * maskBitmap->getHeight(); c++) {
2660    dest[c] = src[c];
2661  }
2662  delete maskBitmap;
2663  maskBitmap = NULL;
2664  endTransparencyGroup(state);
2665  baseMatrix[4] += transpGroupStack->tx;
2666  baseMatrix[5] += transpGroupStack->ty;
2667  paintTransparencyGroup(state, bbox);
2668}
2669
2670struct SplashOutImageData {
2671  ImageStream *imgStr;
2672  GfxImageColorMap *colorMap;
2673  SplashColorPtr lookup;
2674  int *maskColors;
2675  SplashColorMode colorMode;
2676  int width, height, y;
2677};
2678
2679GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2680                                Guchar * /*alphaLine*/) {
2681  SplashOutImageData *imgData = (SplashOutImageData *)data;
2682  Guchar *p;
2683  SplashColorPtr q, col;
2684  GfxRGB rgb;
2685  GfxGray gray;
2686#if SPLASH_CMYK
2687  GfxCMYK cmyk;
2688#endif
2689  int nComps, x;
2690
2691  if (imgData->y == imgData->height) {
2692    return gFalse;
2693  }
2694  if (!(p = imgData->imgStr->getLine())) {
2695    return gFalse;
2696  }
2697
2698  nComps = imgData->colorMap->getNumPixelComps();
2699
2700  if (imgData->lookup) {
2701    switch (imgData->colorMode) {
2702    case splashModeMono1:
2703    case splashModeMono8:
2704      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2705        *q++ = imgData->lookup[*p];
2706      }
2707      break;
2708    case splashModeRGB8:
2709    case splashModeBGR8:
2710      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2711        col = &imgData->lookup[3 * *p];
2712        *q++ = col[0];
2713        *q++ = col[1];
2714        *q++ = col[2];
2715      }
2716      break;
2717  case splashModeXBGR8:
2718      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2719        col = &imgData->lookup[4 * *p];
2720        *q++ = col[0];
2721        *q++ = col[1];
2722        *q++ = col[2];
2723        *q++ = col[3];
2724      }
2725      break;
2726#if SPLASH_CMYK
2727    case splashModeCMYK8:
2728      for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2729        col = &imgData->lookup[4 * *p];
2730        *q++ = col[0];
2731        *q++ = col[1];
2732        *q++ = col[2];
2733        *q++ = col[3];
2734      }
2735      break;
2736#endif
2737    }
2738  } else {
2739    switch (imgData->colorMode) {
2740    case splashModeMono1:
2741    case splashModeMono8:
2742      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
2743        imgData->colorMap->getGray(p, &gray);
2744        *q++ = colToByte(gray);
2745      }
2746      break;
2747    case splashModeRGB8:
2748    case splashModeBGR8:
2749      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
2750        imgData->colorMap->getRGB(p, &rgb);
2751        *q++ = colToByte(rgb.r);
2752        *q++ = colToByte(rgb.g);
2753        *q++ = colToByte(rgb.b);
2754      }
2755      break;
2756    case splashModeXBGR8:
2757      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
2758        imgData->colorMap->getRGB(p, &rgb);
2759        *q++ = colToByte(rgb.r);
2760        *q++ = colToByte(rgb.g);
2761        *q++ = colToByte(rgb.b);
2762        *q++ = 255;
2763      }
2764      break;
2765#if SPLASH_CMYK
2766    case splashModeCMYK8:
2767      for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
2768        imgData->colorMap->getCMYK(p, &cmyk);
2769        *q++ = colToByte(cmyk.c);
2770        *q++ = colToByte(cmyk.m);
2771        *q++ = colToByte(cmyk.y);
2772        *q++ = colToByte(cmyk.k);
2773      }
2774      break;
2775#endif
2776    }
2777  }
2778
2779  ++imgData->y;
2780  return gTrue;
2781}
2782
2783GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
2784                                     Guchar *alphaLine) {
2785  SplashOutImageData *imgData = (SplashOutImageData *)data;
2786  Guchar *p, *aq;
2787  SplashColorPtr q, col;
2788  GfxRGB rgb;
2789  GfxGray gray;
2790#if SPLASH_CMYK
2791  GfxCMYK cmyk;
2792#endif
2793  Guchar alpha;
2794  int nComps, x, i;
2795
2796  if (imgData->y == imgData->height) {
2797    return gFalse;
2798  }
2799  if (!(p = imgData->imgStr->getLine())) {
2800    return gFalse;
2801  }
2802
2803  nComps = imgData->colorMap->getNumPixelComps();
2804
2805  for (x = 0, q = colorLine, aq = alphaLine;
2806       x < imgData->width;
2807       ++x, p += nComps) {
2808    alpha = 0;
2809    for (i = 0; i < nComps; ++i) {
2810      if (p[i] < imgData->maskColors[2*i] ||
2811          p[i] > imgData->maskColors[2*i+1]) {
2812        alpha = 0xff;
2813        break;
2814      }
2815    }
2816    if (imgData->lookup) {
2817      switch (imgData->colorMode) {
2818      case splashModeMono1:
2819      case splashModeMono8:
2820        *q++ = imgData->lookup[*p];
2821        break;
2822      case splashModeRGB8:
2823      case splashModeBGR8:
2824        col = &imgData->lookup[3 * *p];
2825        *q++ = col[0];
2826        *q++ = col[1];
2827        *q++ = col[2];
2828        break;
2829      case splashModeXBGR8:
2830        col = &imgData->lookup[4 * *p];
2831        *q++ = col[0];
2832        *q++ = col[1];
2833        *q++ = col[2];
2834        *q++ = 255;
2835        break;
2836#if SPLASH_CMYK
2837      case splashModeCMYK8:
2838        col = &imgData->lookup[4 * *p];
2839        *q++ = col[0];
2840        *q++ = col[1];
2841        *q++ = col[2];
2842        *q++ = col[3];
2843        break;
2844#endif
2845      }
2846      *aq++ = alpha;
2847    } else {
2848      switch (imgData->colorMode) {
2849      case splashModeMono1:
2850      case splashModeMono8:
2851        imgData->colorMap->getGray(p, &gray);
2852        *q++ = colToByte(gray);
2853        break;
2854      case splashModeXBGR8:
2855      case splashModeRGB8:
2856      case splashModeBGR8:
2857        imgData->colorMap->getRGB(p, &rgb);
2858        *q++ = colToByte(rgb.r);
2859        *q++ = colToByte(rgb.g);
2860        *q++ = colToByte(rgb.b);
2861        if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
2862        break;
2863#if SPLASH_CMYK
2864      case splashModeCMYK8:
2865        imgData->colorMap->getCMYK(p, &cmyk);
2866        *q++ = colToByte(cmyk.c);
2867        *q++ = colToByte(cmyk.m);
2868        *q++ = colToByte(cmyk.y);
2869        *q++ = colToByte(cmyk.k);
2870        break;
2871#endif
2872      }
2873      *aq++ = alpha;
2874    }
2875  }
2876
2877  ++imgData->y;
2878  return gTrue;
2879}
2880
2881struct TilingSplashOutBitmap {
2882  SplashBitmap *bitmap;
2883  SplashPattern *pattern;
2884  SplashColorMode colorMode;
2885  int paintType;
2886  int repeatX;
2887  int repeatY;
2888  int y;
2889};
2890
2891GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
2892                                       Guchar *alphaLine) {
2893  TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data;
2894
2895  if (imgData->y == imgData->bitmap->getHeight()) {
2896    imgData->repeatY--;
2897    if (imgData->repeatY == 0)
2898      return gFalse;
2899    imgData->y = 0;
2900  }
2901
2902  if (imgData->paintType == 1) {
2903    const SplashColorMode cMode = imgData->bitmap->getMode();
2904    SplashColorPtr q = colorLine;
2905    // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel
2906    // for the others we can use raw access
2907    if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) {
2908      for (int m = 0; m < imgData->repeatX; m++) {
2909        for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
2910          imgData->bitmap->getPixel(x, imgData->y, q);
2911          q += splashColorModeNComps[cMode];
2912        }
2913      }
2914    } else {
2915      const int n = imgData->bitmap->getRowSize();
2916      SplashColorPtr p;
2917      for (int m = 0; m < imgData->repeatX; m++) {
2918        p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize();
2919        for (int x = 0; x < n; ++x) {
2920          *q++ = *p++;
2921        }
2922      }
2923    }
2924    if (alphaLine != NULL) {
2925      SplashColorPtr aq = alphaLine;
2926      SplashColorPtr p;
2927      const int n = imgData->bitmap->getWidth() - 1;
2928      for (int m = 0; m < imgData->repeatX; m++) {
2929        p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth();
2930        for (int x = 0; x < n; ++x) {
2931          *aq++ = *p++;
2932        }
2933        // This is a hack, because of how Splash antialias works if we overwrite the
2934        // last alpha pixel of the tile most/all of the files look much better
2935        *aq++ = (n == 0) ? *p : *(p - 1);
2936      }
2937    }
2938  } else {
2939    SplashColor col, pat;
2940    SplashColorPtr dest = colorLine;
2941    for (int m = 0; m < imgData->repeatX; m++) {
2942      for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
2943        imgData->bitmap->getPixel(x, imgData->y, col);
2944        imgData->pattern->getColor(x, imgData->y, pat);
2945        for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) {
2946#if SPLASH_CMYK
2947          if (imgData->colorMode == splashModeCMYK8)
2948            dest[i] = div255(pat[i] * (255 - col[0]));
2949          else
2950#endif
2951            dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0]));
2952        }
2953        dest += splashColorModeNComps[imgData->colorMode];
2954      }
2955    }
2956    if (alphaLine != NULL) {
2957      const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y;
2958      SplashColorPtr aq = alphaLine;
2959      SplashColorPtr p;
2960      const int n = imgData->bitmap->getWidth();
2961      for (int m = 0; m < imgData->repeatX; m++) {
2962        p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth();
2963        for (int x = 0; x < n; ++x) {
2964          *aq++ = *p++;
2965        }
2966      }
2967    }
2968  }
2969  ++imgData->y;
2970  return gTrue;
2971}
2972
2973void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
2974                                int width, int height,
2975                                GfxImageColorMap *colorMap,
2976                                GBool interpolate,
2977                                int *maskColors, GBool inlineImg) {
2978  double *ctm;
2979  SplashCoord mat[6];
2980  SplashOutImageData imgData;
2981  SplashColorMode srcMode;
2982  SplashImageSource src;
2983  GfxGray gray;
2984  GfxRGB rgb;
2985#if SPLASH_CMYK
2986  GfxCMYK cmyk;
2987  GBool grayIndexed = gFalse;
2988#endif
2989  Guchar pix;
2990  int n, i;
2991
2992  ctm = state->getCTM();
2993  for (i = 0; i < 6; ++i) {
2994    if (!isfinite(ctm[i])) return;
2995  }
2996  mat[0] = ctm[0];
2997  mat[1] = ctm[1];
2998  mat[2] = -ctm[2];
2999  mat[3] = -ctm[3];
3000  mat[4] = ctm[2] + ctm[4];
3001  mat[5] = ctm[3] + ctm[5];
3002
3003  imgData.imgStr = new ImageStream(str, width,
3004                                   colorMap->getNumPixelComps(),
3005                                   colorMap->getBits());
3006  imgData.imgStr->reset();
3007  imgData.colorMap = colorMap;
3008  imgData.maskColors = maskColors;
3009  imgData.colorMode = colorMode;
3010  imgData.width = width;
3011  imgData.height = height;
3012  imgData.y = 0;
3013
3014  // special case for one-channel (monochrome/gray/separation) images:
3015  // build a lookup table here
3016  imgData.lookup = NULL;
3017  if (colorMap->getNumPixelComps() == 1) {
3018    n = 1 << colorMap->getBits();
3019    switch (colorMode) {
3020    case splashModeMono1:
3021    case splashModeMono8:
3022      imgData.lookup = (SplashColorPtr)gmalloc(n);
3023      for (i = 0; i < n; ++i) {
3024        pix = (Guchar)i;
3025        colorMap->getGray(&pix, &gray);
3026        imgData.lookup[i] = colToByte(gray);
3027      }
3028      break;
3029    case splashModeRGB8:
3030    case splashModeBGR8:
3031      imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3032      for (i = 0; i < n; ++i) {
3033        pix = (Guchar)i;
3034        colorMap->getRGB(&pix, &rgb);
3035        imgData.lookup[3*i] = colToByte(rgb.r);
3036        imgData.lookup[3*i+1] = colToByte(rgb.g);
3037        imgData.lookup[3*i+2] = colToByte(rgb.b);
3038      }
3039      break;
3040    case splashModeXBGR8:
3041      imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3042      for (i = 0; i < n; ++i) {
3043        pix = (Guchar)i;
3044        colorMap->getRGB(&pix, &rgb);
3045        imgData.lookup[4*i] = colToByte(rgb.r);
3046        imgData.lookup[4*i+1] = colToByte(rgb.g);
3047        imgData.lookup[4*i+2] = colToByte(rgb.b);
3048        imgData.lookup[4*i+3] = 255;
3049      }
3050      break;
3051#if SPLASH_CMYK
3052    case splashModeCMYK8:
3053      grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
3054      imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3055      for (i = 0; i < n; ++i) {
3056        pix = (Guchar)i;
3057        colorMap->getCMYK(&pix, &cmyk);
3058        if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
3059          grayIndexed = gFalse;
3060        }
3061        imgData.lookup[4*i] = colToByte(cmyk.c);
3062        imgData.lookup[4*i+1] = colToByte(cmyk.m);
3063        imgData.lookup[4*i+2] = colToByte(cmyk.y);
3064        imgData.lookup[4*i+3] = colToByte(cmyk.k);
3065      }
3066      break;
3067#endif
3068    }
3069  }
3070
3071#if SPLASH_CMYK
3072  setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3073                   state->getOverprintMode(), NULL, grayIndexed);
3074#else             
3075  setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3076                   state->getOverprintMode(), NULL);
3077#endif             
3078
3079  if (colorMode == splashModeMono1) {
3080    srcMode = splashModeMono8;
3081  } else {
3082    srcMode = colorMode;
3083  }
3084  src = maskColors ? &alphaImageSrc : &imageSrc;
3085  splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
3086                    width, height, mat);
3087  if (inlineImg) {
3088    while (imgData.y < height) {
3089      imgData.imgStr->getLine();
3090      ++imgData.y;
3091    }
3092  }
3093
3094  gfree(imgData.lookup);
3095  delete imgData.imgStr;
3096  str->close();
3097}
3098
3099struct SplashOutMaskedImageData {
3100  ImageStream *imgStr;
3101  GfxImageColorMap *colorMap;
3102  SplashBitmap *mask;
3103  SplashColorPtr lookup;
3104  SplashColorMode colorMode;
3105  int width, height, y;
3106};
3107
3108GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
3109                                      Guchar *alphaLine) {
3110  SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
3111  Guchar *p, *aq;
3112  SplashColorPtr q, col;
3113  GfxRGB rgb;
3114  GfxGray gray;
3115#if SPLASH_CMYK
3116  GfxCMYK cmyk;
3117#endif
3118  Guchar alpha;
3119  Guchar *maskPtr;
3120  int maskBit;
3121  int nComps, x;
3122
3123  if (imgData->y == imgData->height) {
3124    return gFalse;
3125  }
3126  if (!(p = imgData->imgStr->getLine())) {
3127    return gFalse;
3128  }
3129
3130  nComps = imgData->colorMap->getNumPixelComps();
3131
3132  maskPtr = imgData->mask->getDataPtr() +
3133              imgData->y * imgData->mask->getRowSize();
3134  maskBit = 0x80;
3135  for (x = 0, q = colorLine, aq = alphaLine;
3136       x < imgData->width;
3137       ++x, p += nComps) {
3138    alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
3139    if (!(maskBit >>= 1)) {
3140      ++maskPtr;
3141      maskBit = 0x80;
3142    }
3143    if (imgData->lookup) {
3144      switch (imgData->colorMode) {
3145      case splashModeMono1:
3146      case splashModeMono8:
3147        *q++ = imgData->lookup[*p];
3148        break;
3149      case splashModeRGB8:
3150      case splashModeBGR8:
3151        col = &imgData->lookup[3 * *p];
3152        *q++ = col[0];
3153        *q++ = col[1];
3154        *q++ = col[2];
3155        break;
3156      case splashModeXBGR8:
3157        col = &imgData->lookup[4 * *p];
3158        *q++ = col[0];
3159        *q++ = col[1];
3160        *q++ = col[2];
3161        *q++ = 255;
3162        break;
3163#if SPLASH_CMYK
3164      case splashModeCMYK8:
3165        col = &imgData->lookup[4 * *p];
3166        *q++ = col[0];
3167        *q++ = col[1];
3168        *q++ = col[2];
3169        *q++ = col[3];
3170        break;
3171#endif
3172      }
3173      *aq++ = alpha;
3174    } else {
3175      switch (imgData->colorMode) {
3176      case splashModeMono1:
3177      case splashModeMono8:
3178        imgData->colorMap->getGray(p, &gray);
3179        *q++ = colToByte(gray);
3180        break;
3181      case splashModeXBGR8:
3182      case splashModeRGB8:
3183      case splashModeBGR8:
3184        imgData->colorMap->getRGB(p, &rgb);
3185        *q++ = colToByte(rgb.r);
3186        *q++ = colToByte(rgb.g);
3187        *q++ = colToByte(rgb.b);
3188        if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
3189        break;
3190#if SPLASH_CMYK
3191      case splashModeCMYK8:
3192        imgData->colorMap->getCMYK(p, &cmyk);
3193        *q++ = colToByte(cmyk.c);
3194        *q++ = colToByte(cmyk.m);
3195        *q++ = colToByte(cmyk.y);
3196        *q++ = colToByte(cmyk.k);
3197        break;
3198#endif
3199      }
3200      *aq++ = alpha;
3201    }
3202  }
3203
3204  ++imgData->y;
3205  return gTrue;
3206}
3207
3208void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
3209                                      Stream *str, int width, int height,
3210                                      GfxImageColorMap *colorMap,
3211                                      GBool interpolate,
3212                                      Stream *maskStr, int maskWidth,
3213                                      int maskHeight, GBool maskInvert,
3214                                      GBool maskInterpolate) {
3215  GfxImageColorMap *maskColorMap;
3216  Object maskDecode, decodeLow, decodeHigh;
3217  double *ctm;
3218  SplashCoord mat[6];
3219  SplashOutMaskedImageData imgData;
3220  SplashOutImageMaskData imgMaskData;
3221  SplashColorMode srcMode;
3222  SplashBitmap *maskBitmap;
3223  Splash *maskSplash;
3224  SplashColor maskColor;
3225  GfxGray gray;
3226  GfxRGB rgb;
3227#if SPLASH_CMYK
3228  GfxCMYK cmyk;
3229#endif
3230  Guchar pix;
3231  int n, i;
3232
3233  setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3234                   state->getOverprintMode(), NULL);
3235
3236  // If the mask is higher resolution than the image, use
3237  // drawSoftMaskedImage() instead.
3238  if (maskWidth > width || maskHeight > height) {
3239    decodeLow.initInt(maskInvert ? 0 : 1);
3240    decodeHigh.initInt(maskInvert ? 1 : 0);
3241    maskDecode.initArray(doc->getXRef());
3242    maskDecode.arrayAdd(&decodeLow);
3243    maskDecode.arrayAdd(&decodeHigh);
3244    maskColorMap = new GfxImageColorMap(1, &maskDecode,
3245                                        new GfxDeviceGrayColorSpace());
3246    maskDecode.free();
3247    drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3248                        maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
3249    delete maskColorMap;
3250
3251  } else {
3252    //----- scale the mask image to the same size as the source image
3253
3254    mat[0] = (SplashCoord)width;
3255    mat[1] = 0;
3256    mat[2] = 0;
3257    mat[3] = (SplashCoord)height;
3258    mat[4] = 0;
3259    mat[5] = 0;
3260    imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3261    imgMaskData.imgStr->reset();
3262    imgMaskData.invert = maskInvert ? 0 : 1;
3263    imgMaskData.width = maskWidth;
3264    imgMaskData.height = maskHeight;
3265    imgMaskData.y = 0;
3266    maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
3267    maskSplash = new Splash(maskBitmap, gFalse);
3268    maskColor[0] = 0;
3269    maskSplash->clear(maskColor);
3270    maskColor[0] = 0xff;
3271    maskSplash->setFillPattern(new SplashSolidColor(maskColor));
3272    maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
3273                              maskWidth, maskHeight, mat, gFalse);
3274    delete imgMaskData.imgStr;
3275    maskStr->close();
3276    delete maskSplash;
3277
3278    //----- draw the source image
3279
3280    ctm = state->getCTM();
3281    for (i = 0; i < 6; ++i) {
3282      if (!isfinite(ctm[i])) {
3283        delete maskBitmap;
3284        return;
3285      }
3286    }
3287    mat[0] = ctm[0];
3288    mat[1] = ctm[1];
3289    mat[2] = -ctm[2];
3290    mat[3] = -ctm[3];
3291    mat[4] = ctm[2] + ctm[4];
3292    mat[5] = ctm[3] + ctm[5];
3293
3294    imgData.imgStr = new ImageStream(str, width,
3295                                     colorMap->getNumPixelComps(),
3296                                     colorMap->getBits());
3297    imgData.imgStr->reset();
3298    imgData.colorMap = colorMap;
3299    imgData.mask = maskBitmap;
3300    imgData.colorMode = colorMode;
3301    imgData.width = width;
3302    imgData.height = height;
3303    imgData.y = 0;
3304
3305    // special case for one-channel (monochrome/gray/separation) images:
3306    // build a lookup table here
3307    imgData.lookup = NULL;
3308    if (colorMap->getNumPixelComps() == 1) {
3309      n = 1 << colorMap->getBits();
3310      switch (colorMode) {
3311      case splashModeMono1:
3312      case splashModeMono8:
3313        imgData.lookup = (SplashColorPtr)gmalloc(n);
3314        for (i = 0; i < n; ++i) {
3315          pix = (Guchar)i;
3316          colorMap->getGray(&pix, &gray);
3317          imgData.lookup[i] = colToByte(gray);
3318        }
3319        break;
3320      case splashModeRGB8:
3321      case splashModeBGR8:
3322        imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3323        for (i = 0; i < n; ++i) {
3324          pix = (Guchar)i;
3325          colorMap->getRGB(&pix, &rgb);
3326          imgData.lookup[3*i] = colToByte(rgb.r);
3327          imgData.lookup[3*i+1] = colToByte(rgb.g);
3328          imgData.lookup[3*i+2] = colToByte(rgb.b);
3329        }
3330        break;
3331      case splashModeXBGR8:
3332        imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3333        for (i = 0; i < n; ++i) {
3334          pix = (Guchar)i;
3335          colorMap->getRGB(&pix, &rgb);
3336          imgData.lookup[4*i] = colToByte(rgb.r);
3337          imgData.lookup[4*i+1] = colToByte(rgb.g);
3338          imgData.lookup[4*i+2] = colToByte(rgb.b);
3339          imgData.lookup[4*i+3] = 255;
3340        }
3341        break;
3342#if SPLASH_CMYK
3343      case splashModeCMYK8:
3344        imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3345        for (i = 0; i < n; ++i) {
3346          pix = (Guchar)i;
3347          colorMap->getCMYK(&pix, &cmyk);
3348          imgData.lookup[4*i] = colToByte(cmyk.c);
3349          imgData.lookup[4*i+1] = colToByte(cmyk.m);
3350          imgData.lookup[4*i+2] = colToByte(cmyk.y);
3351          imgData.lookup[4*i+3] = colToByte(cmyk.k);
3352        }
3353        break;
3354#endif
3355      }
3356    }
3357
3358    if (colorMode == splashModeMono1) {
3359      srcMode = splashModeMono8;
3360    } else {
3361      srcMode = colorMode;
3362    }
3363    splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
3364                      width, height, mat);
3365    delete maskBitmap;
3366    gfree(imgData.lookup);
3367    delete imgData.imgStr;
3368    str->close();
3369  }
3370}
3371
3372void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
3373                                          Stream *str, int width, int height,
3374                                          GfxImageColorMap *colorMap,
3375                                          GBool interpolate,
3376                                          Stream *maskStr,
3377                                          int maskWidth, int maskHeight,
3378                                          GfxImageColorMap *maskColorMap,
3379                                          GBool maskInterpolate) {
3380  double *ctm;
3381  SplashCoord mat[6];
3382  SplashOutImageData imgData;
3383  SplashOutImageData imgMaskData;
3384  SplashColorMode srcMode;
3385  SplashBitmap *maskBitmap;
3386  Splash *maskSplash;
3387  SplashColor maskColor;
3388  GfxGray gray;
3389  GfxRGB rgb;
3390#if SPLASH_CMYK
3391  GfxCMYK cmyk;
3392#endif
3393  Guchar pix;
3394  int n, i;
3395
3396  setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3397                   state->getOverprintMode(), NULL);
3398
3399  ctm = state->getCTM();
3400  for (i = 0; i < 6; ++i) {
3401    if (!isfinite(ctm[i])) return;
3402  }
3403  mat[0] = ctm[0];
3404  mat[1] = ctm[1];
3405  mat[2] = -ctm[2];
3406  mat[3] = -ctm[3];
3407  mat[4] = ctm[2] + ctm[4];
3408  mat[5] = ctm[3] + ctm[5];
3409
3410  //----- set up the soft mask
3411
3412  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
3413                                       maskColorMap->getNumPixelComps(),
3414                                       maskColorMap->getBits());
3415  imgMaskData.imgStr->reset();
3416  imgMaskData.colorMap = maskColorMap;
3417  imgMaskData.maskColors = NULL;
3418  imgMaskData.colorMode = splashModeMono8;
3419  imgMaskData.width = maskWidth;
3420  imgMaskData.height = maskHeight;
3421  imgMaskData.y = 0;
3422  n = 1 << maskColorMap->getBits();
3423  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
3424  for (i = 0; i < n; ++i) {
3425    pix = (Guchar)i;
3426    maskColorMap->getGray(&pix, &gray);
3427    imgMaskData.lookup[i] = colToByte(gray);
3428  }
3429  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3430                                1, splashModeMono8, gFalse);
3431  maskSplash = new Splash(maskBitmap, vectorAntialias);
3432  maskColor[0] = 0;
3433  maskSplash->clear(maskColor);
3434  maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
3435                        maskWidth, maskHeight, mat);
3436  delete imgMaskData.imgStr;
3437  maskStr->close();
3438  gfree(imgMaskData.lookup);
3439  delete maskSplash;
3440  splash->setSoftMask(maskBitmap);
3441
3442  //----- draw the source image
3443
3444  imgData.imgStr = new ImageStream(str, width,
3445                                   colorMap->getNumPixelComps(),
3446                                   colorMap->getBits());
3447  imgData.imgStr->reset();
3448  imgData.colorMap = colorMap;
3449  imgData.maskColors = NULL;
3450  imgData.colorMode = colorMode;
3451  imgData.width = width;
3452  imgData.height = height;
3453  imgData.y = 0;
3454
3455  // special case for one-channel (monochrome/gray/separation) images:
3456  // build a lookup table here
3457  imgData.lookup = NULL;
3458  if (colorMap->getNumPixelComps() == 1) {
3459    n = 1 << colorMap->getBits();
3460    switch (colorMode) {
3461    case splashModeMono1:
3462    case splashModeMono8:
3463      imgData.lookup = (SplashColorPtr)gmalloc(n);
3464      for (i = 0; i < n; ++i) {
3465        pix = (Guchar)i;
3466        colorMap->getGray(&pix, &gray);
3467        imgData.lookup[i] = colToByte(gray);
3468      }
3469      break;
3470    case splashModeRGB8:
3471    case splashModeBGR8:
3472      imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3473      for (i = 0; i < n; ++i) {
3474        pix = (Guchar)i;
3475        colorMap->getRGB(&pix, &rgb);
3476        imgData.lookup[3*i] = colToByte(rgb.r);
3477        imgData.lookup[3*i+1] = colToByte(rgb.g);
3478        imgData.lookup[3*i+2] = colToByte(rgb.b);
3479      }
3480      break;
3481    case splashModeXBGR8:
3482      imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3483      for (i = 0; i < n; ++i) {
3484        pix = (Guchar)i;
3485        colorMap->getRGB(&pix, &rgb);
3486        imgData.lookup[4*i] = colToByte(rgb.r);
3487        imgData.lookup[4*i+1] = colToByte(rgb.g);
3488        imgData.lookup[4*i+2] = colToByte(rgb.b);
3489        imgData.lookup[4*i+3] = 255;
3490      }
3491      break;
3492#if SPLASH_CMYK
3493    case splashModeCMYK8:
3494      imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3495      for (i = 0; i < n; ++i) {
3496        pix = (Guchar)i;
3497        colorMap->getCMYK(&pix, &cmyk);
3498        imgData.lookup[4*i] = colToByte(cmyk.c);
3499        imgData.lookup[4*i+1] = colToByte(cmyk.m);
3500        imgData.lookup[4*i+2] = colToByte(cmyk.y);
3501        imgData.lookup[4*i+3] = colToByte(cmyk.k);
3502      }
3503      break;
3504#endif
3505    }
3506  }
3507
3508  if (colorMode == splashModeMono1) {
3509    srcMode = splashModeMono8;
3510  } else {
3511    srcMode = colorMode;
3512  }
3513  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
3514  splash->setSoftMask(NULL);
3515  gfree(imgData.lookup);
3516  delete imgData.imgStr;
3517  str->close();
3518}
3519
3520void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
3521                                             GfxColorSpace *blendingColorSpace,
3522                                             GBool isolated, GBool knockout,
3523                                             GBool forSoftMask) {
3524  SplashTransparencyGroup *transpGroup;
3525  SplashColor color;
3526  double xMin, yMin, xMax, yMax, x, y;
3527  int tx, ty, w, h, i;
3528
3529  // transform the bbox
3530  state->transform(bbox[0], bbox[1], &x, &y);
3531  xMin = xMax = x;
3532  yMin = yMax = y;
3533  state->transform(bbox[0], bbox[3], &x, &y);
3534  if (x < xMin) {
3535    xMin = x;
3536  } else if (x > xMax) {
3537    xMax = x;
3538  }
3539  if (y < yMin) {
3540    yMin = y;
3541  } else if (y > yMax) {
3542    yMax = y;
3543  }
3544  state->transform(bbox[2], bbox[1], &x, &y);
3545  if (x < xMin) {
3546    xMin = x;
3547  } else if (x > xMax) {
3548    xMax = x;
3549  }
3550  if (y < yMin) {
3551    yMin = y;
3552  } else if (y > yMax) {
3553    yMax = y;
3554  }
3555  state->transform(bbox[2], bbox[3], &x, &y);
3556  if (x < xMin) {
3557    xMin = x;
3558  } else if (x > xMax) {
3559    xMax = x;
3560  }
3561  if (y < yMin) {
3562    yMin = y;
3563  } else if (y > yMax) {
3564    yMax = y;
3565  }
3566  tx = (int)floor(xMin);
3567  if (tx < 0) {
3568    tx = 0;
3569  } else if (tx >= bitmap->getWidth()) {
3570    tx = bitmap->getWidth() - 1;
3571  }
3572  ty = (int)floor(yMin);
3573  if (ty < 0) {
3574    ty = 0;
3575  } else if (ty >= bitmap->getHeight()) {
3576    ty = bitmap->getHeight() - 1;
3577  }
3578  w = (int)ceil(xMax) - tx + 1;
3579  if (tx + w > bitmap->getWidth()) {
3580    w = bitmap->getWidth() - tx;
3581  }
3582  if (w < 1) {
3583    w = 1;
3584  }
3585  h = (int)ceil(yMax) - ty + 1;
3586  if (ty + h > bitmap->getHeight()) {
3587    h = bitmap->getHeight() - ty;
3588  }
3589  if (h < 1) {
3590    h = 1;
3591  }
3592
3593  // push a new stack entry
3594  transpGroup = new SplashTransparencyGroup();
3595  transpGroup->tx = tx;
3596  transpGroup->ty = ty;
3597  transpGroup->blendingColorSpace = blendingColorSpace;
3598  transpGroup->isolated = isolated;
3599  transpGroup->shape = (knockout) ? SplashBitmap::copy(bitmap) : NULL;
3600  transpGroup->knockout = gFalse; 
3601  transpGroup->knockoutOpacity = 1.0;
3602  transpGroup->next = transpGroupStack;
3603  transpGroupStack = transpGroup;
3604
3605  // save state
3606  transpGroup->origBitmap = bitmap;
3607  transpGroup->origSplash = splash;
3608
3609  //~ this handles the blendingColorSpace arg for soft masks, but
3610  //~   not yet for transparency groups
3611
3612  // switch to the blending color space
3613  if (forSoftMask && isolated && blendingColorSpace) {
3614    if (blendingColorSpace->getMode() == csDeviceGray ||
3615        blendingColorSpace->getMode() == csCalGray ||
3616        (blendingColorSpace->getMode() == csICCBased &&
3617         blendingColorSpace->getNComps() == 1)) {
3618      colorMode = splashModeMono8;
3619    } else if (blendingColorSpace->getMode() == csDeviceRGB ||
3620               blendingColorSpace->getMode() == csCalRGB ||
3621               (blendingColorSpace->getMode() == csICCBased &&
3622                blendingColorSpace->getNComps() == 3)) {
3623      //~ does this need to use BGR8?
3624      colorMode = splashModeRGB8;
3625#if SPLASH_CMYK
3626    } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
3627               (blendingColorSpace->getMode() == csICCBased &&
3628                blendingColorSpace->getNComps() == 4)) {
3629      colorMode = splashModeCMYK8;
3630#endif
3631    }
3632  }
3633
3634  // create the temporary bitmap
3635  bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
3636                            bitmapTopDown);
3637  splash = new Splash(bitmap, vectorAntialias,
3638                      transpGroup->origSplash->getScreen());
3639  splash->setMinLineWidth(globalParams->getMinLineWidth());
3640  //~ Acrobat apparently copies at least the fill and stroke colors, and
3641  //~ maybe other state(?) -- but not the clipping path (and not sure
3642  //~ what else)
3643  //~ [this is likely the same situation as in type3D1()]
3644  splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy());
3645  splash->setStrokePattern(
3646                         transpGroup->origSplash->getStrokePattern()->copy());
3647  if (isolated) {
3648    for (i = 0; i < splashMaxColorComps; ++i) {
3649      color[i] = 0;
3650    }
3651    if (colorMode == splashModeXBGR8) color[3] = 255;
3652    splash->clear(color, 0);
3653  } else {
3654    SplashBitmap *shape = (knockout) ? transpGroup->shape :
3655                                       (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->shape : transpGroup->origBitmap;
3656    splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
3657    splash->setInNonIsolatedGroup(shape, tx, ty);
3658  }
3659  transpGroup->tBitmap = bitmap;
3660  state->shiftCTM(-tx, -ty);
3661  updateCTM(state, 0, 0, 0, 0, 0, 0);
3662  ++nestCount;
3663}
3664
3665void SplashOutputDev::endTransparencyGroup(GfxState *state) {
3666  // restore state
3667  --nestCount;
3668  delete splash;
3669  bitmap = transpGroupStack->origBitmap;
3670  colorMode = bitmap->getMode();
3671  splash = transpGroupStack->origSplash;
3672  state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
3673  updateCTM(state, 0, 0, 0, 0, 0, 0);
3674}
3675
3676void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
3677  SplashBitmap *tBitmap;
3678  SplashTransparencyGroup *transpGroup;
3679  GBool isolated;
3680  int tx, ty;
3681
3682  tx = transpGroupStack->tx;
3683  ty = transpGroupStack->ty;
3684  tBitmap = transpGroupStack->tBitmap;
3685  isolated = transpGroupStack->isolated;
3686
3687  // paint the transparency group onto the parent bitmap
3688  // - the clip path was set in the parent's state)
3689  if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
3690    SplashCoord knockoutOpacity = (transpGroupStack->next != NULL) ? transpGroupStack->next->knockoutOpacity
3691                                                                   : transpGroupStack->knockoutOpacity;
3692    splash->setOverprintMask(0xffffffff, gFalse);
3693    splash->composite(tBitmap, 0, 0, tx, ty,
3694              tBitmap->getWidth(), tBitmap->getHeight(),
3695              gFalse, !isolated, transpGroupStack->next != NULL && transpGroupStack->next->knockout, knockoutOpacity);
3696    if (transpGroupStack->next != NULL && transpGroupStack->next->shape != NULL) {
3697      transpGroupStack->next->knockout = gTrue;
3698    }
3699  }
3700
3701  // pop the stack
3702  transpGroup = transpGroupStack;
3703  transpGroupStack = transpGroup->next;
3704  if (transpGroupStack != NULL && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) {
3705    transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity;
3706  }
3707  delete transpGroup->shape;
3708  delete transpGroup;
3709
3710  delete tBitmap;
3711}
3712
3713void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
3714                                  GBool alpha, Function *transferFunc,
3715                                  GfxColor *backdropColor) {
3716  SplashBitmap *softMask, *tBitmap;
3717  Splash *tSplash;
3718  SplashTransparencyGroup *transpGroup;
3719  SplashColor color;
3720  SplashColorPtr p;
3721  GfxGray gray;
3722  GfxRGB rgb;
3723#if SPLASH_CMYK
3724  GfxCMYK cmyk;
3725#endif
3726  double lum, lum2;
3727  int tx, ty, x, y;
3728
3729  tx = transpGroupStack->tx;
3730  ty = transpGroupStack->ty;
3731  tBitmap = transpGroupStack->tBitmap;
3732
3733  // composite with backdrop color
3734  if (!alpha && tBitmap->getMode() != splashModeMono1) {
3735    //~ need to correctly handle the case where no blending color
3736    //~ space is given
3737    if (transpGroupStack->blendingColorSpace) {
3738      tSplash = new Splash(tBitmap, vectorAntialias,
3739                           transpGroupStack->origSplash->getScreen());
3740      switch (tBitmap->getMode()) {
3741      case splashModeMono1:
3742        // transparency is not supported in mono1 mode
3743        break;
3744      case splashModeMono8:
3745        transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
3746        color[0] = colToByte(gray);
3747        tSplash->compositeBackground(color);
3748        break;
3749      case splashModeXBGR8:
3750        color[3] = 255;
3751      case splashModeRGB8:
3752      case splashModeBGR8:
3753        transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
3754        color[0] = colToByte(rgb.r);
3755        color[1] = colToByte(rgb.g);
3756        color[2] = colToByte(rgb.b);
3757        tSplash->compositeBackground(color);
3758        break;
3759#if SPLASH_CMYK
3760      case splashModeCMYK8:
3761        transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
3762        color[0] = colToByte(cmyk.c);
3763        color[1] = colToByte(cmyk.m);
3764        color[2] = colToByte(cmyk.y);
3765        color[3] = colToByte(cmyk.k);
3766        tSplash->compositeBackground(color);
3767        break;
3768#endif
3769      }
3770      delete tSplash;
3771    }
3772  }
3773
3774  softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3775                              1, splashModeMono8, gFalse);
3776  unsigned char fill = 0;
3777  if (transpGroupStack->blendingColorSpace) {
3778        transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
3779        fill = colToByte(gray);
3780  }
3781  memset(softMask->getDataPtr(), fill,
3782         softMask->getRowSize() * softMask->getHeight());
3783  p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
3784  int xMax = tBitmap->getWidth();
3785  int yMax = tBitmap->getHeight();
3786  if (xMax + tx > bitmap->getWidth()) xMax = bitmap->getWidth() - tx;
3787  if (yMax + ty > bitmap->getHeight()) yMax = bitmap->getHeight() - ty;
3788  for (y = 0; y < yMax; ++y) {
3789    for (x = 0; x < xMax; ++x) {
3790      if (alpha) {
3791        p[x] = tBitmap->getAlpha(x, y);
3792      } else {
3793          tBitmap->getPixel(x, y, color);
3794          // convert to luminosity
3795          switch (tBitmap->getMode()) {
3796          case splashModeMono1:
3797          case splashModeMono8:
3798            lum = color[0] / 255.0;
3799            break;
3800          case splashModeXBGR8:
3801          case splashModeRGB8:
3802          case splashModeBGR8:
3803            lum = (0.3 / 255.0) * color[0] +
3804                  (0.59 / 255.0) * color[1] +
3805                  (0.11 / 255.0) * color[2];
3806            break;
3807#if SPLASH_CMYK
3808          case splashModeCMYK8:
3809            lum = (1 - color[3] / 255.0)
3810                  - (0.3 / 255.0) * color[0]
3811                  - (0.59 / 255.0) * color[1]
3812                  - (0.11 / 255.0) * color[2];
3813            if (lum < 0) {
3814              lum = 0;
3815            }
3816            break;
3817#endif
3818        }
3819        if (transferFunc) {
3820          transferFunc->transform(&lum, &lum2);
3821        } else {
3822          lum2 = lum;
3823        }
3824        p[x] = (int)(lum2 * 255.0 + 0.5);
3825      }
3826    }
3827        p += softMask->getRowSize();
3828  }
3829  splash->setSoftMask(softMask);
3830
3831  // pop the stack
3832  transpGroup = transpGroupStack;
3833  transpGroupStack = transpGroup->next;
3834  delete transpGroup;
3835
3836  delete tBitmap;
3837}
3838
3839void SplashOutputDev::clearSoftMask(GfxState *state) {
3840  splash->setSoftMask(NULL);
3841}
3842
3843void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
3844  splashColorCopy(paperColor, paperColorA);
3845}
3846
3847int SplashOutputDev::getBitmapWidth() {
3848  return bitmap->getWidth();
3849}
3850
3851int SplashOutputDev::getBitmapHeight() {
3852  return bitmap->getHeight();
3853}
3854
3855SplashBitmap *SplashOutputDev::takeBitmap() {
3856  SplashBitmap *ret;
3857
3858  ret = bitmap;
3859  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
3860                            colorMode != splashModeMono1, bitmapTopDown);
3861  return ret;
3862}
3863
3864void SplashOutputDev::getModRegion(int *xMin, int *yMin,
3865                                   int *xMax, int *yMax) {
3866  splash->getModRegion(xMin, yMin, xMax, yMax);
3867}
3868
3869void SplashOutputDev::clearModRegion() {
3870  splash->clearModRegion();
3871}
3872
3873#if 1 //~tmp: turn off anti-aliasing temporarily
3874GBool SplashOutputDev::getVectorAntialias() {
3875  return splash->getVectorAntialias();
3876}
3877
3878void SplashOutputDev::setVectorAntialias(GBool vaa) {
3879  vectorAntialias = vaa;
3880  splash->setVectorAntialias(vaa);
3881}
3882#endif
3883
3884void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHintingA)
3885{
3886  enableFreeTypeHinting = enable;
3887  enableSlightHinting = enableSlightHintingA;
3888}
3889
3890GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfx1, Catalog *catalog, Object *str,
3891                                        double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
3892                                        double *mat, double *bbox,
3893                                        int x0, int y0, int x1, int y1,
3894                                        double xStep, double yStep)
3895{
3896  PDFRectangle box;
3897  Gfx *gfx;
3898  Splash *formerSplash = splash;
3899  SplashBitmap *formerBitmap = bitmap;
3900  double width, height;
3901  int surface_width, surface_height, result_width, result_height, i;
3902  int repeatX, repeatY;
3903  SplashCoord matc[6];
3904  Matrix m1;
3905  double *ctm, savedCTM[6];
3906  double kx, ky, sx, sy;
3907
3908  width = bbox[2] - bbox[0];
3909  height = bbox[3] - bbox[1];
3910
3911  if (xStep != width || yStep != height)
3912    return gFalse;
3913
3914  // calculate offsets
3915  ctm = state->getCTM();
3916  for (i = 0; i < 6; ++i) {
3917    savedCTM[i] = ctm[i];
3918  }
3919  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3920  state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]);
3921  ctm = state->getCTM();
3922  for (i = 0; i < 6; ++i) {
3923    if (!isfinite(ctm[i]))
3924      return gFalse;
3925  }
3926  matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4];
3927  matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5];
3928  if (splashAbs(ctm[1]) > splashAbs(ctm[0])) {
3929    kx = -ctm[1];
3930    ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1];
3931  } else {
3932    kx = ctm[0];
3933    ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0];
3934  }
3935  result_width = (int) ceil(fabs(kx * width * (x1 - x0)));
3936  result_height = (int) ceil(fabs(ky * height * (y1 - y0)));
3937  kx = state->getHDPI() / 72.0;
3938  ky = state->getVDPI() / 72.0;
3939  m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx;
3940  m1.m[1] = 0;
3941  m1.m[2] = 0;
3942  m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky;
3943  m1.m[4] = 0;
3944  m1.m[5] = 0;
3945  m1.transform(width, height, &kx, &ky);
3946  surface_width = (int) ceil (fabs(kx));
3947  surface_height = (int) ceil (fabs(ky));
3948
3949  sx = (double) result_width / (surface_width * (x1 - x0));
3950  sy = (double) result_height / (surface_height * (y1 - y0));
3951  m1.m[0] *= sx;
3952  m1.m[3] *= sy;
3953  m1.transform(width, height, &kx, &ky);
3954
3955  if(fabs(kx) < 1 && fabs(ky) < 1) {
3956    kx = std::min<double>(kx, ky);
3957    ky = 2 / kx;
3958    m1.m[0] *= ky;
3959    m1.m[3] *= ky;
3960    m1.transform(width, height, &kx, &ky);
3961    surface_width = (int) ceil (fabs(kx));
3962    surface_height = (int) ceil (fabs(ky));
3963    repeatX = x1 - x0;
3964    repeatY = y1 - y0;
3965  } else {
3966    while(fabs(kx) > 16384 || fabs(ky) > 16384) {
3967      // limit pattern bitmap size
3968      m1.m[0] /= 2;
3969      m1.m[3] /= 2;
3970      m1.transform(width, height, &kx, &ky);
3971    }
3972    surface_width = (int) ceil (fabs(kx));
3973    surface_height = (int) ceil (fabs(ky));
3974    // adjust repeat values to completely fill region
3975    repeatX = result_width / surface_width;
3976    repeatY = result_height / surface_height;
3977    if (surface_width * repeatX < result_width)
3978      repeatX++;
3979    if (surface_height * repeatY < result_height)
3980      repeatY++;
3981    if (x1 - x0 > repeatX)
3982      repeatX = x1 - x0;
3983    if (y1 - y0 > repeatY)
3984      repeatY = y1 - y0;
3985  }
3986  // restore CTM and calculate rotate and scale with rounded matric
3987  state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
3988  state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
3989  state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]);
3990  ctm = state->getCTM();
3991  matc[0] = ctm[0];
3992  matc[1] = ctm[1];
3993  matc[2] = ctm[2];
3994  matc[3] = ctm[3];
3995
3996  if (surface_width == 0 || surface_height == 0)
3997    return gFalse;
3998  m1.transform(bbox[0], bbox[1], &kx, &ky);
3999  m1.m[4] = -kx;
4000  m1.m[5] = -ky;
4001
4002  bitmap = new SplashBitmap(surface_width, surface_height, colorMode != splashModeMono1,
4003                            (paintType == 1) ? colorMode : splashModeMono8, gTrue);
4004  memset(bitmap->getAlphaPtr(), 0, bitmap->getWidth() * bitmap->getHeight());
4005  if (paintType == 2) {
4006#if SPLASH_CMYK
4007    memset(bitmap->getDataPtr(), (colorMode == splashModeCMYK8) ? 0x00 : 0xFF, bitmap->getRowSize() * bitmap->getHeight());
4008#else
4009    memset(bitmap->getDataPtr(), 0xFF, bitmap->getRowSize() * bitmap->getHeight());
4010#endif
4011  }
4012  splash = new Splash(bitmap, gTrue);
4013  splash->setMinLineWidth(globalParams->getMinLineWidth());
4014
4015  box.x1 = bbox[0]; box.y1 = bbox[1];
4016  box.x2 = bbox[2]; box.y2 = bbox[3];
4017  gfx = new Gfx(doc, this, resDict, &box, NULL);
4018  // set pattern transformation matrix
4019  gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4020  updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4021  gfx->display(str);
4022  splash = formerSplash;
4023  TilingSplashOutBitmap imgData;
4024  imgData.bitmap = bitmap;
4025  imgData.paintType = paintType;
4026  imgData.pattern = splash->getFillPattern();
4027  imgData.colorMode = colorMode;
4028  imgData.y = 0;
4029  imgData.repeatX = repeatX;
4030  imgData.repeatY = repeatY;
4031  SplashBitmap *tBitmap = bitmap;
4032  bitmap = formerBitmap;
4033  result_width = tBitmap->getWidth() * imgData.repeatX;
4034  result_height = tBitmap->getHeight() * imgData.repeatY;
4035
4036  if (splashAbs(matc[1]) > splashAbs(matc[0])) {
4037    kx = -matc[1];
4038    ky = matc[2] - (matc[0] * matc[3]) / matc[1];
4039  } else {
4040    kx = matc[0];
4041    ky = matc[3] - (matc[1] * matc[2]) / matc[0];
4042  }
4043  kx = result_width / (fabs(kx) + 1);
4044  ky = result_height / (fabs(ky) + 1);
4045  state->concatCTM(kx, 0, 0, ky, 0, 0);
4046  ctm = state->getCTM();
4047  matc[0] = ctm[0];
4048  matc[1] = ctm[1];
4049  matc[2] = ctm[2];
4050  matc[3] = ctm[3];
4051  splash->drawImage(&tilingBitmapSrc, &imgData, colorMode, gTrue, result_width, result_height, matc);
4052  delete tBitmap;
4053  delete gfx;
4054  return gTrue;
4055}
4056
4057GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
4058{
4059  GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode();
4060  GBool bDirectColorTranslation = gFalse; // triggers an optimization.
4061  switch (colorMode) {
4062    case splashModeRGB8:
4063      bDirectColorTranslation = (shadingMode == csDeviceRGB);
4064    break;
4065#if SPLASH_CMYK
4066    case splashModeCMYK8:
4067      bDirectColorTranslation = (shadingMode == csDeviceCMYK);
4068    break;
4069#endif
4070    default:
4071    break;
4072  }
4073  SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading, colorMode);
4074  // restore vector antialias because we support it here
4075  if (shading->isParameterized()) {
4076    GBool vaa = getVectorAntialias();
4077    GBool retVal = gFalse;
4078    setVectorAntialias(gTrue);
4079    retVal = splash->gouraudTriangleShadedFill(splashShading);
4080    setVectorAntialias(vaa);
4081    return retVal;
4082  }
4083  return gFalse;
4084}
4085
4086GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) {
4087  double xMin, yMin, xMax, yMax;
4088  SplashPath *path;
4089  GBool vaa = getVectorAntialias();
4090  // restore vector antialias because we support it here
4091  setVectorAntialias(gTrue);
4092
4093  GBool retVal = gFalse;
4094  // get the clip region bbox
4095  if (pattern->getShading()->getHasBBox()) {
4096    pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax);
4097  } else {
4098    state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
4099
4100    xMin = floor (xMin);
4101    yMin = floor (yMin);
4102    xMax = ceil (xMax);
4103    yMax = ceil (yMax);
4104
4105    {
4106      Matrix ctm, ictm;
4107      double x[4], y[4];
4108      int i;
4109
4110      state->getCTM(&ctm);
4111      ctm.invertTo(&ictm);
4112
4113      ictm.transform(xMin, yMin, &x[0], &y[0]);
4114      ictm.transform(xMax, yMin, &x[1], &y[1]);
4115      ictm.transform(xMin, yMax, &x[2], &y[2]);
4116      ictm.transform(xMax, yMax, &x[3], &y[3]);
4117
4118      xMin = xMax = x[0];
4119      yMin = yMax = y[0];
4120      for (i = 1; i < 4; i++) {
4121        xMin = std::min<double>(xMin, x[i]);
4122        yMin = std::min<double>(yMin, y[i]);
4123        xMax = std::max<double>(xMax, x[i]);
4124        yMax = std::max<double>(yMax, y[i]);
4125      }
4126    }
4127  }
4128
4129  // fill the region
4130  state->moveTo(xMin, yMin);
4131  state->lineTo(xMax, yMin);
4132  state->lineTo(xMax, yMax);
4133  state->lineTo(xMin, yMax);
4134  state->closePath();
4135  path = convertPath(state, state->getPath(), gTrue);
4136
4137  setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(),
4138                   state->getOverprintMode(), state->getFillColor());
4139  retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
4140  state->clearPath();
4141  setVectorAntialias(vaa);
4142  delete path;
4143
4144  return retVal;
4145}
4146
4147GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
4148  SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
4149  GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4150
4151  delete pattern;
4152
4153  return retVal;
4154}
4155
4156GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) {
4157  SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading);
4158  GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4159
4160  delete pattern;
4161
4162  return retVal;
4163}
Note: See TracBrowser for help on using the browser.