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

Last change on this file since 254 was 254, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.8.6

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