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

Last change on this file since 44 was 44, checked in by Eugene Romanenko, 15 years ago

poppler updated to version 0.5.3, related changes

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