source: trunk/poppler/mypoppler/poppler/SplashOutputDev.cc @ 461

Last change on this file since 461 was 461, checked in by Silvan Scherrer, 11 years ago

poppler update to 0.14.2

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