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

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

PDF plugin: poppler library updated to version 0.8.3

File size: 69.7 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          goto err2;
1129        codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
1130        delete ff;
1131      }
1132      if (!(fontFile = fontEngine->loadTrueTypeFont(
1133                           id,
1134                           fontsrc,
1135                           codeToGID, n, faceIndex))) {
1136        error(-1, "Couldn't create a font for '%s'",
1137              gfxFont->getName() ? gfxFont->getName()->getCString()
1138                                 : "(unnamed)");
1139        goto err2;
1140      }
1141      break;
1142    default:
1143      // this shouldn't happen
1144      goto err2;
1145    }
1146    fontFile->doAdjustMatrix = gTrue;
1147  }
1148
1149  // get the font matrix
1150  textMat = state->getTextMat();
1151  fontSize = state->getFontSize();
1152  m11 = textMat[0] * fontSize * state->getHorizScaling();
1153  m12 = textMat[1] * fontSize * state->getHorizScaling();
1154  m21 = textMat[2] * fontSize;
1155  m22 = textMat[3] * fontSize;
1156
1157  // create the scaled font
1158  mat[0] = m11;  mat[1] = m12;
1159  mat[2] = m21;  mat[3] = m22;
1160  font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1161
1162  // for substituted fonts: adjust the font matrix -- compare the
1163  // width of 'm' in the original font and the substituted font
1164  if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) {
1165    double w1, w2;
1166    CharCode code;
1167    char *name;
1168    for (code = 0; code < 256; ++code) {
1169      if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
1170          name[0] == 'm' && name[1] == '\0') {
1171        break;
1172      }
1173    }
1174    if (code < 256) {
1175      w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
1176      w2 = font->getGlyphAdvance(code);
1177      if (!gfxFont->isSymbolic() && w2 > 0) {
1178        // if real font is substantially narrower than substituted
1179        // font, reduce the font size accordingly
1180        if (w1 > 0.01 && w1 < 0.9 * w2) {
1181          w1 /= w2;
1182          m11 *= w1;
1183          m21 *= w1;
1184          recreateFont = gTrue;
1185        }
1186      }
1187    }
1188  }
1189
1190  if (recreateFont)
1191  {
1192    mat[0] = m11;  mat[1] = m12;
1193    mat[2] = m21;  mat[3] = m22;
1194    font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
1195  }
1196
1197  if (fontsrc && !fontsrc->isFile)
1198      fontsrc->unref();
1199  return;
1200
1201 err2:
1202  delete id;
1203 err1:
1204  if (fontsrc && !fontsrc->isFile)
1205      fontsrc->unref();
1206  return;
1207}
1208
1209void SplashOutputDev::stroke(GfxState *state) {
1210  SplashPath *path;
1211
1212  if (state->getStrokeColorSpace()->isNonMarking()) {
1213    return;
1214  }
1215  path = convertPath(state, state->getPath());
1216  splash->stroke(path);
1217  delete path;
1218}
1219
1220void SplashOutputDev::fill(GfxState *state) {
1221  SplashPath *path;
1222
1223  if (state->getFillColorSpace()->isNonMarking()) {
1224    return;
1225  }
1226  path = convertPath(state, state->getPath());
1227  splash->fill(path, gFalse);
1228  delete path;
1229}
1230
1231void SplashOutputDev::eoFill(GfxState *state) {
1232  SplashPath *path;
1233
1234  if (state->getFillColorSpace()->isNonMarking()) {
1235    return;
1236  }
1237  path = convertPath(state, state->getPath());
1238  splash->fill(path, gTrue);
1239  delete path;
1240}
1241
1242void SplashOutputDev::clip(GfxState *state) {
1243  SplashPath *path;
1244
1245  path = convertPath(state, state->getPath());
1246  splash->clipToPath(path, gFalse);
1247  delete path;
1248}
1249
1250void SplashOutputDev::eoClip(GfxState *state) {
1251  SplashPath *path;
1252
1253  path = convertPath(state, state->getPath());
1254  splash->clipToPath(path, gTrue);
1255  delete path;
1256}
1257
1258void SplashOutputDev::clipToStrokePath(GfxState *state) {
1259  SplashPath *path, *path2;
1260
1261  path = convertPath(state, state->getPath());
1262  path2 = splash->makeStrokePath(path);
1263  delete path;
1264  splash->clipToPath(path2, gFalse);
1265  delete path2;
1266}
1267
1268SplashPath *SplashOutputDev::convertPath(GfxState * /*state*/, GfxPath *path) {
1269  SplashPath *sPath;
1270  GfxSubpath *subpath;
1271  int i, j;
1272
1273  sPath = new SplashPath();
1274  for (i = 0; i < path->getNumSubpaths(); ++i) {
1275    subpath = path->getSubpath(i);
1276    if (subpath->getNumPoints() > 0) {
1277      sPath->moveTo((SplashCoord)subpath->getX(0),
1278                    (SplashCoord)subpath->getY(0));
1279      j = 1;
1280      while (j < subpath->getNumPoints()) {
1281        if (subpath->getCurve(j)) {
1282          sPath->curveTo((SplashCoord)subpath->getX(j),
1283                         (SplashCoord)subpath->getY(j),
1284                         (SplashCoord)subpath->getX(j+1),
1285                         (SplashCoord)subpath->getY(j+1),
1286                         (SplashCoord)subpath->getX(j+2),
1287                         (SplashCoord)subpath->getY(j+2));
1288          j += 3;
1289        } else {
1290          sPath->lineTo((SplashCoord)subpath->getX(j),
1291                        (SplashCoord)subpath->getY(j));
1292          ++j;
1293        }
1294      }
1295      if (subpath->isClosed()) {
1296        sPath->close();
1297      }
1298    }
1299  }
1300  return sPath;
1301}
1302
1303void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1304                               double dx, double dy,
1305                               double originX, double originY,
1306                               CharCode code, int nBytes,
1307                               Unicode *u, int uLen) {
1308  SplashPath *path;
1309  int render;
1310
1311  // check for invisible text -- this is used by Acrobat Capture
1312  render = state->getRender();
1313  if (render == 3) {
1314    return;
1315  }
1316
1317  if (needFontUpdate) {
1318    doUpdateFont(state);
1319  }
1320  if (!font) {
1321    return;
1322  }
1323
1324  x -= originX;
1325  y -= originY;
1326
1327  // fill
1328  if (!(render & 1)) {
1329    if (!state->getFillColorSpace()->isNonMarking()) {
1330      splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
1331    }
1332  }
1333
1334  // stroke
1335  if ((render & 3) == 1 || (render & 3) == 2) {
1336    if (!state->getStrokeColorSpace()->isNonMarking()) {
1337      if ((path = font->getGlyphPath(code))) {
1338        path->offset((SplashCoord)x, (SplashCoord)y);
1339        splash->stroke(path);
1340        delete path;
1341      }
1342    }
1343  }
1344
1345  // clip
1346  if (render & 4) {
1347    if ((path = font->getGlyphPath(code))) {
1348      path->offset((SplashCoord)x, (SplashCoord)y);
1349      if (textClipPath) {
1350        textClipPath->append(path);
1351        delete path;
1352      } else {
1353        textClipPath = path;
1354      }
1355    }
1356  }
1357}
1358
1359GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1360                                      double dx, double dy,
1361                                      CharCode code, Unicode *u, int uLen) {
1362  GfxFont *gfxFont;
1363  Ref *fontID;
1364  double *ctm, *bbox;
1365  T3FontCache *t3Font;
1366  T3GlyphStack *t3gs;
1367  GBool validBBox;
1368  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1369  int i, j;
1370
1371  if (!(gfxFont = state->getFont())) {
1372    return gFalse;
1373  }
1374  fontID = gfxFont->getID();
1375  ctm = state->getCTM();
1376  state->transform(0, 0, &xt, &yt);
1377
1378  // is it the first (MRU) font in the cache?
1379  if (!(nT3Fonts > 0 &&
1380        t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1381
1382    // is the font elsewhere in the cache?
1383    for (i = 1; i < nT3Fonts; ++i) {
1384      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1385        t3Font = t3FontCache[i];
1386        for (j = i; j > 0; --j) {
1387          t3FontCache[j] = t3FontCache[j - 1];
1388        }
1389        t3FontCache[0] = t3Font;
1390        break;
1391      }
1392    }
1393    if (i >= nT3Fonts) {
1394
1395      // create new entry in the font cache
1396      if (nT3Fonts == splashOutT3FontCacheSize) {
1397        delete t3FontCache[nT3Fonts - 1];
1398        --nT3Fonts;
1399      }
1400      for (j = nT3Fonts; j > 0; --j) {
1401        t3FontCache[j] = t3FontCache[j - 1];
1402      }
1403      ++nT3Fonts;
1404      bbox = gfxFont->getFontBBox();
1405      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1406        // unspecified bounding box -- just take a guess
1407        xMin = xt - 5;
1408        xMax = xMin + 30;
1409        yMax = yt + 15;
1410        yMin = yMax - 45;
1411        validBBox = gFalse;
1412      } else {
1413        state->transform(bbox[0], bbox[1], &x1, &y1);
1414        xMin = xMax = x1;
1415        yMin = yMax = y1;
1416        state->transform(bbox[0], bbox[3], &x1, &y1);
1417        if (x1 < xMin) {
1418          xMin = x1;
1419        } else if (x1 > xMax) {
1420          xMax = x1;
1421        }
1422        if (y1 < yMin) {
1423          yMin = y1;
1424        } else if (y1 > yMax) {
1425          yMax = y1;
1426        }
1427        state->transform(bbox[2], bbox[1], &x1, &y1);
1428        if (x1 < xMin) {
1429          xMin = x1;
1430        } else if (x1 > xMax) {
1431          xMax = x1;
1432        }
1433        if (y1 < yMin) {
1434          yMin = y1;
1435        } else if (y1 > yMax) {
1436          yMax = y1;
1437        }
1438        state->transform(bbox[2], bbox[3], &x1, &y1);
1439        if (x1 < xMin) {
1440          xMin = x1;
1441        } else if (x1 > xMax) {
1442          xMax = x1;
1443        }
1444        if (y1 < yMin) {
1445          yMin = y1;
1446        } else if (y1 > yMax) {
1447          yMax = y1;
1448        }
1449        validBBox = gTrue;
1450      }
1451      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
1452                                       (int)floor(xMin - xt),
1453                                       (int)floor(yMin - yt),
1454                                       (int)ceil(xMax) - (int)floor(xMin) + 3,
1455                                       (int)ceil(yMax) - (int)floor(yMin) + 3,
1456                                       validBBox,
1457                                       colorMode != splashModeMono1);
1458    }
1459  }
1460  t3Font = t3FontCache[0];
1461
1462  // is the glyph in the cache?
1463  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1464  for (j = 0; j < t3Font->cacheAssoc; ++j) {
1465    if (t3Font->cacheTags != NULL) {
1466      if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
1467        t3Font->cacheTags[i+j].code == code) {
1468        drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
1469                     t3Font->cacheData + (i+j) * t3Font->glyphSize);
1470        return gTrue;
1471      }
1472    }
1473  }
1474
1475  // push a new Type 3 glyph record
1476  t3gs = new T3GlyphStack();
1477  t3gs->next = t3GlyphStack;
1478  t3GlyphStack = t3gs;
1479  t3GlyphStack->code = code;
1480  t3GlyphStack->cache = t3Font;
1481  t3GlyphStack->cacheTag = NULL;
1482  t3GlyphStack->cacheData = NULL;
1483
1484  return gFalse;
1485}
1486
1487void SplashOutputDev::endType3Char(GfxState *state) {
1488  T3GlyphStack *t3gs;
1489  double *ctm;
1490
1491  if (t3GlyphStack->cacheTag) {
1492    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
1493           t3GlyphStack->cache->glyphSize);
1494    delete bitmap;
1495    delete splash;
1496    bitmap = t3GlyphStack->origBitmap;
1497    splash = t3GlyphStack->origSplash;
1498    ctm = state->getCTM();
1499    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1500                  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
1501    updateCTM(state, 0, 0, 0, 0, 0, 0);
1502    drawType3Glyph(t3GlyphStack->cache,
1503                   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
1504  }
1505  t3gs = t3GlyphStack;
1506  t3GlyphStack = t3gs->next;
1507  delete t3gs;
1508}
1509
1510void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1511}
1512
1513void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1514                              double llx, double lly, double urx, double ury) {
1515  double *ctm;
1516  T3FontCache *t3Font;
1517  SplashColor color;
1518  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1519  int i, j;
1520
1521  t3Font = t3GlyphStack->cache;
1522
1523  // check for a valid bbox
1524  state->transform(0, 0, &xt, &yt);
1525  state->transform(llx, lly, &x1, &y1);
1526  xMin = xMax = x1;
1527  yMin = yMax = y1;
1528  state->transform(llx, ury, &x1, &y1);
1529  if (x1 < xMin) {
1530    xMin = x1;
1531  } else if (x1 > xMax) {
1532    xMax = x1;
1533  }
1534  if (y1 < yMin) {
1535    yMin = y1;
1536  } else if (y1 > yMax) {
1537    yMax = y1;
1538  }
1539  state->transform(urx, lly, &x1, &y1);
1540  if (x1 < xMin) {
1541    xMin = x1;
1542  } else if (x1 > xMax) {
1543    xMax = x1;
1544  }
1545  if (y1 < yMin) {
1546    yMin = y1;
1547  } else if (y1 > yMax) {
1548    yMax = y1;
1549  }
1550  state->transform(urx, ury, &x1, &y1);
1551  if (x1 < xMin) {
1552    xMin = x1;
1553  } else if (x1 > xMax) {
1554    xMax = x1;
1555  }
1556  if (y1 < yMin) {
1557    yMin = y1;
1558  } else if (y1 > yMax) {
1559    yMax = y1;
1560  }
1561  if (xMin - xt < t3Font->glyphX ||
1562      yMin - yt < t3Font->glyphY ||
1563      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1564      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1565    if (t3Font->validBBox) {
1566      error(-1, "Bad bounding box in Type 3 glyph");
1567    }
1568    return;
1569  }
1570
1571  // allocate a cache entry
1572  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1573  for (j = 0; j < t3Font->cacheAssoc; ++j) {
1574    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1575      t3Font->cacheTags[i+j].mru = 0x8000;
1576      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1577      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1578      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1579    } else {
1580      ++t3Font->cacheTags[i+j].mru;
1581    }
1582  }
1583
1584  // save state
1585  t3GlyphStack->origBitmap = bitmap;
1586  t3GlyphStack->origSplash = splash;
1587  ctm = state->getCTM();
1588  t3GlyphStack->origCTM4 = ctm[4];
1589  t3GlyphStack->origCTM5 = ctm[5];
1590
1591  // create the temporary bitmap
1592  if (colorMode == splashModeMono1) {
1593    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1594                              splashModeMono1, gFalse);
1595    splash = new Splash(bitmap, gFalse,
1596                        t3GlyphStack->origSplash->getScreen());
1597    color[0] = 0;
1598    splash->clear(color);
1599    color[0] = 1;
1600  } else {
1601    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1602                              splashModeMono8, gFalse);
1603    splash = new Splash(bitmap, vectorAntialias,
1604                        t3GlyphStack->origSplash->getScreen());
1605    color[0] = 0x00;
1606    splash->clear(color);
1607    color[0] = 0xff;
1608  }
1609  splash->setFillPattern(new SplashSolidColor(color));
1610  splash->setStrokePattern(new SplashSolidColor(color));
1611  //~ this should copy other state from t3GlyphStack->origSplash?
1612  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1613                -t3Font->glyphX, -t3Font->glyphY);
1614  updateCTM(state, 0, 0, 0, 0, 0, 0);
1615}
1616
1617void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1618                                     T3FontCacheTag * /*tag*/, Guchar *data) {
1619  SplashGlyphBitmap glyph;
1620
1621  glyph.x = -t3Font->glyphX;
1622  glyph.y = -t3Font->glyphY;
1623  glyph.w = t3Font->glyphW;
1624  glyph.h = t3Font->glyphH;
1625  glyph.aa = colorMode != splashModeMono1;
1626  glyph.data = data;
1627  glyph.freeData = gFalse;
1628  splash->fillGlyph(0, 0, &glyph);
1629}
1630
1631void SplashOutputDev::endTextObject(GfxState *state) {
1632  if (textClipPath) {
1633    splash->clipToPath(textClipPath, gFalse);
1634    delete textClipPath;
1635    textClipPath = NULL;
1636  }
1637}
1638
1639struct SplashOutImageMaskData {
1640  ImageStream *imgStr;
1641  GBool invert;
1642  int width, height, y;
1643};
1644
1645GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
1646  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1647  Guchar *p;
1648  SplashColorPtr q;
1649  int x;
1650
1651  if (imgMaskData->y == imgMaskData->height) {
1652    return gFalse;
1653  }
1654  for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
1655       x < imgMaskData->width;
1656       ++x) {
1657    *q++ = *p++ ^ imgMaskData->invert;
1658  }
1659  ++imgMaskData->y;
1660  return gTrue;
1661}
1662
1663void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1664                                    int width, int height, GBool invert,
1665                                    GBool inlineImg) {
1666  double *ctm;
1667  SplashCoord mat[6];
1668  SplashOutImageMaskData imgMaskData;
1669
1670  if (state->getFillColorSpace()->isNonMarking()) {
1671    return;
1672  }
1673
1674  ctm = state->getCTM();
1675  mat[0] = ctm[0];
1676  mat[1] = ctm[1];
1677  mat[2] = -ctm[2];
1678  mat[3] = -ctm[3];
1679  mat[4] = ctm[2] + ctm[4];
1680  mat[5] = ctm[3] + ctm[5];
1681
1682  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1683  imgMaskData.imgStr->reset();
1684  imgMaskData.invert = invert ? 0 : 1;
1685  imgMaskData.width = width;
1686  imgMaskData.height = height;
1687  imgMaskData.y = 0;
1688
1689  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat,
1690                        t3GlyphStack != NULL);
1691  if (inlineImg) {
1692    while (imgMaskData.y < height) {
1693      imgMaskData.imgStr->getLine();
1694      ++imgMaskData.y;
1695    }
1696  }
1697
1698  delete imgMaskData.imgStr;
1699  str->close();
1700}
1701
1702struct SplashOutImageData {
1703  ImageStream *imgStr;
1704  GfxImageColorMap *colorMap;
1705  SplashColorPtr lookup;
1706  int *maskColors;
1707  SplashColorMode colorMode;
1708  int width, height, y;
1709};
1710
1711GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
1712                                Guchar * /*alphaLine*/) {
1713  SplashOutImageData *imgData = (SplashOutImageData *)data;
1714  Guchar *p;
1715  SplashColorPtr q, col;
1716  GfxRGB rgb;
1717  GfxGray gray;
1718#if SPLASH_CMYK
1719  GfxCMYK cmyk;
1720#endif
1721  int nComps, x;
1722
1723  if (imgData->y == imgData->height) {
1724    return gFalse;
1725  }
1726
1727  nComps = imgData->colorMap->getNumPixelComps();
1728
1729  if (imgData->lookup) {
1730    switch (imgData->colorMode) {
1731  case splashModeMono1:
1732  case splashModeMono8:
1733      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1734           x < imgData->width;
1735           ++x, ++p) {
1736        *q++ = imgData->lookup[*p];
1737      }
1738    break;
1739  case splashModeRGB8:
1740  case splashModeBGR8:
1741      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1742           x < imgData->width;
1743           ++x, ++p) {
1744        col = &imgData->lookup[3 * *p];
1745        *q++ = col[0];
1746        *q++ = col[1];
1747        *q++ = col[2];
1748      }
1749    break;
1750  case splashModeXBGR8:
1751      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1752           x < imgData->width;
1753           ++x, ++p) {
1754        col = &imgData->lookup[4 * *p];
1755        *q++ = col[0];
1756        *q++ = col[1];
1757        *q++ = col[2];
1758        *q++ = col[3];
1759      }
1760      break;
1761#if SPLASH_CMYK
1762    case splashModeCMYK8:
1763      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1764           x < imgData->width;
1765           ++x, ++p) {
1766        col = &imgData->lookup[4 * *p];
1767        *q++ = col[0];
1768        *q++ = col[1];
1769        *q++ = col[2];
1770        *q++ = col[3];
1771      }
1772      break;
1773#endif
1774  }
1775  } else {
1776    switch (imgData->colorMode) {
1777    case splashModeMono1:
1778    case splashModeMono8:
1779      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1780           x < imgData->width;
1781           ++x, p += nComps) {
1782        imgData->colorMap->getGray(p, &gray);
1783        *q++ = colToByte(gray);
1784      }
1785        break;
1786        case splashModeXBGR8:
1787    case splashModeRGB8:
1788    case splashModeBGR8:
1789      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1790           x < imgData->width;
1791           ++x, p += nComps) {
1792        imgData->colorMap->getRGB(p, &rgb);
1793        *q++ = colToByte(rgb.r);
1794        *q++ = colToByte(rgb.g);
1795        *q++ = colToByte(rgb.b);
1796        if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
1797      }
1798      break;
1799#if SPLASH_CMYK
1800    case splashModeCMYK8:
1801      for (x = 0, p = imgData->imgStr->getLine(), q = colorLine;
1802           x < imgData->width;
1803           ++x, p += nComps) {
1804        imgData->colorMap->getCMYK(p, &cmyk);
1805        *q++ = colToByte(cmyk.c);
1806        *q++ = colToByte(cmyk.m);
1807        *q++ = colToByte(cmyk.y);
1808        *q++ = colToByte(cmyk.k);
1809      }
1810      break;
1811#endif
1812    }
1813  }
1814
1815  ++imgData->y;
1816  return gTrue;
1817}
1818
1819GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
1820                                     Guchar *alphaLine) {
1821  SplashOutImageData *imgData = (SplashOutImageData *)data;
1822  Guchar *p, *aq;
1823  SplashColorPtr q, col;
1824  GfxRGB rgb;
1825  GfxGray gray;
1826#if SPLASH_CMYK
1827  GfxCMYK cmyk;
1828#endif
1829  Guchar alpha;
1830  int nComps, x, i;
1831
1832  if (imgData->y == imgData->height) {
1833    return gFalse;
1834  }
1835
1836  nComps = imgData->colorMap->getNumPixelComps();
1837
1838  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
1839       x < imgData->width;
1840       ++x, p += nComps) {
1841    alpha = 0;
1842    for (i = 0; i < nComps; ++i) {
1843      if (p[i] < imgData->maskColors[2*i] ||
1844          p[i] > imgData->maskColors[2*i+1]) {
1845        alpha = 0xff;
1846        break;
1847      }
1848    }
1849    if (imgData->lookup) {
1850      switch (imgData->colorMode) {
1851      case splashModeMono1:
1852      case splashModeMono8:
1853        *q++ = imgData->lookup[*p];
1854        *aq++ = alpha;
1855        break;
1856      case splashModeRGB8:
1857      case splashModeBGR8:
1858        col = &imgData->lookup[3 * *p];
1859        *q++ = col[0];
1860        *q++ = col[1];
1861        *q++ = col[2];
1862        *aq++ = alpha;
1863        break;
1864      case splashModeXBGR8:
1865        col = &imgData->lookup[4 * *p];
1866        *q++ = col[0];
1867        *q++ = col[1];
1868        *q++ = col[2];
1869        *q++ = 255;
1870        *aq++ = alpha;
1871        break;
1872#if SPLASH_CMYK
1873      case splashModeCMYK8:
1874        col = &imgData->lookup[4 * *p];
1875        *q++ = col[0];
1876        *q++ = col[1];
1877        *q++ = col[2];
1878        *q++ = col[3];
1879        *aq++ = alpha;
1880        break;
1881#endif
1882      }
1883    } else {
1884      switch (imgData->colorMode) {
1885      case splashModeMono1:
1886      case splashModeMono8:
1887        imgData->colorMap->getGray(p, &gray);
1888        *q++ = colToByte(gray);
1889        *aq++ = alpha;
1890        break;
1891      case splashModeXBGR8:
1892      case splashModeRGB8:
1893      case splashModeBGR8:
1894        imgData->colorMap->getRGB(p, &rgb);
1895        *q++ = colToByte(rgb.r);
1896        *q++ = colToByte(rgb.g);
1897        *q++ = colToByte(rgb.b);
1898        if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
1899        *aq++ = alpha;
1900        break;
1901#if SPLASH_CMYK
1902      case splashModeCMYK8:
1903        imgData->colorMap->getCMYK(p, &cmyk);
1904        *q++ = colToByte(cmyk.c);
1905        *q++ = colToByte(cmyk.m);
1906        *q++ = colToByte(cmyk.y);
1907        *q++ = colToByte(cmyk.k);
1908        *aq++ = alpha;
1909        break;
1910#endif
1911      }
1912    }
1913  }
1914
1915  ++imgData->y;
1916  return gTrue;
1917}
1918
1919void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1920                                int width, int height,
1921                                GfxImageColorMap *colorMap,
1922                                int *maskColors, GBool inlineImg) {
1923  double *ctm;
1924  SplashCoord mat[6];
1925  SplashOutImageData imgData;
1926  SplashColorMode srcMode;
1927  SplashImageSource src;
1928  GfxGray gray;
1929  GfxRGB rgb;
1930#if SPLASH_CMYK
1931  GfxCMYK cmyk;
1932#endif
1933  Guchar pix;
1934  int n, i;
1935
1936  ctm = state->getCTM();
1937  mat[0] = ctm[0];
1938  mat[1] = ctm[1];
1939  mat[2] = -ctm[2];
1940  mat[3] = -ctm[3];
1941  mat[4] = ctm[2] + ctm[4];
1942  mat[5] = ctm[3] + ctm[5];
1943
1944  imgData.imgStr = new ImageStream(str, width,
1945                                   colorMap->getNumPixelComps(),
1946                                   colorMap->getBits());
1947  imgData.imgStr->reset();
1948  imgData.colorMap = colorMap;
1949  imgData.maskColors = maskColors;
1950  imgData.colorMode = colorMode;
1951  imgData.width = width;
1952  imgData.height = height;
1953  imgData.y = 0;
1954
1955  // special case for one-channel (monochrome/gray/separation) images:
1956  // build a lookup table here
1957  imgData.lookup = NULL;
1958  if (colorMap->getNumPixelComps() == 1) {
1959    n = 1 << colorMap->getBits();
1960    switch (colorMode) {
1961    case splashModeMono1:
1962    case splashModeMono8:
1963      imgData.lookup = (SplashColorPtr)gmalloc(n);
1964      for (i = 0; i < n; ++i) {
1965        pix = (Guchar)i;
1966        colorMap->getGray(&pix, &gray);
1967        imgData.lookup[i] = colToByte(gray);
1968      }
1969      break;
1970    case splashModeRGB8:
1971    case splashModeBGR8:
1972      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
1973      for (i = 0; i < n; ++i) {
1974        pix = (Guchar)i;
1975        colorMap->getRGB(&pix, &rgb);
1976        imgData.lookup[3*i] = colToByte(rgb.r);
1977        imgData.lookup[3*i+1] = colToByte(rgb.g);
1978        imgData.lookup[3*i+2] = colToByte(rgb.b);
1979      }
1980      break;
1981    case splashModeXBGR8:
1982      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
1983      for (i = 0; i < n; ++i) {
1984        pix = (Guchar)i;
1985        colorMap->getRGB(&pix, &rgb);
1986        imgData.lookup[4*i] = colToByte(rgb.r);
1987        imgData.lookup[4*i+1] = colToByte(rgb.g);
1988        imgData.lookup[4*i+2] = colToByte(rgb.b);
1989        imgData.lookup[4*i+3] = 255;
1990      }
1991      break;
1992#if SPLASH_CMYK
1993    case splashModeCMYK8:
1994      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
1995      for (i = 0; i < n; ++i) {
1996        pix = (Guchar)i;
1997        colorMap->getCMYK(&pix, &cmyk);
1998        imgData.lookup[4*i] = colToByte(cmyk.c);
1999        imgData.lookup[4*i+1] = colToByte(cmyk.m);
2000        imgData.lookup[4*i+2] = colToByte(cmyk.y);
2001        imgData.lookup[4*i+3] = colToByte(cmyk.k);
2002      }
2003      break;
2004#endif
2005      break;
2006    }
2007  }
2008
2009  if (colorMode == splashModeMono1) {
2010    srcMode = splashModeMono8;
2011  } else {
2012    srcMode = colorMode;
2013  }
2014  src = maskColors ? &alphaImageSrc : &imageSrc;
2015  splash->drawImage(src, &imgData, srcMode, maskColors ? gTrue : gFalse,
2016                    width, height, mat);
2017  if (inlineImg) {
2018    while (imgData.y < height) {
2019      imgData.imgStr->getLine();
2020      ++imgData.y;
2021    }
2022  }
2023
2024  gfree(imgData.lookup);
2025  delete imgData.imgStr;
2026  str->close();
2027}
2028
2029struct SplashOutMaskedImageData {
2030  ImageStream *imgStr;
2031  GfxImageColorMap *colorMap;
2032  SplashBitmap *mask;
2033  SplashColorPtr lookup;
2034  SplashColorMode colorMode;
2035  int width, height, y;
2036};
2037
2038GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
2039                                      Guchar *alphaLine) {
2040  SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2041  Guchar *p, *aq;
2042  SplashColor maskColor;
2043  SplashColorPtr q, col;
2044  GfxRGB rgb;
2045  GfxGray gray;
2046#if SPLASH_CMYK
2047  GfxCMYK cmyk;
2048#endif
2049  Guchar alpha;
2050  int nComps, x;
2051
2052  if (imgData->y == imgData->height) {
2053    return gFalse;
2054  }
2055
2056  nComps = imgData->colorMap->getNumPixelComps();
2057
2058  for (x = 0, p = imgData->imgStr->getLine(), q = colorLine, aq = alphaLine;
2059       x < imgData->width;
2060       ++x, p += nComps) {
2061    imgData->mask->getPixel(x, imgData->y, maskColor);
2062    alpha = maskColor[0] ? 0xff : 0x00;
2063    if (imgData->lookup) {
2064      switch (imgData->colorMode) {
2065      case splashModeMono1:
2066      case splashModeMono8:
2067        *q++ = imgData->lookup[*p];
2068        *aq++ = alpha;
2069        break;
2070      case splashModeRGB8:
2071      case splashModeBGR8:
2072        col = &imgData->lookup[3 * *p];
2073        *q++ = col[0];
2074        *q++ = col[1];
2075        *q++ = col[2];
2076        *aq++ = alpha;
2077        break;
2078      case splashModeXBGR8:
2079        col = &imgData->lookup[4 * *p];
2080        *q++ = col[0];
2081        *q++ = col[1];
2082        *q++ = col[2];
2083        *q++ = 255;
2084        *aq++ = alpha;
2085        break;
2086#if SPLASH_CMYK
2087      case splashModeCMYK8:
2088        col = &imgData->lookup[4 * *p];
2089        *q++ = col[0];
2090        *q++ = col[1];
2091        *q++ = col[2];
2092        *q++ = col[3];
2093        *aq++ = alpha;
2094        break;
2095#endif
2096      }
2097    } else {
2098      switch (imgData->colorMode) {
2099      case splashModeMono1:
2100      case splashModeMono8:
2101        imgData->colorMap->getGray(p, &gray);
2102        *q++ = colToByte(gray);
2103        *aq++ = alpha;
2104        break;
2105      case splashModeXBGR8:
2106      case splashModeRGB8:
2107      case splashModeBGR8:
2108        imgData->colorMap->getRGB(p, &rgb);
2109        *q++ = colToByte(rgb.r);
2110        *q++ = colToByte(rgb.g);
2111        *q++ = colToByte(rgb.b);
2112        if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
2113        *aq++ = alpha;
2114        break;
2115#if SPLASH_CMYK
2116      case splashModeCMYK8:
2117        imgData->colorMap->getCMYK(p, &cmyk);
2118        *q++ = colToByte(cmyk.c);
2119        *q++ = colToByte(cmyk.m);
2120        *q++ = colToByte(cmyk.y);
2121        *q++ = colToByte(cmyk.k);
2122        *aq++ = alpha;
2123        break;
2124#endif
2125      }
2126    }
2127  }
2128
2129  ++imgData->y;
2130  return gTrue;
2131}
2132
2133void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2134                                      Stream *str, int width, int height,
2135                                      GfxImageColorMap *colorMap,
2136                                      Stream *maskStr, int maskWidth,
2137                                      int maskHeight, GBool maskInvert) {
2138  GfxImageColorMap *maskColorMap;
2139  Object maskDecode, decodeLow, decodeHigh;
2140  double *ctm;
2141  SplashCoord mat[6];
2142  SplashOutMaskedImageData imgData;
2143  SplashOutImageMaskData imgMaskData;
2144  SplashColorMode srcMode;
2145  SplashBitmap *maskBitmap;
2146  Splash *maskSplash;
2147  SplashColor maskColor;
2148  GfxGray gray;
2149  GfxRGB rgb;
2150#if SPLASH_CMYK
2151  GfxCMYK cmyk;
2152#endif
2153  Guchar pix;
2154  int n, i;
2155
2156  // If the mask is higher resolution than the image, use
2157  // drawSoftMaskedImage() instead.
2158  if (maskWidth > width || maskHeight > height) {
2159    decodeLow.initInt(maskInvert ? 0 : 1);
2160    decodeHigh.initInt(maskInvert ? 1 : 0);
2161    maskDecode.initArray(xref);
2162    maskDecode.arrayAdd(&decodeLow);
2163    maskDecode.arrayAdd(&decodeHigh);
2164    maskColorMap = new GfxImageColorMap(1, &maskDecode,
2165                                        new GfxDeviceGrayColorSpace());
2166    maskDecode.free();
2167    drawSoftMaskedImage(state, ref, str, width, height, colorMap,
2168                        maskStr, maskWidth, maskHeight, maskColorMap);
2169    delete maskColorMap;
2170
2171  } else {
2172
2173    //----- scale the mask image to the same size as the source image
2174
2175    mat[0] = (SplashCoord)width;
2176    mat[1] = 0;
2177    mat[2] = 0;
2178    mat[3] = (SplashCoord)height;
2179    mat[4] = 0;
2180    mat[5] = 0;
2181    imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2182    imgMaskData.imgStr->reset();
2183    imgMaskData.invert = maskInvert ? 0 : 1;
2184    imgMaskData.width = maskWidth;
2185    imgMaskData.height = maskHeight;
2186    imgMaskData.y = 0;
2187    maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
2188    maskSplash = new Splash(maskBitmap, gFalse);
2189    maskColor[0] = 0;
2190    maskSplash->clear(maskColor);
2191    maskColor[0] = 0xff;
2192    maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2193    maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2194                              maskWidth, maskHeight, mat, gFalse);
2195    delete imgMaskData.imgStr;
2196    maskStr->close();
2197    delete maskSplash;
2198
2199    //----- draw the source image
2200
2201    ctm = state->getCTM();
2202    mat[0] = ctm[0];
2203    mat[1] = ctm[1];
2204    mat[2] = -ctm[2];
2205    mat[3] = -ctm[3];
2206    mat[4] = ctm[2] + ctm[4];
2207    mat[5] = ctm[3] + ctm[5];
2208
2209    imgData.imgStr = new ImageStream(str, width,
2210                                     colorMap->getNumPixelComps(),
2211                                     colorMap->getBits());
2212    imgData.imgStr->reset();
2213    imgData.colorMap = colorMap;
2214    imgData.mask = maskBitmap;
2215    imgData.colorMode = colorMode;
2216    imgData.width = width;
2217    imgData.height = height;
2218    imgData.y = 0;
2219
2220    // special case for one-channel (monochrome/gray/separation) images:
2221    // build a lookup table here
2222    imgData.lookup = NULL;
2223    if (colorMap->getNumPixelComps() == 1) {
2224      n = 1 << colorMap->getBits();
2225      switch (colorMode) {
2226      case splashModeMono1:
2227      case splashModeMono8:
2228        imgData.lookup = (SplashColorPtr)gmalloc(n);
2229        for (i = 0; i < n; ++i) {
2230          pix = (Guchar)i;
2231          colorMap->getGray(&pix, &gray);
2232          imgData.lookup[i] = colToByte(gray);
2233        }
2234        break;
2235      case splashModeRGB8:
2236      case splashModeBGR8:
2237        imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2238        for (i = 0; i < n; ++i) {
2239          pix = (Guchar)i;
2240          colorMap->getRGB(&pix, &rgb);
2241          imgData.lookup[3*i] = colToByte(rgb.r);
2242          imgData.lookup[3*i+1] = colToByte(rgb.g);
2243          imgData.lookup[3*i+2] = colToByte(rgb.b);
2244        }
2245        break;
2246      case splashModeXBGR8:
2247        imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2248        for (i = 0; i < n; ++i) {
2249          pix = (Guchar)i;
2250          colorMap->getRGB(&pix, &rgb);
2251          imgData.lookup[4*i] = colToByte(rgb.r);
2252          imgData.lookup[4*i+1] = colToByte(rgb.g);
2253          imgData.lookup[4*i+2] = colToByte(rgb.b);
2254          imgData.lookup[4*i+3] = 255;
2255        }
2256        break;
2257#if SPLASH_CMYK
2258      case splashModeCMYK8:
2259        imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2260        for (i = 0; i < n; ++i) {
2261          pix = (Guchar)i;
2262          colorMap->getCMYK(&pix, &cmyk);
2263          imgData.lookup[4*i] = colToByte(cmyk.c);
2264          imgData.lookup[4*i+1] = colToByte(cmyk.m);
2265          imgData.lookup[4*i+2] = colToByte(cmyk.y);
2266          imgData.lookup[4*i+3] = colToByte(cmyk.k);
2267        }
2268        break;
2269#endif
2270      }
2271    }
2272
2273    if (colorMode == splashModeMono1) {
2274      srcMode = splashModeMono8;
2275    } else {
2276      srcMode = colorMode;
2277    }
2278    splash->drawImage(&maskedImageSrc, &imgData, srcMode, gTrue,
2279                      width, height, mat);
2280
2281    delete maskBitmap;
2282    gfree(imgData.lookup);
2283    delete imgData.imgStr;
2284    str->close();
2285  }
2286}
2287
2288void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2289                                          Stream *str, int width, int height,
2290                                          GfxImageColorMap *colorMap,
2291                                          Stream *maskStr,
2292                                          int maskWidth, int maskHeight,
2293                                          GfxImageColorMap *maskColorMap) {
2294  double *ctm;
2295  SplashCoord mat[6];
2296  SplashOutImageData imgData;
2297  SplashOutImageData imgMaskData;
2298  SplashColorMode srcMode;
2299  SplashBitmap *maskBitmap;
2300  Splash *maskSplash;
2301  SplashColor maskColor;
2302  GfxGray gray;
2303  GfxRGB rgb;
2304#if SPLASH_CMYK
2305  GfxCMYK cmyk;
2306#endif
2307  Guchar pix;
2308  int n, i;
2309
2310  ctm = state->getCTM();
2311  mat[0] = ctm[0];
2312  mat[1] = ctm[1];
2313  mat[2] = -ctm[2];
2314  mat[3] = -ctm[3];
2315  mat[4] = ctm[2] + ctm[4];
2316  mat[5] = ctm[3] + ctm[5];
2317
2318  //----- set up the soft mask
2319
2320  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2321                                       maskColorMap->getNumPixelComps(),
2322                                       maskColorMap->getBits());
2323  imgMaskData.imgStr->reset();
2324  imgMaskData.colorMap = maskColorMap;
2325  imgMaskData.maskColors = NULL;
2326  imgMaskData.colorMode = splashModeMono8;
2327  imgMaskData.width = maskWidth;
2328  imgMaskData.height = maskHeight;
2329  imgMaskData.y = 0;
2330  n = 1 << maskColorMap->getBits();
2331  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2332  for (i = 0; i < n; ++i) {
2333    pix = (Guchar)i;
2334    maskColorMap->getGray(&pix, &gray);
2335    imgMaskData.lookup[i] = colToByte(gray);
2336  }
2337  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2338                                1, splashModeMono8, gFalse);
2339  maskSplash = new Splash(maskBitmap, vectorAntialias);
2340  maskColor[0] = 0;
2341  maskSplash->clear(maskColor);
2342  maskSplash->drawImage(&imageSrc, &imgMaskData, splashModeMono8, gFalse,
2343                        maskWidth, maskHeight, mat);
2344  delete imgMaskData.imgStr;
2345  maskStr->close();
2346  gfree(imgMaskData.lookup);
2347  delete maskSplash;
2348  splash->setSoftMask(maskBitmap);
2349
2350  //----- draw the source image
2351
2352  imgData.imgStr = new ImageStream(str, width,
2353                                   colorMap->getNumPixelComps(),
2354                                   colorMap->getBits());
2355  imgData.imgStr->reset();
2356  imgData.colorMap = colorMap;
2357  imgData.maskColors = NULL;
2358  imgData.colorMode = colorMode;
2359  imgData.width = width;
2360  imgData.height = height;
2361  imgData.y = 0;
2362
2363  // special case for one-channel (monochrome/gray/separation) images:
2364  // build a lookup table here
2365  imgData.lookup = NULL;
2366  if (colorMap->getNumPixelComps() == 1) {
2367    n = 1 << colorMap->getBits();
2368    switch (colorMode) {
2369    case splashModeMono1:
2370    case splashModeMono8:
2371      imgData.lookup = (SplashColorPtr)gmalloc(n);
2372      for (i = 0; i < n; ++i) {
2373        pix = (Guchar)i;
2374        colorMap->getGray(&pix, &gray);
2375        imgData.lookup[i] = colToByte(gray);
2376      }
2377      break;
2378    case splashModeRGB8:
2379    case splashModeBGR8:
2380      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2381      for (i = 0; i < n; ++i) {
2382        pix = (Guchar)i;
2383        colorMap->getRGB(&pix, &rgb);
2384        imgData.lookup[3*i] = colToByte(rgb.r);
2385        imgData.lookup[3*i+1] = colToByte(rgb.g);
2386        imgData.lookup[3*i+2] = colToByte(rgb.b);
2387      }
2388      break;
2389    case splashModeXBGR8:
2390      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2391      for (i = 0; i < n; ++i) {
2392        pix = (Guchar)i;
2393        colorMap->getRGB(&pix, &rgb);
2394        imgData.lookup[4*i] = colToByte(rgb.r);
2395        imgData.lookup[4*i+1] = colToByte(rgb.g);
2396        imgData.lookup[4*i+2] = colToByte(rgb.b);
2397        imgData.lookup[4*i+3] = 255;
2398      }
2399      break;
2400#if SPLASH_CMYK
2401    case splashModeCMYK8:
2402      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2403      for (i = 0; i < n; ++i) {
2404        pix = (Guchar)i;
2405        colorMap->getCMYK(&pix, &cmyk);
2406        imgData.lookup[4*i] = colToByte(cmyk.c);
2407        imgData.lookup[4*i+1] = colToByte(cmyk.m);
2408        imgData.lookup[4*i+2] = colToByte(cmyk.y);
2409        imgData.lookup[4*i+3] = colToByte(cmyk.k);
2410      }
2411      break;
2412#endif
2413    }
2414  }
2415
2416  if (colorMode == splashModeMono1) {
2417    srcMode = splashModeMono8;
2418  } else {
2419    srcMode = colorMode;
2420  }
2421  splash->drawImage(&imageSrc, &imgData, srcMode, gFalse, width, height, mat);
2422
2423  splash->setSoftMask(NULL);
2424  gfree(imgData.lookup);
2425  delete imgData.imgStr;
2426  str->close();
2427}
2428
2429void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
2430                                             GfxColorSpace *blendingColorSpace,
2431                                             GBool isolated, GBool /*knockout*/,
2432                                             GBool /*forSoftMask*/) {
2433  SplashTransparencyGroup *transpGroup;
2434  SplashColor color;
2435  double xMin, yMin, xMax, yMax, x, y;
2436  int tx, ty, w, h;
2437
2438  // transform the bbox
2439  state->transform(bbox[0], bbox[1], &x, &y);
2440  xMin = xMax = x;
2441  yMin = yMax = y;
2442  state->transform(bbox[0], bbox[3], &x, &y);
2443  if (x < xMin) {
2444    xMin = x;
2445  } else if (x > xMax) {
2446    xMax = x;
2447  }
2448  if (y < yMin) {
2449    yMin = y;
2450  } else if (y > yMax) {
2451    yMax = y;
2452  }
2453  state->transform(bbox[2], bbox[1], &x, &y);
2454  if (x < xMin) {
2455    xMin = x;
2456  } else if (x > xMax) {
2457    xMax = x;
2458  }
2459  if (y < yMin) {
2460    yMin = y;
2461  } else if (y > yMax) {
2462    yMax = y;
2463  }
2464  state->transform(bbox[2], bbox[3], &x, &y);
2465  if (x < xMin) {
2466    xMin = x;
2467  } else if (x > xMax) {
2468    xMax = x;
2469  }
2470  if (y < yMin) {
2471    yMin = y;
2472  } else if (y > yMax) {
2473    yMax = y;
2474  }
2475  tx = (int)floor(xMin);
2476  if (tx < 0) {
2477    tx = 0;
2478  } else if (tx > bitmap->getWidth()) {
2479    tx = bitmap->getWidth();
2480  }
2481  ty = (int)floor(yMin);
2482  if (ty < 0) {
2483    ty = 0;
2484  } else if (ty > bitmap->getHeight()) {
2485    ty = bitmap->getHeight();
2486  }
2487  w = (int)ceil(xMax) - tx + 1;
2488  if (tx + w > bitmap->getWidth()) {
2489    w = bitmap->getWidth() - tx;
2490  }
2491  if (w < 1) {
2492    w = 1;
2493  }
2494  h = (int)ceil(yMax) - ty + 1;
2495  if (ty + h > bitmap->getHeight()) {
2496    h = bitmap->getHeight() - ty;
2497  }
2498  if (h < 1) {
2499    h = 1;
2500  }
2501
2502  // push a new stack entry
2503  transpGroup = new SplashTransparencyGroup();
2504  transpGroup->tx = tx;
2505  transpGroup->ty = ty;
2506  transpGroup->blendingColorSpace = blendingColorSpace;
2507  transpGroup->isolated = isolated;
2508  transpGroup->next = transpGroupStack;
2509  transpGroupStack = transpGroup;
2510
2511  // save state
2512  transpGroup->origBitmap = bitmap;
2513  transpGroup->origSplash = splash;
2514
2515  //~ this ignores the blendingColorSpace arg
2516
2517  // create the temporary bitmap
2518  bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
2519                            bitmapTopDown); 
2520  splash = new Splash(bitmap, vectorAntialias,
2521                      transpGroup->origSplash->getScreen());
2522  if (isolated) {
2523    switch (colorMode) {
2524    case splashModeMono1:
2525    case splashModeMono8:
2526      color[0] = 0;
2527      break;
2528    case splashModeXBGR8:
2529      color[3] = 255;
2530    case splashModeRGB8:
2531    case splashModeBGR8:
2532      color[0] = color[1] = color[2] = 0;
2533      break;
2534#if SPLASH_CMYK
2535    case splashModeCMYK8:
2536      color[0] = color[1] = color[2] = color[3] = 0;
2537      break;
2538#endif
2539    default:
2540      // make gcc happy
2541      break;
2542    }
2543    splash->clear(color, 0);
2544  } else {
2545    splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
2546    splash->setInNonIsolatedGroup(transpGroup->origBitmap, tx, ty);
2547  }
2548  transpGroup->tBitmap = bitmap;
2549  state->shiftCTM(-tx, -ty);
2550  updateCTM(state, 0, 0, 0, 0, 0, 0);
2551}
2552
2553void SplashOutputDev::endTransparencyGroup(GfxState *state) {
2554  double *ctm;
2555
2556  // restore state
2557  delete splash;
2558  bitmap = transpGroupStack->origBitmap;
2559  splash = transpGroupStack->origSplash;
2560  ctm = state->getCTM();
2561  state->shiftCTM(transpGroupStack->tx, transpGroupStack->ty);
2562  updateCTM(state, 0, 0, 0, 0, 0, 0);
2563}
2564
2565void SplashOutputDev::paintTransparencyGroup(GfxState * /*state*/, double * /*bbox*/) {
2566  SplashBitmap *tBitmap;
2567  SplashTransparencyGroup *transpGroup;
2568  GBool isolated;
2569  int tx, ty;
2570
2571  tx = transpGroupStack->tx;
2572  ty = transpGroupStack->ty;
2573  tBitmap = transpGroupStack->tBitmap;
2574  isolated = transpGroupStack->isolated;
2575
2576  // paint the transparency group onto the parent bitmap
2577  // - the clip path was set in the parent's state)
2578  splash->composite(tBitmap, 0, 0, tx, ty,
2579                    tBitmap->getWidth(), tBitmap->getHeight(),
2580                    gFalse, !isolated);
2581
2582  // pop the stack
2583  transpGroup = transpGroupStack;
2584  transpGroupStack = transpGroup->next;
2585  delete transpGroup;
2586
2587  delete tBitmap;
2588}
2589
2590void SplashOutputDev::setSoftMask(GfxState * /*state*/, double * /*bbox*/,
2591                                  GBool alpha, Function *transferFunc,
2592                                  GfxColor *backdropColor) {
2593  SplashBitmap *softMask, *tBitmap;
2594  Splash *tSplash;
2595  SplashTransparencyGroup *transpGroup;
2596  SplashColor color;
2597  SplashColorPtr p;
2598  GfxGray gray;
2599  GfxRGB rgb;
2600#if SPLASH_CMYK
2601  GfxCMYK cmyk;
2602#endif
2603  double lum, lum2;
2604  int tx, ty, x, y;
2605
2606  tx = transpGroupStack->tx;
2607  ty = transpGroupStack->ty;
2608  tBitmap = transpGroupStack->tBitmap;
2609
2610  // composite with backdrop color
2611  if (!alpha && colorMode != splashModeMono1) {
2612    //~ need to correctly handle the case where no blending color
2613    //~ space is given
2614    if (transpGroupStack->blendingColorSpace) {
2615      tSplash = new Splash(tBitmap, vectorAntialias,
2616                           transpGroupStack->origSplash->getScreen());
2617      switch (colorMode) {
2618      case splashModeMono1:
2619        // transparency is not supported in mono1 mode
2620        break;
2621      case splashModeMono8:
2622        transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
2623        color[0] = colToByte(gray);
2624        tSplash->compositeBackground(color);
2625        break;
2626      case splashModeXBGR8:
2627        color[3] = 255;
2628      case splashModeRGB8:
2629      case splashModeBGR8:
2630        transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
2631        color[0] = colToByte(rgb.r);
2632        color[1] = colToByte(rgb.g);
2633        color[2] = colToByte(rgb.b);
2634        tSplash->compositeBackground(color);
2635        break;
2636#if SPLASH_CMYK
2637      case splashModeCMYK8:
2638        transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
2639        color[0] = colToByte(cmyk.c);
2640        color[1] = colToByte(cmyk.m);
2641        color[2] = colToByte(cmyk.y);
2642        color[3] = colToByte(cmyk.k);
2643        tSplash->compositeBackground(color);
2644        break;
2645#endif
2646      }
2647      delete tSplash;
2648    }
2649  }
2650
2651  softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2652                              1, splashModeMono8, gFalse);
2653  memset(softMask->getDataPtr(), 0,
2654         softMask->getRowSize() * softMask->getHeight());
2655  p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
2656  int xMax = tBitmap->getWidth();
2657  int yMax = tBitmap->getHeight();
2658  if (xMax + tx > bitmap->getWidth()) xMax = bitmap->getWidth() - tx;
2659  if (yMax + ty > bitmap->getHeight()) yMax = bitmap->getHeight() - ty;
2660  for (y = 0; y < yMax; ++y) {
2661    for (x = 0; x < xMax; ++x) {
2662      tBitmap->getPixel(x, y, color);
2663      if (alpha) {
2664        //~ unimplemented
2665      } else {
2666        // convert to luminosity
2667        switch (colorMode) {
2668        case splashModeMono1:
2669        case splashModeMono8:
2670          lum = color[0] / 255.0;
2671          break;
2672        case splashModeXBGR8:
2673        case splashModeRGB8:
2674        case splashModeBGR8:
2675          lum = (0.3 / 255.0) * color[0] +
2676                (0.59 / 255.0) * color[1] +
2677                (0.11 / 255.0) * color[2];
2678          break;
2679#if SPLASH_CMYK
2680        case splashModeCMYK8:
2681          lum = (1 - color[4] / 255.0)
2682                - (0.3 / 255.0) * color[0]
2683                - (0.59 / 255.0) * color[1]
2684                - (0.11 / 255.0) * color[2];
2685          if (lum < 0) {
2686            lum = 0;
2687          }
2688          break;
2689#endif
2690        }
2691        if (transferFunc) {
2692          transferFunc->transform(&lum, &lum2);
2693        } else {
2694          lum2 = lum;
2695        }
2696        p[x] = (int)(lum2 * 255.0 + 0.5);
2697      }
2698    }
2699    p += softMask->getRowSize();
2700  }
2701  splash->setSoftMask(softMask);
2702
2703  // pop the stack
2704  transpGroup = transpGroupStack;
2705  transpGroupStack = transpGroup->next;
2706  delete transpGroup;
2707
2708  delete tBitmap;
2709}
2710
2711void SplashOutputDev::clearSoftMask(GfxState * /*state*/) {
2712  splash->setSoftMask(NULL);
2713}
2714
2715void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
2716  splashColorCopy(paperColor, paperColorA);
2717}
2718
2719int SplashOutputDev::getBitmapWidth() {
2720  return bitmap->getWidth();
2721}
2722
2723int SplashOutputDev::getBitmapHeight() {
2724  return bitmap->getHeight();
2725}
2726
2727SplashBitmap *SplashOutputDev::takeBitmap() {
2728  SplashBitmap *ret;
2729
2730  ret = bitmap;
2731  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
2732                            colorMode != splashModeMono1, bitmapTopDown);
2733  return ret;
2734}
2735
2736void SplashOutputDev::getModRegion(int *xMin, int *yMin,
2737                                   int *xMax, int *yMax) {
2738  splash->getModRegion(xMin, yMin, xMax, yMax);
2739}
2740
2741void SplashOutputDev::clearModRegion() {
2742  splash->clearModRegion();
2743}
2744
2745void SplashOutputDev::setFillColor(int r, int g, int b) {
2746  GfxRGB rgb;
2747  GfxGray gray;
2748#if SPLASH_CMYK
2749  GfxCMYK cmyk;
2750#endif
2751
2752  rgb.r = byteToCol(r);
2753  rgb.g = byteToCol(g);
2754  rgb.b = byteToCol(b);
2755  gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5);
2756  if (gray > gfxColorComp1) {
2757    gray = gfxColorComp1;
2758  }
2759#if SPLASH_CMYK
2760  cmyk.c = gfxColorComp1 - rgb.r;
2761  cmyk.m = gfxColorComp1 - rgb.g;
2762  cmyk.y = gfxColorComp1 - rgb.b;
2763  cmyk.k = 0;
2764  splash->setFillPattern(getColor(gray, &rgb, &cmyk));
2765#else
2766  splash->setFillPattern(getColor(gray, &rgb));
2767#endif
2768}
2769
2770#if 1 //~tmp: turn off anti-aliasing temporarily
2771GBool SplashOutputDev::getVectorAntialias() {
2772  return splash->getVectorAntialias();
2773}
2774
2775void SplashOutputDev::setVectorAntialias(GBool vaa) {
2776  splash->setVectorAntialias(vaa);
2777}
2778#endif
Note: See TracBrowser for help on using the repository browser.