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

Last change on this file since 2 was 2, checked in by Eugene Romanenko, 16 years ago

First import

File size: 62.0 KB
RevLine 
[2]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    color[0] = color[1] = color[2] = 0;
630    break;
631  case splashModeAMono8:
632    color[0] = 0xff;
633    color[1] = 0;
634    break;
635  case splashModeARGB8:
636    color[0] = 255;
637    color[1] = color[2] = color[3] = 0;
638    break;
639  case splashModeBGRA8:
640    color[0] = color[1] = color[2] = 0;
641    color[3] = 255;
642    break;
643#if SPLASH_CMYK
644  case splashModeCMYK8:
645    color[0] = color[1] = color[2] = color[3] = 0;
646    break;
647  case splashModeACMYK8:
648    color[0] = 255;
649    color[1] = color[2] = color[3] = color[4] = 0;
650    break;
651#endif
652  }
653  splash->setStrokePattern(new SplashSolidColor(color));
654  splash->setFillPattern(new SplashSolidColor(color));
655  splash->setLineCap(splashLineCapButt);
656  splash->setLineJoin(splashLineJoinMiter);
657  splash->setLineDash(NULL, 0, 0);
658  splash->setMiterLimit(10);
659  splash->setFlatness(1);
660  splash->clear(paperColor);
661}
662
663void SplashOutputDev::endPage() {
664}
665
666void SplashOutputDev::drawLink(Link *link, Catalog *catalog) {
667  double x1, y1, x2, y2;
668  LinkBorderStyle *borderStyle;
669  double r, g, b;
670  GfxRGB rgb;
671  GfxGray gray;
672#if SPLASH_CMYK
673  GfxCMYK cmyk;
674#endif
675  double *dash;
676  int dashLength;
677  SplashCoord dashList[20];
678  SplashPath *path;
679  int x, y, i;
680
681  link->getRect(&x1, &y1, &x2, &y2);
682  borderStyle = link->getBorderStyle();
683  if (borderStyle->getWidth() > 0) {
684    borderStyle->getColor(&r, &g, &b);
685    rgb.r = dblToCol(r);
686    rgb.g = dblToCol(g);
687    rgb.b = dblToCol(b);
688    gray = dblToCol(0.299 * r + 0.587 * g + 0.114 * b);
689    if (gray > gfxColorComp1) {
690      gray = gfxColorComp1;
691    }
692#if SPLASH_CMYK
693    cmyk.c = gfxColorComp1 - rgb.r;
694    cmyk.m = gfxColorComp1 - rgb.g;
695    cmyk.y = gfxColorComp1 - rgb.b;
696    cmyk.k = 0;
697    splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
698#else
699    splash->setStrokePattern(getColor(gray, &rgb));
700#endif
701    splash->setLineWidth((SplashCoord)borderStyle->getWidth());
702    borderStyle->getDash(&dash, &dashLength);
703    if (borderStyle->getType() == linkBorderDashed && dashLength > 0) {
704      if (dashLength > 20) {
705        dashLength = 20;
706      }
707      for (i = 0; i < dashLength; ++i) {
708        dashList[i] = (SplashCoord)dash[i];
709      }
710      splash->setLineDash(dashList, dashLength, 0);
711    }
712    path = new SplashPath();
713    if (borderStyle->getType() == linkBorderUnderlined) {
714      cvtUserToDev(x1, y1, &x, &y);
715      path->moveTo((SplashCoord)x, (SplashCoord)y);
716      cvtUserToDev(x2, y1, &x, &y);
717      path->lineTo((SplashCoord)x, (SplashCoord)y);
718    } else {
719      cvtUserToDev(x1, y1, &x, &y);
720      path->moveTo((SplashCoord)x, (SplashCoord)y);
721      cvtUserToDev(x2, y1, &x, &y);
722      path->lineTo((SplashCoord)x, (SplashCoord)y);
723      cvtUserToDev(x2, y2, &x, &y);
724      path->lineTo((SplashCoord)x, (SplashCoord)y);
725      cvtUserToDev(x1, y2, &x, &y);
726      path->lineTo((SplashCoord)x, (SplashCoord)y);
727      path->close();
728    }
729    splash->stroke(path);
730    delete path;
731  }
732}
733
734void SplashOutputDev::saveState(GfxState *state) {
735  splash->saveState();
736}
737
738void SplashOutputDev::restoreState(GfxState *state) {
739  splash->restoreState();
740  needFontUpdate = gTrue;
741}
742
743void SplashOutputDev::updateAll(GfxState *state) {
744  updateLineDash(state);
745  updateLineJoin(state);
746  updateLineCap(state);
747  updateLineWidth(state);
748  updateFlatness(state);
749  updateMiterLimit(state);
750  updateFillColor(state);
751  updateStrokeColor(state);
752  needFontUpdate = gTrue;
753}
754
755void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
756                                double m21, double m22,
757                                double m31, double m32) {
758  updateLineDash(state);
759  updateLineJoin(state);
760  updateLineCap(state);
761  updateLineWidth(state);
762}
763
764void SplashOutputDev::updateLineDash(GfxState *state) {
765  double *dashPattern;
766  int dashLength;
767  double dashStart;
768  SplashCoord dash[20];
769  SplashCoord phase;
770  int i;
771
772  state->getLineDash(&dashPattern, &dashLength, &dashStart);
773  if (dashLength > 20) {
774    dashLength = 20;
775  }
776  for (i = 0; i < dashLength; ++i) {
777    dash[i] =  (SplashCoord)state->transformWidth(dashPattern[i]);
778    if (dash[i] < 1) {
779      dash[i] = 1;
780    }
781  }
782  phase = (SplashCoord)state->transformWidth(dashStart);
783  splash->setLineDash(dash, dashLength, phase);
784}
785
786void SplashOutputDev::updateFlatness(GfxState *state) {
787  splash->setFlatness(state->getFlatness());
788}
789
790void SplashOutputDev::updateLineJoin(GfxState *state) {
791  splash->setLineJoin(state->getLineJoin());
792}
793
794void SplashOutputDev::updateLineCap(GfxState *state) {
795  splash->setLineCap(state->getLineCap());
796}
797
798void SplashOutputDev::updateMiterLimit(GfxState *state) {
799  splash->setMiterLimit(state->getMiterLimit());
800}
801
802void SplashOutputDev::updateLineWidth(GfxState *state) {
803  splash->setLineWidth(state->getTransformedLineWidth());
804}
805
806void SplashOutputDev::updateFillColor(GfxState *state) {
807  GfxGray gray;
808  GfxRGB rgb;
809#if SPLASH_CMYK
810  GfxCMYK cmyk;
811#endif
812
813  state->getFillGray(&gray);
814  state->getFillRGB(&rgb);
815#if SPLASH_CMYK
816  state->getFillCMYK(&cmyk);
817  splash->setFillPattern(getColor(gray, &rgb, &cmyk));
818#else
819  splash->setFillPattern(getColor(gray, &rgb));
820#endif
821}
822
823void SplashOutputDev::updateStrokeColor(GfxState *state) {
824  GfxGray gray;
825  GfxRGB rgb;
826#if SPLASH_CMYK
827  GfxCMYK cmyk;
828#endif
829
830  state->getStrokeGray(&gray);
831  state->getStrokeRGB(&rgb);
832#if SPLASH_CMYK
833  state->getStrokeCMYK(&cmyk);
834  splash->setStrokePattern(getColor(gray, &rgb, &cmyk));
835#else
836  splash->setStrokePattern(getColor(gray, &rgb));
837#endif
838}
839
840#if SPLASH_CMYK
841SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb,
842                                         GfxCMYK *cmyk) {
843#else
844SplashPattern *SplashOutputDev::getColor(GfxGray gray, GfxRGB *rgb) {
845#endif
846  SplashPattern *pattern;
847  SplashColor color0, color1;
848  GfxColorComp r, g, b;
849
850  if (reverseVideo) {
851    gray = gfxColorComp1 - gray;
852    r = gfxColorComp1 - rgb->r;
853    g = gfxColorComp1 - rgb->g;
854    b = gfxColorComp1 - rgb->b;
855  } else {
856    r = rgb->r;
857    g = rgb->g;
858    b = rgb->b;
859  }
860
861  pattern = NULL; // make gcc happy
862  switch (colorMode) {
863  case splashModeMono1:
864    color0[0] = 0;
865    color1[0] = 1;
866    pattern = new SplashHalftone(color0, color1,
867                                 splash->getScreen()->copy(),
868                                 (SplashCoord)colToDbl(gray));
869    break;
870  case splashModeMono8:
871    color1[0] = colToByte(gray);
872    pattern = new SplashSolidColor(color1);
873    break;
874  case splashModeAMono8:
875    color1[0] = 255;
876    color1[1] = colToByte(gray);
877    pattern = new SplashSolidColor(color1);
878    break;
879  case splashModeRGB8:
880    color1[0] = colToByte(r);
881    color1[1] = colToByte(g);
882    color1[2] = colToByte(b);
883    pattern = new SplashSolidColor(color1);
884    break;
885  case splashModeBGR8:
886    color1[2] = colToByte(r);
887    color1[1] = colToByte(g);
888    color1[0] = colToByte(b);
889    pattern = new SplashSolidColor(color1);
890    break;
891  case splashModeARGB8:
892    color1[0] = 255;
893    color1[1] = colToByte(r);
894    color1[2] = colToByte(g);
895    color1[3] = colToByte(b);
896    pattern = new SplashSolidColor(color1);
897    break;
898  case splashModeBGRA8:
899    color1[3] = 255;
900    color1[2] = colToByte(r);
901    color1[1] = colToByte(g);
902    color1[0] = colToByte(b);
903    pattern = new SplashSolidColor(color1);
904    break;
905#if SPLASH_CMYK
906  case splashModeCMYK8:
907    color1[0] = colToByte(cmyk->c);
908    color1[1] = colToByte(cmyk->m);
909    color1[2] = colToByte(cmyk->y);
910    color1[3] = colToByte(cmyk->k);
911    pattern = new SplashSolidColor(color1);
912    break;
913  case splashModeACMYK8:
914    color1[0] = 255;
915    color1[1] = colToByte(cmyk->c);
916    color1[2] = colToByte(cmyk->m);
917    color1[3] = colToByte(cmyk->y);
918    color1[4] = colToByte(cmyk->k);
919    pattern = new SplashSolidColor(color1);
920    break;
921#endif
922  }
923
924  return pattern;
925}
926
927void SplashOutputDev::updateBlendMode(GfxState *state) {
928  splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
929}
930
931void SplashOutputDev::updateFillOpacity(GfxState *state) {
932  splash->setFillAlpha((SplashCoord)state->getFillOpacity());
933}
934
935void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
936  splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
937}
938
939void SplashOutputDev::updateFont(GfxState *state) {
940  GfxFont *gfxFont;
941  GfxFontType fontType;
942  SplashOutFontFileID *id;
943  SplashFontFile *fontFile;
944  SplashFontSrc *fontsrc;
945  FoFiTrueType *ff;
946  Ref embRef;
947  Object refObj, strObj;
948  GooString *fileName, *substName;
949  char *tmpBuf;
950  int tmpBufLen;
951  Gushort *codeToGID;
952  DisplayFontParam *dfp;
953  CharCodeToUnicode *ctu;
954  double m11, m12, m21, m22, w1, w2;
955  SplashCoord mat[4];
956  char *name;
957  Unicode uBuf[8];
958  int c, substIdx, n, code, cmap;
959  int faceIndex = 0;
960
961  needFontUpdate = gFalse;
962  font = NULL;
963  fileName = NULL;
964  tmpBuf = NULL;
965  substIdx = -1;
966  dfp = NULL;
967
968  if (!(gfxFont = state->getFont())) {
969    goto err1;
970  }
971  fontType = gfxFont->getType();
972  if (fontType == fontType3) {
973    goto err1;
974  }
975
976  // check the font file cache
977  id = new SplashOutFontFileID(gfxFont->getID());
978  if ((fontFile = fontEngine->getFontFile(id))) {
979    delete id;
980
981  } else {
982
983    // if there is an embedded font, write it to disk
984    if (gfxFont->getEmbeddedFontID(&embRef)) {
985      tmpBuf = gfxFont->readEmbFontFile(xref, &tmpBufLen);
986      if (! tmpBuf)
987        goto err2;
988    // if there is an external font file, use it
989    } else if (!(fileName = gfxFont->getExtFontFile())) {
990
991      // look for a display font mapping or a substitute font
992      dfp = NULL;
993      if (gfxFont->getName()) {
994        dfp = globalParams->getDisplayFont(gfxFont);
995      }
996      if (!dfp) {
997        error(-1, "Couldn't find a font for '%s'",
998              gfxFont->getName() ? gfxFont->getName()->getCString()
999                                 : "(unnamed)");
1000        goto err2;
1001      }
1002      switch (dfp->kind) {
1003      case displayFontT1:
1004        fileName = dfp->t1.fileName;
1005        fontType = gfxFont->isCIDFont() ? fontCIDType0 : fontType1;
1006        break;
1007      case displayFontTT:
1008        fileName = dfp->tt.fileName;
1009        fontType = gfxFont->isCIDFont() ? fontCIDType2 : fontTrueType;
1010        faceIndex = dfp->tt.faceIndex;
1011        break;
1012      }
1013    }
1014
1015    fontsrc = new SplashFontSrc;
1016    if (fileName)
1017      fontsrc->setFile(fileName, gFalse);
1018    else
1019      fontsrc->setBuf(tmpBuf, tmpBufLen, gFalse);
1020
1021    // load the font file
1022    switch (fontType) {
1023    case fontType1:
1024      fontFile = fontEngine->loadType1Font(id, fontsrc, 
1025                                           ((Gfx8BitFont *)gfxFont)->getEncoding());
1026      if (! fontFile) {
1027        error(-1, "Couldn't create a font for '%s'",
1028              gfxFont->getName() ? gfxFont->getName()->getCString()
1029                                 : "(unnamed)");
1030        goto err2;
1031      }
1032      break;
1033    case fontType1C:
1034      fontFile = fontEngine->loadType1CFont(id, fontsrc,
1035                                            ((Gfx8BitFont *)gfxFont)->getEncoding());
1036      if (! fontFile) {
1037        error(-1, "Couldn't create a font for '%s'",
1038              gfxFont->getName() ? gfxFont->getName()->getCString()
1039                                 : "(unnamed)");
1040        goto err2;
1041      }
1042      break;
1043    case fontTrueType:
1044        if (fileName)
1045         ff = FoFiTrueType::load(fileName->getCString());
1046        else
1047         ff = new FoFiTrueType(tmpBuf, tmpBufLen, gFalse);
1048        if (ff) {
1049      codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
1050        n = 256;
1051      delete ff;
1052      } else {
1053        codeToGID = NULL;
1054        n = 0;
1055      }
1056      if (!(fontFile = fontEngine->loadTrueTypeFont(
1057                           id,
1058                           fontsrc,
1059                           codeToGID, n))) {
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 fontCIDType0:
1067    case fontCIDType0C:
1068      fontFile = fontEngine->loadCIDFont(id, fontsrc);
1069      if (! fontFile) {
1070        error(-1, "Couldn't create a font for '%s'",
1071              gfxFont->getName() ? gfxFont->getName()->getCString()
1072                                 : "(unnamed)");
1073        goto err2;
1074      }
1075      break;
1076    case fontCIDType2:
1077      codeToGID = NULL;
1078      n = 0;
1079      if (dfp) {
1080        // create a CID-to-GID mapping, via Unicode
1081        if ((ctu = ((GfxCIDFont *)gfxFont)->getToUnicode())) {
1082                if (fileName)
1083                 ff = FoFiTrueType::load(fileName->getCString());
1084                else
1085                 ff = new FoFiTrueType(tmpBuf, tmpBufLen, gFalse);
1086                if (ff) {
1087            // look for a Unicode cmap
1088            for (cmap = 0; cmap < ff->getNumCmaps(); ++cmap) {
1089              if ((ff->getCmapPlatform(cmap) == 3 &&
1090                   ff->getCmapEncoding(cmap) == 1) ||
1091                  ff->getCmapPlatform(cmap) == 0) {
1092                break;
1093              }
1094            }
1095            if (cmap < ff->getNumCmaps()) {
1096              // map CID -> Unicode -> GID
1097              n = ctu->getLength();
1098              codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1099              for (code = 0; code < n; ++code) {
1100                if (ctu->mapToUnicode(code, uBuf, 8) > 0) {
1101                  codeToGID[code] = ff->mapCodeToGID(cmap, uBuf[0]);
1102                } else {
1103                  codeToGID[code] = 0;
1104                }
1105              }
1106            }
1107            delete ff;
1108          }
1109          ctu->decRefCnt();
1110        } else {
1111          error(-1, "Couldn't find a mapping to Unicode for font '%s'",
1112                gfxFont->getName() ? gfxFont->getName()->getCString()
1113                                   : "(unnamed)");
1114        }
1115      } else {
1116        if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
1117      n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
1118        if (n) {
1119                codeToGID = (Gushort *)gmallocn(n, sizeof(Gushort));
1120                memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
1121                        n * sizeof(Gushort));
1122        } else {
1123                if (fileName)
1124                 ff = FoFiTrueType::load(fileName->getCString());
1125                else
1126                 ff = new FoFiTrueType(tmpBuf, tmpBufLen, gFalse);
1127                if (! ff)
1128                 goto err2;
1129                codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
1130                delete ff;
1131        }
1132        }
1133      }
1134      if (!(fontFile = fontEngine->loadTrueTypeFont(
1135                           id,
1136                           fontsrc,
1137                           codeToGID,
1138                           n,
1139                           faceIndex))) {
1140        error(-1, "Couldn't create a font for '%s'",
1141              gfxFont->getName() ? gfxFont->getName()->getCString()
1142                                 : "(unnamed)");
1143        goto err2;
1144      }
1145      break;
1146    default:
1147      // this shouldn't happen
1148      goto err2;
1149    }
1150  }
1151
1152  // get the font matrix
1153  state->getFontTransMat(&m11, &m12, &m21, &m22);
1154  m11 *= state->getHorizScaling();
1155  m12 *= state->getHorizScaling();
1156
1157  // create the scaled font
1158  mat[0] = m11;  mat[1] = -m12;
1159  mat[2] = m21;  mat[3] = -m22;
1160  if (fabs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.01) {
1161    // avoid a singular (or close-to-singular) matrix
1162    mat[0] = 0.01;  mat[1] = 0;
1163    mat[2] = 0;     mat[3] = 0.01;
1164  }
1165  font = fontEngine->getFont(fontFile, mat);
1166
1167  return;
1168
1169 err2:
1170  delete id;
1171 err1:
1172  return;
1173}
1174
1175void SplashOutputDev::stroke(GfxState *state) {
1176  SplashPath *path;
1177
1178  path = convertPath(state, state->getPath());
1179  splash->stroke(path);
1180  delete path;
1181}
1182
1183void SplashOutputDev::fill(GfxState *state) {
1184  SplashPath *path;
1185
1186  path = convertPath(state, state->getPath());
1187  splash->fill(path, gFalse);
1188  delete path;
1189}
1190
1191void SplashOutputDev::eoFill(GfxState *state) {
1192  SplashPath *path;
1193
1194  path = convertPath(state, state->getPath());
1195  splash->fill(path, gTrue);
1196  delete path;
1197}
1198
1199void SplashOutputDev::clip(GfxState *state) {
1200  SplashPath *path;
1201
1202  path = convertPath(state, state->getPath());
1203  splash->clipToPath(path, gFalse);
1204  delete path;
1205}
1206
1207void SplashOutputDev::eoClip(GfxState *state) {
1208  SplashPath *path;
1209
1210  path = convertPath(state, state->getPath());
1211  splash->clipToPath(path, gTrue);
1212  delete path;
1213}
1214
1215SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path) {
1216  SplashPath *sPath;
1217  GfxSubpath *subpath;
1218  double x1, y1, x2, y2, x3, y3;
1219  int i, j;
1220
1221  sPath = new SplashPath();
1222  for (i = 0; i < path->getNumSubpaths(); ++i) {
1223    subpath = path->getSubpath(i);
1224    if (subpath->getNumPoints() > 0) {
1225      state->transform(subpath->getX(0), subpath->getY(0), &x1, &y1);
1226      sPath->moveTo((SplashCoord)x1, (SplashCoord)y1);
1227      j = 1;
1228      while (j < subpath->getNumPoints()) {
1229        if (subpath->getCurve(j)) {
1230          state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1231          state->transform(subpath->getX(j+1), subpath->getY(j+1), &x2, &y2);
1232          state->transform(subpath->getX(j+2), subpath->getY(j+2), &x3, &y3);
1233          sPath->curveTo((SplashCoord)x1, (SplashCoord)y1,
1234                         (SplashCoord)x2, (SplashCoord)y2,
1235                         (SplashCoord)x3, (SplashCoord)y3);
1236          j += 3;
1237        } else {
1238          state->transform(subpath->getX(j), subpath->getY(j), &x1, &y1);
1239          sPath->lineTo((SplashCoord)x1, (SplashCoord)y1);
1240          ++j;
1241        }
1242      }
1243      if (subpath->isClosed()) {
1244        sPath->close();
1245      }
1246    }
1247  }
1248  return sPath;
1249}
1250
1251void SplashOutputDev::drawChar(GfxState *state, double x, double y,
1252                               double dx, double dy,
1253                               double originX, double originY,
1254                               CharCode code, int nBytes,
1255                               Unicode *u, int uLen) {
1256  double x1, y1;
1257  SplashPath *path;
1258  int render;
1259
1260  if (needFontUpdate) {
1261    updateFont(state);
1262  }
1263  if (!font) {
1264    return;
1265  }
1266
1267  // check for invisible text -- this is used by Acrobat Capture
1268  render = state->getRender();
1269  if (render == 3) {
1270    return;
1271  }
1272
1273  x -= originX;
1274  y -= originY;
1275  state->transform(x, y, &x1, &y1);
1276
1277  // fill
1278  if (!(render & 1)) {
1279    splash->fillChar((SplashCoord)x1, (SplashCoord)y1, code, font);
1280  }
1281
1282  // stroke
1283  if ((render & 3) == 1 || (render & 3) == 2) {
1284    if ((path = font->getGlyphPath(code))) {
1285      path->offset((SplashCoord)x1, (SplashCoord)y1);
1286      splash->stroke(path);
1287      delete path;
1288    }
1289  }
1290
1291  // clip
1292  if (render & 4) {
1293    path = font->getGlyphPath(code);
1294    path->offset((SplashCoord)x1, (SplashCoord)y1);
1295    if (textClipPath) {
1296      textClipPath->append(path);
1297      delete path;
1298    } else {
1299      textClipPath = path;
1300    }
1301  }
1302}
1303
1304GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
1305                                      double dx, double dy,
1306                                      CharCode code, Unicode *u, int uLen) {
1307  GfxFont *gfxFont;
1308  Ref *fontID;
1309  double *ctm, *bbox;
1310  T3FontCache *t3Font;
1311  T3GlyphStack *t3gs;
1312  double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
1313  int i, j;
1314
1315  if (!(gfxFont = state->getFont())) {
1316    return gFalse;
1317  }
1318  fontID = gfxFont->getID();
1319  ctm = state->getCTM();
1320  state->transform(0, 0, &xt, &yt);
1321
1322  // is it the first (MRU) font in the cache?
1323  if (!(nT3Fonts > 0 &&
1324        t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
1325
1326    // is the font elsewhere in the cache?
1327    for (i = 1; i < nT3Fonts; ++i) {
1328      if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
1329        t3Font = t3FontCache[i];
1330        for (j = i; j > 0; --j) {
1331          t3FontCache[j] = t3FontCache[j - 1];
1332        }
1333        t3FontCache[0] = t3Font;
1334        break;
1335      }
1336    }
1337    if (i >= nT3Fonts) {
1338
1339      // create new entry in the font cache
1340      if (nT3Fonts == splashOutT3FontCacheSize) {
1341        delete t3FontCache[nT3Fonts - 1];
1342        --nT3Fonts;
1343      }
1344      for (j = nT3Fonts; j > 0; --j) {
1345        t3FontCache[j] = t3FontCache[j - 1];
1346      }
1347      ++nT3Fonts;
1348      bbox = gfxFont->getFontBBox();
1349      if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
1350        // broken bounding box -- just take a guess
1351        xMin = xt - 5;
1352        xMax = xMin + 30;
1353        yMax = yt + 15;
1354        yMin = yMax - 45;
1355      } else {
1356        state->transform(bbox[0], bbox[1], &x1, &y1);
1357        xMin = xMax = x1;
1358        yMin = yMax = y1;
1359        state->transform(bbox[0], bbox[3], &x1, &y1);
1360        if (x1 < xMin) {
1361          xMin = x1;
1362        } else if (x1 > xMax) {
1363          xMax = x1;
1364        }
1365        if (y1 < yMin) {
1366          yMin = y1;
1367        } else if (y1 > yMax) {
1368          yMax = y1;
1369        }
1370        state->transform(bbox[2], bbox[1], &x1, &y1);
1371        if (x1 < xMin) {
1372          xMin = x1;
1373        } else if (x1 > xMax) {
1374          xMax = x1;
1375        }
1376        if (y1 < yMin) {
1377          yMin = y1;
1378        } else if (y1 > yMax) {
1379          yMax = y1;
1380        }
1381        state->transform(bbox[2], bbox[3], &x1, &y1);
1382        if (x1 < xMin) {
1383          xMin = x1;
1384        } else if (x1 > xMax) {
1385          xMax = x1;
1386        }
1387        if (y1 < yMin) {
1388          yMin = y1;
1389        } else if (y1 > yMax) {
1390          yMax = y1;
1391        }
1392      }
1393      t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
1394                                       (int)floor(xMin - xt),
1395                                       (int)floor(yMin - yt),
1396                                       (int)ceil(xMax) - (int)floor(xMin) + 3,
1397                                       (int)ceil(yMax) - (int)floor(yMin) + 3,
1398                                       colorMode != splashModeMono1);
1399    }
1400  }
1401  t3Font = t3FontCache[0];
1402
1403  // is the glyph in the cache?
1404  i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1405  for (j = 0; j < t3Font->cacheAssoc; ++j) {
1406    if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
1407        t3Font->cacheTags[i+j].code == code) {
1408      drawType3Glyph(t3Font, &t3Font->cacheTags[i+j],
1409                     t3Font->cacheData + (i+j) * t3Font->glyphSize,
1410                     xt, yt);
1411      return gTrue;
1412    }
1413  }
1414
1415  // push a new Type 3 glyph record
1416  t3gs = new T3GlyphStack();
1417  t3gs->next = t3GlyphStack;
1418  t3GlyphStack = t3gs;
1419  t3GlyphStack->code = code;
1420  t3GlyphStack->x = xt;
1421  t3GlyphStack->y = yt;
1422  t3GlyphStack->cache = t3Font;
1423  t3GlyphStack->cacheTag = NULL;
1424  t3GlyphStack->cacheData = NULL;
1425
1426  return gFalse;
1427}
1428
1429void SplashOutputDev::endType3Char(GfxState *state) {
1430  T3GlyphStack *t3gs;
1431  double *ctm;
1432
1433  if (t3GlyphStack->cacheTag) {
1434    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
1435           t3GlyphStack->cache->glyphSize);
1436    delete bitmap;
1437    delete splash;
1438    bitmap = t3GlyphStack->origBitmap;
1439    splash = t3GlyphStack->origSplash;
1440    ctm = state->getCTM();
1441    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1442                  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
1443    drawType3Glyph(t3GlyphStack->cache,
1444                   t3GlyphStack->cacheTag, t3GlyphStack->cacheData,
1445                   t3GlyphStack->x, t3GlyphStack->y);
1446  }
1447  t3gs = t3GlyphStack;
1448  t3GlyphStack = t3gs->next;
1449  delete t3gs;
1450}
1451
1452void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
1453}
1454
1455void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
1456                              double llx, double lly, double urx, double ury) {
1457  double *ctm;
1458  T3FontCache *t3Font;
1459  SplashColor color;
1460  double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
1461  int i, j;
1462
1463  t3Font = t3GlyphStack->cache;
1464
1465  // check for a valid bbox
1466  state->transform(0, 0, &xt, &yt);
1467  state->transform(llx, lly, &x1, &y1);
1468  xMin = xMax = x1;
1469  yMin = yMax = y1;
1470  state->transform(llx, ury, &x1, &y1);
1471  if (x1 < xMin) {
1472    xMin = x1;
1473  } else if (x1 > xMax) {
1474    xMax = x1;
1475  }
1476  if (y1 < yMin) {
1477    yMin = y1;
1478  } else if (y1 > yMax) {
1479    yMax = y1;
1480  }
1481  state->transform(urx, lly, &x1, &y1);
1482  if (x1 < xMin) {
1483    xMin = x1;
1484  } else if (x1 > xMax) {
1485    xMax = x1;
1486  }
1487  if (y1 < yMin) {
1488    yMin = y1;
1489  } else if (y1 > yMax) {
1490    yMax = y1;
1491  }
1492  state->transform(urx, ury, &x1, &y1);
1493  if (x1 < xMin) {
1494    xMin = x1;
1495  } else if (x1 > xMax) {
1496    xMax = x1;
1497  }
1498  if (y1 < yMin) {
1499    yMin = y1;
1500  } else if (y1 > yMax) {
1501    yMax = y1;
1502  }
1503  if (xMin - xt < t3Font->glyphX ||
1504      yMin - yt < t3Font->glyphY ||
1505      xMax - xt > t3Font->glyphX + t3Font->glyphW ||
1506      yMax - yt > t3Font->glyphY + t3Font->glyphH) {
1507    error(-1, "Bad bounding box in Type 3 glyph");
1508    return;
1509  }
1510
1511  // allocate a cache entry
1512  i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
1513  for (j = 0; j < t3Font->cacheAssoc; ++j) {
1514    if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
1515      t3Font->cacheTags[i+j].mru = 0x8000;
1516      t3Font->cacheTags[i+j].code = t3GlyphStack->code;
1517      t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
1518      t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
1519    } else {
1520      ++t3Font->cacheTags[i+j].mru;
1521    }
1522  }
1523
1524  // save state
1525  t3GlyphStack->origBitmap = bitmap;
1526  t3GlyphStack->origSplash = splash;
1527  ctm = state->getCTM();
1528  t3GlyphStack->origCTM4 = ctm[4];
1529  t3GlyphStack->origCTM5 = ctm[5];
1530
1531  // create the temporary bitmap
1532  if (colorMode == splashModeMono1) {
1533    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1534                              splashModeMono1);
1535    splash = new Splash(bitmap);
1536    color[0] = 0;
1537    splash->clear(color);
1538    color[0] = 1;
1539  } else {
1540    bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
1541                              splashModeMono8);
1542    splash = new Splash(bitmap);
1543    color[0] = 0x00;
1544    splash->clear(color);
1545    color[0] = 0xff;
1546  }
1547  splash->setFillPattern(new SplashSolidColor(color));
1548  splash->setStrokePattern(new SplashSolidColor(color));
1549  //~ this should copy other state from t3GlyphStack->origSplash?
1550  state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
1551                -t3Font->glyphX, -t3Font->glyphY);
1552}
1553
1554void SplashOutputDev::drawType3Glyph(T3FontCache *t3Font,
1555                                     T3FontCacheTag *tag, Guchar *data,
1556                                     double x, double y) {
1557  SplashGlyphBitmap glyph;
1558
1559  glyph.x = -t3Font->glyphX;
1560  glyph.y = -t3Font->glyphY;
1561  glyph.w = t3Font->glyphW;
1562  glyph.h = t3Font->glyphH;
1563  glyph.aa = colorMode != splashModeMono1;
1564  glyph.data = data;
1565  glyph.freeData = gFalse;
1566  splash->fillGlyph((SplashCoord)x, (SplashCoord)y, &glyph);
1567}
1568
1569void SplashOutputDev::endTextObject(GfxState *state) {
1570  if (textClipPath) {
1571    splash->clipToPath(textClipPath, gFalse);
1572    delete textClipPath;
1573    textClipPath = NULL;
1574  }
1575}
1576
1577struct SplashOutImageMaskData {
1578  ImageStream *imgStr;
1579  GBool invert;
1580  int width, height, y;
1581};
1582
1583GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
1584  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
1585  Guchar *p;
1586  SplashColorPtr q;
1587  int x;
1588
1589  if (imgMaskData->y == imgMaskData->height) {
1590    return gFalse;
1591  }
1592  for (x = 0, p = imgMaskData->imgStr->getLine(), q = line;
1593       x < imgMaskData->width;
1594       ++x) {
1595    *q++ = *p++ ^ imgMaskData->invert;
1596  }
1597  ++imgMaskData->y;
1598  return gTrue;
1599}
1600
1601void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
1602                                    int width, int height, GBool invert,
1603                                    GBool inlineImg) {
1604  double *ctm;
1605  SplashCoord mat[6];
1606  SplashOutImageMaskData imgMaskData;
1607
1608  ctm = state->getCTM();
1609  mat[0] = ctm[0];
1610  mat[1] = ctm[1];
1611  mat[2] = -ctm[2];
1612  mat[3] = -ctm[3];
1613  mat[4] = ctm[2] + ctm[4];
1614  mat[5] = ctm[3] + ctm[5];
1615
1616  imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
1617  imgMaskData.imgStr->reset();
1618  imgMaskData.invert = invert ? 0 : 1;
1619  imgMaskData.width = width;
1620  imgMaskData.height = height;
1621  imgMaskData.y = 0;
1622
1623  splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat);
1624  if (inlineImg) {
1625    while (imgMaskData.y < height) {
1626      imgMaskData.imgStr->getLine();
1627      ++imgMaskData.y;
1628    }
1629  }
1630
1631  delete imgMaskData.imgStr;
1632  str->close();
1633}
1634
1635struct SplashOutImageData {
1636  ImageStream *imgStr;
1637  GfxImageColorMap *colorMap;
1638  SplashColorPtr lookup;
1639  int *maskColors;
1640  SplashColorMode colorMode;
1641  int width, height, y;
1642};
1643
1644GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr line) {
1645  SplashOutImageData *imgData = (SplashOutImageData *)data;
1646  Guchar *p;
1647  SplashColorPtr q, col;
1648  GfxRGB rgb;
1649  GfxGray gray;
1650#if SPLASH_CMYK
1651  GfxCMYK cmyk;
1652#endif
1653  int nComps, x;
1654
1655  if (imgData->y == imgData->height) {
1656    return gFalse;
1657  }
1658
1659  nComps = imgData->colorMap->getNumPixelComps();
1660
1661  if (imgData->lookup) {
1662    switch (imgData->colorMode) {
1663  case splashModeMono1:
1664  case splashModeMono8:
1665      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1666           x < imgData->width;
1667           ++x, ++p) {
1668        *q++ = imgData->lookup[*p];
1669      }
1670    break;
1671  case splashModeRGB8:
1672    case splashModeBGR8:
1673      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1674           x < imgData->width;
1675           ++x, ++p) {
1676        col = &imgData->lookup[3 * *p];
1677        *q++ = col[0];
1678        *q++ = col[1];
1679        *q++ = col[2];
1680      }
1681    break;
1682#if SPLASH_CMYK
1683    case splashModeCMYK8:
1684      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1685           x < imgData->width;
1686           ++x, ++p) {
1687        col = &imgData->lookup[4 * *p];
1688        *q++ = col[0];
1689        *q++ = col[1];
1690        *q++ = col[2];
1691        *q++ = col[3];
1692      }
1693      break;
1694#endif
1695    case splashModeAMono8:
1696    case splashModeARGB8:
1697    case splashModeBGRA8:
1698#if SPLASH_CMYK
1699    case splashModeACMYK8:
1700#endif
1701      //~ unimplemented
1702    break;
1703  }
1704  } else {
1705    switch (imgData->colorMode) {
1706    case splashModeMono1:
1707    case splashModeMono8:
1708      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1709           x < imgData->width;
1710           ++x, p += nComps) {
1711        imgData->colorMap->getGray(p, &gray);
1712        *q++ = colToByte(gray);
1713      }
1714        break;
1715    case splashModeRGB8:
1716      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1717           x < imgData->width;
1718           ++x, p += nComps) {
1719        imgData->colorMap->getRGB(p, &rgb);
1720        *q++ = colToByte(rgb.r);
1721        *q++ = colToByte(rgb.g);
1722        *q++ = colToByte(rgb.b);
1723      }
1724      break;
1725    case splashModeBGR8:
1726      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1727           x < imgData->width;
1728           ++x, p += nComps) {
1729        imgData->colorMap->getRGB(p, &rgb);
1730        *q++ = colToByte(rgb.b);
1731        *q++ = colToByte(rgb.g);
1732        *q++ = colToByte(rgb.r);
1733      }
1734      break;
1735#if SPLASH_CMYK
1736    case splashModeCMYK8:
1737      for (x = 0, p = imgData->imgStr->getLine(), q = line;
1738           x < imgData->width;
1739           ++x, p += nComps) {
1740        imgData->colorMap->getCMYK(p, &cmyk);
1741        *q++ = colToByte(cmyk.c);
1742        *q++ = colToByte(cmyk.m);
1743        *q++ = colToByte(cmyk.y);
1744        *q++ = colToByte(cmyk.k);
1745      }
1746      break;
1747#endif
1748    case splashModeAMono8:
1749    case splashModeARGB8:
1750    case splashModeBGRA8:
1751#if SPLASH_CMYK
1752    case splashModeACMYK8:
1753#endif
1754      //~ unimplemented
1755      break;
1756    }
1757  }
1758
1759  ++imgData->y;
1760  return gTrue;
1761}
1762
1763GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr line) {
1764  SplashOutImageData *imgData = (SplashOutImageData *)data;
1765  Guchar *p;
1766  SplashColorPtr q, col;
1767  GfxRGB rgb;
1768  GfxGray gray;
1769#if SPLASH_CMYK
1770  GfxCMYK cmyk;
1771#endif
1772  Guchar alpha;
1773  int nComps, x, i;
1774
1775  if (imgData->y == imgData->height) {
1776    return gFalse;
1777  }
1778
1779  nComps = imgData->colorMap->getNumPixelComps();
1780
1781  for (x = 0, p = imgData->imgStr->getLine(), q = line;
1782       x < imgData->width;
1783       ++x, p += nComps) {
1784    alpha = 0;
1785    for (i = 0; i < nComps; ++i) {
1786      if (p[i] < imgData->maskColors[2*i] ||
1787          p[i] > imgData->maskColors[2*i+1]) {
1788        alpha = 0xff;
1789        break;
1790      }
1791    }
1792    if (imgData->lookup) {
1793      switch (imgData->colorMode) {
1794      case splashModeMono1:
1795      case splashModeMono8:
1796        *q++ = alpha;
1797        *q++ = imgData->lookup[*p];
1798        break;
1799      case splashModeRGB8:
1800        *q++ = alpha;
1801        col = &imgData->lookup[3 * *p];
1802        *q++ = col[0];
1803        *q++ = col[1];
1804        *q++ = col[2];
1805        break;
1806      case splashModeBGR8:
1807        col = &imgData->lookup[3 * *p];
1808        *q++ = col[0];
1809        *q++ = col[1];
1810        *q++ = col[2];
1811        *q++ = alpha;
1812        break;
1813#if SPLASH_CMYK
1814      case splashModeCMYK8:
1815        *q++ = alpha;
1816        col = &imgData->lookup[4 * *p];
1817        *q++ = col[0];
1818        *q++ = col[1];
1819        *q++ = col[2];
1820        *q++ = col[3];
1821        break;
1822#endif
1823      case splashModeAMono8:
1824      case splashModeARGB8:
1825      case splashModeBGRA8:
1826#if SPLASH_CMYK
1827      case splashModeACMYK8:
1828#endif
1829        //~ unimplemented
1830        break;
1831      }
1832    } else {
1833      switch (imgData->colorMode) {
1834      case splashModeMono1:
1835      case splashModeMono8:
1836        imgData->colorMap->getGray(p, &gray);
1837        *q++ = alpha;
1838        *q++ = colToByte(gray);
1839        break;
1840      case splashModeRGB8:
1841        imgData->colorMap->getRGB(p, &rgb);
1842        *q++ = alpha;
1843        *q++ = colToByte(rgb.r);
1844        *q++ = colToByte(rgb.g);
1845        *q++ = colToByte(rgb.b);
1846        break;
1847      case splashModeBGR8:
1848        imgData->colorMap->getRGB(p, &rgb);
1849        *q++ = colToByte(rgb.b);
1850        *q++ = colToByte(rgb.g);
1851        *q++ = colToByte(rgb.r);
1852        *q++ = alpha;
1853        break;
1854#if SPLASH_CMYK
1855      case splashModeCMYK8:
1856        imgData->colorMap->getCMYK(p, &cmyk);
1857        *q++ = alpha;
1858        *q++ = colToByte(cmyk.c);
1859        *q++ = colToByte(cmyk.m);
1860        *q++ = colToByte(cmyk.y);
1861        *q++ = colToByte(cmyk.k);
1862        break;
1863#endif
1864      case splashModeAMono8:
1865      case splashModeARGB8:
1866      case splashModeBGRA8:
1867#if SPLASH_CMYK
1868      case splashModeACMYK8:
1869#endif
1870        //~ unimplemented
1871        break;
1872      }
1873    }
1874  }
1875
1876  ++imgData->y;
1877  return gTrue;
1878}
1879
1880void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
1881                                int width, int height,
1882                                GfxImageColorMap *colorMap,
1883                                int *maskColors, GBool inlineImg) {
1884  double *ctm;
1885  SplashCoord mat[6];
1886  SplashOutImageData imgData;
1887  SplashColorMode srcMode;
1888  SplashImageSource src;
1889  GfxGray gray;
1890  GfxRGB rgb;
1891#if SPLASH_CMYK
1892  GfxCMYK cmyk;
1893#endif
1894  Guchar pix;
1895  int n, i;
1896
1897  ctm = state->getCTM();
1898  mat[0] = ctm[0];
1899  mat[1] = ctm[1];
1900  mat[2] = -ctm[2];
1901  mat[3] = -ctm[3];
1902  mat[4] = ctm[2] + ctm[4];
1903  mat[5] = ctm[3] + ctm[5];
1904
1905  imgData.imgStr = new ImageStream(str, width,
1906                                   colorMap->getNumPixelComps(),
1907                                   colorMap->getBits());
1908  imgData.imgStr->reset();
1909  imgData.colorMap = colorMap;
1910  imgData.maskColors = maskColors;
1911  imgData.colorMode = colorMode;
1912  imgData.width = width;
1913  imgData.height = height;
1914  imgData.y = 0;
1915
1916  // special case for one-channel (monochrome/gray/separation) images:
1917  // build a lookup table here
1918  imgData.lookup = NULL;
1919  if (colorMap->getNumPixelComps() == 1) {
1920    n = 1 << colorMap->getBits();
1921    switch (colorMode) {
1922    case splashModeMono1:
1923    case splashModeMono8:
1924      imgData.lookup = (SplashColorPtr)gmalloc(n);
1925      for (i = 0; i < n; ++i) {
1926        pix = (Guchar)i;
1927        colorMap->getGray(&pix, &gray);
1928        imgData.lookup[i] = colToByte(gray);
1929      }
1930      break;
1931    case splashModeRGB8:
1932      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
1933      for (i = 0; i < n; ++i) {
1934        pix = (Guchar)i;
1935        colorMap->getRGB(&pix, &rgb);
1936        imgData.lookup[3*i] = colToByte(rgb.r);
1937        imgData.lookup[3*i+1] = colToByte(rgb.g);
1938        imgData.lookup[3*i+2] = colToByte(rgb.b);
1939      }
1940      break;
1941    case splashModeBGR8:
1942      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
1943      for (i = 0; i < n; ++i) {
1944        pix = (Guchar)i;
1945        colorMap->getRGB(&pix, &rgb);
1946        imgData.lookup[3*i] = colToByte(rgb.b);
1947        imgData.lookup[3*i+1] = colToByte(rgb.g);
1948        imgData.lookup[3*i+2] = colToByte(rgb.r);
1949      }
1950      break;
1951#if SPLASH_CMYK
1952    case splashModeCMYK8:
1953      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
1954      for (i = 0; i < n; ++i) {
1955        pix = (Guchar)i;
1956        colorMap->getCMYK(&pix, &cmyk);
1957        imgData.lookup[4*i] = colToByte(cmyk.c);
1958        imgData.lookup[4*i+1] = colToByte(cmyk.m);
1959        imgData.lookup[4*i+2] = colToByte(cmyk.y);
1960        imgData.lookup[4*i+3] = colToByte(cmyk.k);
1961      }
1962      break;
1963#endif
1964    default:
1965      //~ unimplemented
1966      break;
1967    }
1968  }
1969
1970  switch (colorMode) {
1971  case splashModeMono1:
1972  case splashModeMono8:
1973    srcMode = maskColors ? splashModeAMono8 : splashModeMono8;
1974    break;
1975  case splashModeRGB8:
1976    srcMode = maskColors ? splashModeARGB8 : splashModeRGB8;
1977    break;
1978  case splashModeBGR8:
1979    srcMode = maskColors ? splashModeBGRA8 : splashModeBGR8;
1980    break;
1981#if SPLASH_CMYK
1982  case splashModeCMYK8:
1983    srcMode = maskColors ? splashModeACMYK8 : splashModeCMYK8;
1984    break;
1985#endif
1986  default:
1987    //~ unimplemented
1988    srcMode = splashModeRGB8;
1989    break;
1990  } 
1991  src = maskColors ? &alphaImageSrc : &imageSrc;
1992  splash->drawImage(src, &imgData, srcMode, width, height, mat);
1993  if (inlineImg) {
1994    while (imgData.y < height) {
1995      imgData.imgStr->getLine();
1996      ++imgData.y;
1997    }
1998  }
1999
2000  gfree(imgData.lookup);
2001  delete imgData.imgStr;
2002  str->close();
2003}
2004
2005struct SplashOutMaskedImageData {
2006  ImageStream *imgStr;
2007  GfxImageColorMap *colorMap;
2008  SplashBitmap *mask;
2009  SplashColorPtr lookup;
2010  SplashColorMode colorMode;
2011  int width, height, y;
2012};
2013
2014GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr line) {
2015  SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
2016  Guchar *p;
2017  SplashColor maskColor;
2018  SplashColorPtr q, col;
2019  GfxRGB rgb;
2020  GfxGray gray;
2021#if SPLASH_CMYK
2022  GfxCMYK cmyk;
2023#endif
2024  Guchar alpha;
2025  int nComps, x;
2026
2027  if (imgData->y == imgData->height) {
2028    return gFalse;
2029  }
2030
2031  nComps = imgData->colorMap->getNumPixelComps();
2032
2033  for (x = 0, p = imgData->imgStr->getLine(), q = line;
2034       x < imgData->width;
2035       ++x, p += nComps) {
2036    imgData->mask->getPixel(x, imgData->y, maskColor);
2037    alpha = maskColor[0] ? 0xff : 0x00;
2038    if (imgData->lookup) {
2039      switch (imgData->colorMode) {
2040      case splashModeMono1:
2041      case splashModeMono8:
2042        *q++ = alpha;
2043        *q++ = imgData->lookup[*p];
2044        break;
2045      case splashModeRGB8:
2046        *q++ = alpha;
2047        col = &imgData->lookup[3 * *p];
2048        *q++ = col[0];
2049        *q++ = col[1];
2050        *q++ = col[2];
2051        break;
2052      case splashModeBGR8:
2053        col = &imgData->lookup[3 * *p];
2054        *q++ = col[0];
2055        *q++ = col[1];
2056        *q++ = col[2];
2057        *q++ = alpha;
2058        break;
2059#if SPLASH_CMYK
2060      case splashModeCMYK8:
2061        *q++ = alpha;
2062        col = &imgData->lookup[4 * *p];
2063        *q++ = col[0];
2064        *q++ = col[1];
2065        *q++ = col[2];
2066        *q++ = col[3];
2067        break;
2068#endif
2069      case splashModeAMono8:
2070      case splashModeARGB8:
2071      case splashModeBGRA8:
2072#if SPLASH_CMYK
2073      case splashModeACMYK8:
2074#endif
2075        //~ unimplemented
2076        break;
2077      }
2078    } else {
2079      switch (imgData->colorMode) {
2080      case splashModeMono1:
2081      case splashModeMono8:
2082        imgData->colorMap->getGray(p, &gray);
2083        *q++ = alpha;
2084        *q++ = colToByte(gray);
2085        break;
2086      case splashModeRGB8:
2087        imgData->colorMap->getRGB(p, &rgb);
2088        *q++ = alpha;
2089        *q++ = colToByte(rgb.r);
2090        *q++ = colToByte(rgb.g);
2091        *q++ = colToByte(rgb.b);
2092        break;
2093      case splashModeBGR8:
2094        imgData->colorMap->getRGB(p, &rgb);
2095        *q++ = colToByte(rgb.b);
2096        *q++ = colToByte(rgb.g);
2097        *q++ = colToByte(rgb.r);
2098        *q++ = alpha;
2099        break;
2100#if SPLASH_CMYK
2101      case splashModeCMYK8:
2102        imgData->colorMap->getCMYK(p, &cmyk);
2103        *q++ = alpha;
2104        *q++ = colToByte(cmyk.c);
2105        *q++ = colToByte(cmyk.m);
2106        *q++ = colToByte(cmyk.y);
2107        *q++ = colToByte(cmyk.k);
2108        break;
2109#endif
2110      case splashModeAMono8:
2111      case splashModeARGB8:
2112      case splashModeBGRA8:
2113#if SPLASH_CMYK
2114      case splashModeACMYK8:
2115#endif
2116        //~ unimplemented
2117        break;
2118      }
2119    }
2120  }
2121
2122  ++imgData->y;
2123  return gTrue;
2124}
2125
2126void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
2127                                      Stream *str, int width, int height,
2128                                      GfxImageColorMap *colorMap,
2129                                      Stream *maskStr, int maskWidth,
2130                                      int maskHeight, GBool maskInvert) {
2131  double *ctm;
2132  SplashCoord mat[6];
2133  SplashOutMaskedImageData imgData;
2134  SplashOutImageMaskData imgMaskData;
2135  SplashColorMode srcMode;
2136  SplashBitmap *maskBitmap;
2137  Splash *maskSplash;
2138  SplashColor maskColor;
2139  GfxGray gray;
2140  GfxRGB rgb;
2141#if SPLASH_CMYK
2142  GfxCMYK cmyk;
2143#endif
2144  Guchar pix;
2145  int n, i;
2146
2147  //----- scale the mask image to the same size as the source image
2148
2149  mat[0] = (SplashCoord)width;
2150  mat[1] = 0;
2151  mat[2] = 0;
2152  mat[3] = (SplashCoord)height;
2153  mat[4] = 0;
2154  mat[5] = 0;
2155  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
2156  imgMaskData.imgStr->reset();
2157  imgMaskData.invert = maskInvert ? 0 : 1;
2158  imgMaskData.width = maskWidth;
2159  imgMaskData.height = maskHeight;
2160  imgMaskData.y = 0;
2161  maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1);
2162  maskSplash = new Splash(maskBitmap);
2163  maskColor[0] = 0;
2164  maskSplash->clear(maskColor);
2165  maskColor[0] = 1;
2166  maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2167  maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
2168                            maskWidth, maskHeight, mat);
2169  delete imgMaskData.imgStr;
2170  maskStr->close();
2171  delete maskSplash;
2172
2173  //----- draw the source image
2174
2175  ctm = state->getCTM();
2176  mat[0] = ctm[0];
2177  mat[1] = ctm[1];
2178  mat[2] = -ctm[2];
2179  mat[3] = -ctm[3];
2180  mat[4] = ctm[2] + ctm[4];
2181  mat[5] = ctm[3] + ctm[5];
2182
2183  imgData.imgStr = new ImageStream(str, width,
2184                                   colorMap->getNumPixelComps(),
2185                                   colorMap->getBits());
2186  imgData.imgStr->reset();
2187  imgData.colorMap = colorMap;
2188  imgData.mask = maskBitmap;
2189  imgData.colorMode = colorMode;
2190  imgData.width = width;
2191  imgData.height = height;
2192  imgData.y = 0;
2193
2194  // special case for one-channel (monochrome/gray/separation) images:
2195  // build a lookup table here
2196  imgData.lookup = NULL;
2197  if (colorMap->getNumPixelComps() == 1) {
2198    n = 1 << colorMap->getBits();
2199    switch (colorMode) {
2200    case splashModeMono1:
2201    case splashModeMono8:
2202      imgData.lookup = (SplashColorPtr)gmalloc(n);
2203      for (i = 0; i < n; ++i) {
2204        pix = (Guchar)i;
2205        colorMap->getGray(&pix, &gray);
2206        imgData.lookup[i] = colToByte(gray);
2207      }
2208      break;
2209    case splashModeRGB8:
2210      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2211      for (i = 0; i < n; ++i) {
2212        pix = (Guchar)i;
2213        colorMap->getRGB(&pix, &rgb);
2214        imgData.lookup[3*i] = colToByte(rgb.r);
2215        imgData.lookup[3*i+1] = colToByte(rgb.g);
2216        imgData.lookup[3*i+2] = colToByte(rgb.b);
2217      }
2218      break;
2219    case splashModeBGR8:
2220      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2221      for (i = 0; i < n; ++i) {
2222        pix = (Guchar)i;
2223        colorMap->getRGB(&pix, &rgb);
2224        imgData.lookup[3*i] = colToByte(rgb.b);
2225        imgData.lookup[3*i+1] = colToByte(rgb.g);
2226        imgData.lookup[3*i+2] = colToByte(rgb.r);
2227      }
2228      break;
2229#if SPLASH_CMYK
2230    case splashModeCMYK8:
2231      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2232      for (i = 0; i < n; ++i) {
2233        pix = (Guchar)i;
2234        colorMap->getCMYK(&pix, &cmyk);
2235        imgData.lookup[4*i] = colToByte(cmyk.c);
2236        imgData.lookup[4*i+1] = colToByte(cmyk.m);
2237        imgData.lookup[4*i+2] = colToByte(cmyk.y);
2238        imgData.lookup[4*i+3] = colToByte(cmyk.k);
2239      }
2240      break;
2241#endif
2242    default:
2243      //~ unimplemented
2244      break;
2245    }
2246  }
2247
2248  switch (colorMode) {
2249  case splashModeMono1:
2250  case splashModeMono8:
2251    srcMode = splashModeAMono8;
2252    break;
2253  case splashModeRGB8:
2254    srcMode = splashModeARGB8;
2255    break;
2256  case splashModeBGR8:
2257    srcMode = splashModeBGRA8;
2258    break;
2259#if SPLASH_CMYK
2260  case splashModeCMYK8:
2261    srcMode = splashModeACMYK8;
2262    break;
2263#endif
2264  default:
2265    //~ unimplemented
2266    srcMode = splashModeARGB8;
2267    break;
2268  } 
2269  splash->drawImage(&maskedImageSrc, &imgData, srcMode, width, height, mat);
2270
2271  delete maskBitmap;
2272  gfree(imgData.lookup);
2273  delete imgData.imgStr;
2274  str->close();
2275}
2276
2277void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
2278                                          Stream *str, int width, int height,
2279                                          GfxImageColorMap *colorMap,
2280                                          Stream *maskStr,
2281                                          int maskWidth, int maskHeight,
2282                                          GfxImageColorMap *maskColorMap) {
2283  double *ctm;
2284  SplashCoord mat[6];
2285  SplashOutImageData imgData;
2286  SplashOutImageData imgMaskData;
2287  SplashColorMode srcMode;
2288  SplashBitmap *maskBitmap;
2289  Splash *maskSplash;
2290  SplashColor maskColor;
2291  GfxGray gray;
2292  GfxRGB rgb;
2293#if SPLASH_CMYK
2294  GfxCMYK cmyk;
2295#endif
2296  Guchar pix;
2297  int n, i;
2298
2299  ctm = state->getCTM();
2300  mat[0] = ctm[0];
2301  mat[1] = ctm[1];
2302  mat[2] = -ctm[2];
2303  mat[3] = -ctm[3];
2304  mat[4] = ctm[2] + ctm[4];
2305  mat[5] = ctm[3] + ctm[5];
2306
2307  //----- set up the soft mask
2308
2309  imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
2310                                       maskColorMap->getNumPixelComps(),
2311                                       maskColorMap->getBits());
2312  imgMaskData.imgStr->reset();
2313  imgMaskData.colorMap = maskColorMap;
2314  imgMaskData.maskColors = NULL;
2315  imgMaskData.colorMode = splashModeMono8;
2316  imgMaskData.width = maskWidth;
2317  imgMaskData.height = maskHeight;
2318  imgMaskData.y = 0;
2319  n = 1 << maskColorMap->getBits();
2320  imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
2321  for (i = 0; i < n; ++i) {
2322    pix = (Guchar)i;
2323    maskColorMap->getGray(&pix, &gray);
2324    imgMaskData.lookup[i] = colToByte(gray);
2325  }
2326  maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
2327                                1, splashModeMono8);
2328  maskSplash = new Splash(maskBitmap);
2329  maskColor[0] = 0;
2330  maskSplash->clear(maskColor);
2331  maskSplash->drawImage(&imageSrc, &imgMaskData,
2332                        splashModeMono8, maskWidth, maskHeight, mat);
2333  delete imgMaskData.imgStr;
2334  maskStr->close();
2335  gfree(imgMaskData.lookup);
2336  delete maskSplash;
2337  splash->setSoftMask(maskBitmap);
2338
2339  //----- draw the source image
2340
2341  imgData.imgStr = new ImageStream(str, width,
2342                                   colorMap->getNumPixelComps(),
2343                                   colorMap->getBits());
2344  imgData.imgStr->reset();
2345  imgData.colorMap = colorMap;
2346  imgData.maskColors = NULL;
2347  imgData.colorMode = colorMode;
2348  imgData.width = width;
2349  imgData.height = height;
2350  imgData.y = 0;
2351
2352  // special case for one-channel (monochrome/gray/separation) images:
2353  // build a lookup table here
2354  imgData.lookup = NULL;
2355  if (colorMap->getNumPixelComps() == 1) {
2356    n = 1 << colorMap->getBits();
2357    switch (colorMode) {
2358    case splashModeMono1:
2359    case splashModeMono8:
2360      imgData.lookup = (SplashColorPtr)gmalloc(n);
2361      for (i = 0; i < n; ++i) {
2362        pix = (Guchar)i;
2363        colorMap->getGray(&pix, &gray);
2364        imgData.lookup[i] = colToByte(gray);
2365      }
2366      break;
2367    case splashModeRGB8:
2368      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2369      for (i = 0; i < n; ++i) {
2370        pix = (Guchar)i;
2371        colorMap->getRGB(&pix, &rgb);
2372        imgData.lookup[3*i] = colToByte(rgb.r);
2373        imgData.lookup[3*i+1] = colToByte(rgb.g);
2374        imgData.lookup[3*i+2] = colToByte(rgb.b);
2375      }
2376      break;
2377    case splashModeBGR8:
2378      imgData.lookup = (SplashColorPtr)gmalloc(3 * n);
2379      for (i = 0; i < n; ++i) {
2380        pix = (Guchar)i;
2381        colorMap->getRGB(&pix, &rgb);
2382        imgData.lookup[3*i] = colToByte(rgb.b);
2383        imgData.lookup[3*i+1] = colToByte(rgb.g);
2384        imgData.lookup[3*i+2] = colToByte(rgb.r);
2385      }
2386      break;
2387#if SPLASH_CMYK
2388    case splashModeCMYK8:
2389      imgData.lookup = (SplashColorPtr)gmalloc(4 * n);
2390      for (i = 0; i < n; ++i) {
2391        pix = (Guchar)i;
2392        colorMap->getCMYK(&pix, &cmyk);
2393        imgData.lookup[4*i] = colToByte(cmyk.c);
2394        imgData.lookup[4*i+1] = colToByte(cmyk.m);
2395        imgData.lookup[4*i+2] = colToByte(cmyk.y);
2396        imgData.lookup[4*i+3] = colToByte(cmyk.k);
2397      }
2398      break;
2399#endif
2400    default:
2401      //~ unimplemented
2402      break;
2403    }
2404  }
2405
2406  switch (colorMode) {
2407  case splashModeMono1:
2408  case splashModeMono8:
2409    srcMode = splashModeMono8;
2410    break;
2411  case splashModeRGB8:
2412    srcMode = splashModeRGB8;
2413    break;
2414  case splashModeBGR8:
2415    srcMode = splashModeBGR8;
2416    break;
2417#if SPLASH_CMYK
2418  case splashModeCMYK8:
2419    srcMode = splashModeCMYK8;
2420    break;
2421#endif
2422  default:
2423    //~ unimplemented
2424    srcMode = splashModeRGB8;
2425    break;
2426  } 
2427  splash->drawImage(&imageSrc, &imgData, srcMode, width, height, mat);
2428
2429  splash->setSoftMask(NULL);
2430  gfree(imgData.lookup);
2431  delete imgData.imgStr;
2432  str->close();
2433}
2434
2435void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
2436  splashColorCopy(paperColor, paperColorA);
2437}
2438
2439int SplashOutputDev::getBitmapWidth() {
2440  return bitmap->getWidth();
2441}
2442
2443int SplashOutputDev::getBitmapHeight() {
2444  return bitmap->getHeight();
2445}
2446
2447SplashBitmap *SplashOutputDev::takeBitmap() {
2448  SplashBitmap *ret;
2449
2450  ret = bitmap;
2451  bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode, bitmapTopDown);
2452  return ret;
2453}
2454
2455void SplashOutputDev::getModRegion(int *xMin, int *yMin,
2456                                   int *xMax, int *yMax) {
2457  splash->getModRegion(xMin, yMin, xMax, yMax);
2458}
2459
2460void SplashOutputDev::clearModRegion() {
2461  splash->clearModRegion();
2462}
2463
2464void SplashOutputDev::setFillColor(int r, int g, int b) {
2465  GfxRGB rgb;
2466  GfxGray gray;
2467#if SPLASH_CMYK
2468  GfxCMYK cmyk;
2469#endif
2470
2471  rgb.r = byteToCol(r);
2472  rgb.g = byteToCol(g);
2473  rgb.b = byteToCol(b);
2474  gray = (GfxColorComp)(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.g + 0.5);
2475  if (gray > gfxColorComp1) {
2476    gray = gfxColorComp1;
2477  }
2478#if SPLASH_CMYK
2479  cmyk.c = gfxColorComp1 - rgb.r;
2480  cmyk.m = gfxColorComp1 - rgb.g;
2481  cmyk.y = gfxColorComp1 - rgb.b;
2482  cmyk.k = 0;
2483  splash->setFillPattern(getColor(gray, &rgb, &cmyk));
2484#else
2485  splash->setFillPattern(getColor(gray, &rgb));
2486#endif
2487}
2488
Note: See TracBrowser for help on using the repository browser.