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

Last change on this file since 277 was 277, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.3

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