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