a patch for ttc fonts with bad index
[luatex.git] / source / libs / poppler / poppler-0.36.0 / poppler / SplashOutputDev.cc
blobd9d5699a051c44fce5fdb2da450e9cc4387a10da
1 //========================================================================
2 //
3 // SplashOutputDev.cc
4 //
5 // Copyright 2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2005 Takashi Iwai <tiwai@suse.de>
17 // Copyright (C) 2006 Stefan Schweizer <genstef@gentoo.org>
18 // Copyright (C) 2006-2015 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-2015 Thomas Freitag <Thomas.Freitag@alfa.de>
24 // Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
25 // Copyright (C) 2009, 2014, 2015 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 // Copyright (C) 2011 Andreas Hartmetz <ahartmetz@gmail.com>
31 // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
32 // Copyright (C) 2011, 2012 Adrian Johnson <ajohnson@redneon.com>
33 // Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
34 // Copyright (C) 2013 Li Junling <lijunling@sina.com>
35 // Copyright (C) 2014 Ed Porras <ed@moto-research.com>
36 // Copyright (C) 2014 Richard PALO <richard@netbsd.org>
37 // Copyright (C) 2015 Tamas Szekeres <szekerest@gmail.com>
39 // To see a description of the changes please see the Changelog file that
40 // came with your tarball or type make ChangeLog if you are building from git
42 //========================================================================
44 #include <config.h>
46 #ifdef USE_GCC_PRAGMAS
47 #pragma implementation
48 #endif
50 #include <string.h>
51 #include <math.h>
52 #include "goo/gfile.h"
53 #include "GlobalParams.h"
54 #include "Error.h"
55 #include "Object.h"
56 #include "Gfx.h"
57 #include "GfxFont.h"
58 #include "Page.h"
59 #include "PDFDoc.h"
60 #include "Link.h"
61 #include "FontEncodingTables.h"
62 #include "fofi/FoFiTrueType.h"
63 #include "splash/SplashBitmap.h"
64 #include "splash/SplashGlyphBitmap.h"
65 #include "splash/SplashPattern.h"
66 #include "splash/SplashScreen.h"
67 #include "splash/SplashPath.h"
68 #include "splash/SplashState.h"
69 #include "splash/SplashErrorCodes.h"
70 #include "splash/SplashFontEngine.h"
71 #include "splash/SplashFont.h"
72 #include "splash/SplashFontFile.h"
73 #include "splash/SplashFontFileID.h"
74 #include "splash/Splash.h"
75 #include "SplashOutputDev.h"
76 #include <algorithm>
78 #ifdef VMS
79 #if (__VMS_VER < 70000000)
80 extern "C" int unlink(char *filename);
81 #endif
82 #endif
84 #ifdef _MSC_VER
85 #include <float.h>
86 #define isfinite(x) _finite(x)
87 #endif
89 #ifdef __sun
90 #include <ieeefp.h>
91 #ifndef isfinite
92 #define isfinite(x) finite(x)
93 #endif
94 #endif
96 static inline void convertGfxColor(SplashColorPtr dest,
97 SplashColorMode colorMode,
98 GfxColorSpace *colorSpace,
99 GfxColor *src) {
100 SplashColor color;
101 GfxGray gray;
102 GfxRGB rgb;
103 #if SPLASH_CMYK
104 GfxCMYK cmyk;
105 GfxColor deviceN;
106 #endif
108 // make gcc happy
109 color[0] = color[1] = color[2] = 0;
110 #if SPLASH_CMYK
111 color[3] = 0;
112 #endif
113 switch (colorMode) {
114 case splashModeMono1:
115 case splashModeMono8:
116 colorSpace->getGray(src, &gray);
117 color[0] = colToByte(gray);
118 break;
119 case splashModeXBGR8:
120 color[3] = 255;
121 case splashModeBGR8:
122 case splashModeRGB8:
123 colorSpace->getRGB(src, &rgb);
124 color[0] = colToByte(rgb.r);
125 color[1] = colToByte(rgb.g);
126 color[2] = colToByte(rgb.b);
127 break;
128 #if SPLASH_CMYK
129 case splashModeCMYK8:
130 colorSpace->getCMYK(src, &cmyk);
131 color[0] = colToByte(cmyk.c);
132 color[1] = colToByte(cmyk.m);
133 color[2] = colToByte(cmyk.y);
134 color[3] = colToByte(cmyk.k);
135 break;
136 case splashModeDeviceN8:
137 colorSpace->getDeviceN(src, &deviceN);
138 for (int i = 0; i < SPOT_NCOMPS + 4; i++)
139 color[i] = colToByte(deviceN.c[i]);
140 break;
141 #endif
143 splashColorCopy(dest, color);
146 // Copy a color according to the color mode.
147 // Use convertGfxShortColor() below when the destination is a bitmap
148 // to avoid overwriting cells.
149 // Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570.
150 // Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes,
151 // to ensure that everything is initialized.
153 static inline void convertGfxShortColor(SplashColorPtr dest,
154 SplashColorMode colorMode,
155 GfxColorSpace *colorSpace,
156 GfxColor *src) {
157 switch (colorMode) {
158 case splashModeMono1:
159 case splashModeMono8:
161 GfxGray gray;
162 colorSpace->getGray(src, &gray);
163 dest[0] = colToByte(gray);
165 break;
166 case splashModeXBGR8:
167 dest[3] = 255;
168 case splashModeBGR8:
169 case splashModeRGB8:
171 GfxRGB rgb;
172 colorSpace->getRGB(src, &rgb);
173 dest[0] = colToByte(rgb.r);
174 dest[1] = colToByte(rgb.g);
175 dest[2] = colToByte(rgb.b);
177 break;
178 #if SPLASH_CMYK
179 case splashModeCMYK8:
181 GfxCMYK cmyk;
182 colorSpace->getCMYK(src, &cmyk);
183 dest[0] = colToByte(cmyk.c);
184 dest[1] = colToByte(cmyk.m);
185 dest[2] = colToByte(cmyk.y);
186 dest[3] = colToByte(cmyk.k);
188 break;
189 case splashModeDeviceN8:
191 GfxColor deviceN;
192 colorSpace->getDeviceN(src, &deviceN);
193 for (int i = 0; i < SPOT_NCOMPS + 4; i++)
194 dest[i] = colToByte(deviceN.c[i]);
196 break;
197 #endif
201 //------------------------------------------------------------------------
202 // SplashGouraudPattern
203 //------------------------------------------------------------------------
204 SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA,
205 GfxState *stateA, GfxGouraudTriangleShading *shadingA, SplashColorMode modeA) {
206 SplashColor defaultColor;
207 GfxColor srcColor;
208 state = stateA;
209 shading = shadingA;
210 mode = modeA;
211 bDirectColorTranslation = bDirectColorTranslationA;
212 shadingA->getColorSpace()->getDefaultColor(&srcColor);
213 convertGfxColor(defaultColor, mode, shadingA->getColorSpace(), &srcColor);
214 gfxMode = shadingA->getColorSpace()->getMode();
217 SplashGouraudPattern::~SplashGouraudPattern() {
220 void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
221 GfxColor src;
222 GfxColorSpace* srcColorSpace = shading->getColorSpace();
223 int colorComps = 3;
224 #if SPLASH_CMYK
225 if (mode == splashModeCMYK8)
226 colorComps=4;
227 else if (mode == splashModeDeviceN8)
228 colorComps=4 + SPOT_NCOMPS;
229 #endif
231 shading->getParameterizedColor(colorinterp, &src);
233 if (bDirectColorTranslation) {
234 for (int m = 0; m < colorComps; ++m)
235 dest[m] = colToByte(src.c[m]);
236 } else {
237 convertGfxShortColor(dest, mode, srcColorSpace, &src);
241 //------------------------------------------------------------------------
242 // SplashUnivariatePattern
243 //------------------------------------------------------------------------
245 SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) {
246 Matrix ctm;
247 double xMin, yMin, xMax, yMax;
249 shading = shadingA;
250 state = stateA;
251 colorMode = colorModeA;
253 state->getCTM(&ctm);
254 ctm.invertTo(&ictm);
256 // get the function domain
257 t0 = shading->getDomain0();
258 t1 = shading->getDomain1();
259 dt = t1 - t0;
261 stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
262 shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax);
263 gfxMode = shadingA->getColorSpace()->getMode();
266 SplashUnivariatePattern::~SplashUnivariatePattern() {
269 GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) {
270 GfxColor gfxColor;
271 double xc, yc, t;
273 ictm.transform(x, y, &xc, &yc);
274 if (! getParameter (xc, yc, &t))
275 return gFalse;
277 shading->getColor(t, &gfxColor);
278 convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor);
279 return gTrue;
282 GBool SplashUnivariatePattern::testPosition(int x, int y) {
283 double xc, yc, t;
285 ictm.transform(x, y, &xc, &yc);
286 if (! getParameter (xc, yc, &t))
287 return gFalse;
288 return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0);
292 //------------------------------------------------------------------------
293 // SplashRadialPattern
294 //------------------------------------------------------------------------
295 #define RADIAL_EPSILON (1. / 1024 / 1024)
297 SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA):
298 SplashUnivariatePattern(colorModeA, stateA, shadingA)
300 SplashColor defaultColor;
301 GfxColor srcColor;
303 shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr);
304 dx -= x0;
305 dy -= y0;
306 dr -= r0;
307 a = dx*dx + dy*dy - dr*dr;
308 if (fabs(a) > RADIAL_EPSILON)
309 inva = 1.0 / a;
310 shadingA->getColorSpace()->getDefaultColor(&srcColor);
311 convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
314 SplashRadialPattern::~SplashRadialPattern() {
317 GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) {
318 double b, c, s0, s1;
320 // We want to solve this system of equations:
322 // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2
323 // 2. xc(s) = x0 + s * (x1 - xo)
324 // 3. yc(s) = y0 + s * (y1 - yo)
325 // 4. rc(s) = r0 + s * (r1 - ro)
327 // To simplify the system a little, we translate
328 // our coordinates to have the origin in (x0,y0)
330 xs -= x0;
331 ys -= y0;
333 // Then we have to solve the equation:
334 // A*s^2 - 2*B*s + C = 0
335 // where
336 // A = dx^2 + dy^2 - dr^2
337 // B = xs*dx + ys*dy + r0*dr
338 // C = xs^2 + ys^2 - r0^2
340 b = xs*dx + ys*dy + r0*dr;
341 c = xs*xs + ys*ys - r0*r0;
343 if (fabs(a) <= RADIAL_EPSILON) {
344 // A is 0, thus the equation simplifies to:
345 // -2*B*s + C = 0
346 // If B is 0, we can either have no solution or an indeterminate
347 // equation, thus we behave as if we had an invalid solution
348 if (fabs(b) <= RADIAL_EPSILON)
349 return gFalse;
351 s0 = s1 = 0.5 * c / b;
352 } else {
353 double d;
355 d = b*b - a*c;
356 if (d < 0)
357 return gFalse;
359 d = sqrt (d);
360 s0 = b + d;
361 s1 = b - d;
363 // If A < 0, one of the two solutions will have negative radius,
364 // thus it will be ignored. Otherwise we know that s1 <= s0
365 // (because d >=0 implies b - d <= b + d), so if both are valid it
366 // will be the true solution.
367 s0 *= inva;
368 s1 *= inva;
371 if (r0 + s0 * dr >= 0) {
372 if (0 <= s0 && s0 <= 1) {
373 *t = t0 + dt * s0;
374 return gTrue;
375 } else if (s0 < 0 && shading->getExtend0()) {
376 *t = t0;
377 return gTrue;
378 } else if (s0 > 1 && shading->getExtend1()) {
379 *t = t1;
380 return gTrue;
384 if (r0 + s1 * dr >= 0) {
385 if (0 <= s1 && s1 <= 1) {
386 *t = t0 + dt * s1;
387 return gTrue;
388 } else if (s1 < 0 && shading->getExtend0()) {
389 *t = t0;
390 return gTrue;
391 } else if (s1 > 1 && shading->getExtend1()) {
392 *t = t1;
393 return gTrue;
397 return gFalse;
400 #undef RADIAL_EPSILON
402 //------------------------------------------------------------------------
403 // SplashAxialPattern
404 //------------------------------------------------------------------------
406 SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA):
407 SplashUnivariatePattern(colorModeA, stateA, shadingA)
409 SplashColor defaultColor;
410 GfxColor srcColor;
412 shadingA->getCoords(&x0, &y0, &x1, &y1);
413 dx = x1 - x0;
414 dy = y1 - y0;
415 mul = 1 / (dx * dx + dy * dy);
416 shadingA->getColorSpace()->getDefaultColor(&srcColor);
417 convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
420 SplashAxialPattern::~SplashAxialPattern() {
423 GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
424 double s;
426 xc -= x0;
427 yc -= y0;
429 s = (xc * dx + yc * dy) * mul;
430 if (0 <= s && s <= 1) {
431 *t = t0 + dt * s;
432 } else if (s < 0 && shading->getExtend0()) {
433 *t = t0;
434 } else if (s > 1 && shading->getExtend1()) {
435 *t = t1;
436 } else {
437 return gFalse;
440 return gTrue;
443 //------------------------------------------------------------------------
444 // Type 3 font cache size parameters
445 #define type3FontCacheAssoc 8
446 #define type3FontCacheMaxSets 8
447 #define type3FontCacheSize (128*1024)
449 //------------------------------------------------------------------------
450 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
451 static inline Guchar div255(int x) {
452 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
455 #if SPLASH_CMYK
457 #include "GfxState_helpers.h"
459 //-------------------------------------------------------------------------
460 // helper for Blend functions (convert CMYK to RGB, do blend, convert back)
461 //-------------------------------------------------------------------------
463 // based in GfxState.cc
465 static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) {
466 double c, m, y, k, c1, m1, y1, k1, r, g, b;
468 c = colToDbl(byteToCol(cmyk[0]));
469 m = colToDbl(byteToCol(cmyk[1]));
470 y = colToDbl(byteToCol(cmyk[2]));
471 k = colToDbl(byteToCol(cmyk[3]));
472 c1 = 1 - c;
473 m1 = 1 - m;
474 y1 = 1 - y;
475 k1 = 1 - k;
476 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
477 rgb[0] = colToByte(clip01(dblToCol(r)));
478 rgb[1] = colToByte(clip01(dblToCol(g)));
479 rgb[2] = colToByte(clip01(dblToCol(b)));
482 static void rgbToCMYK(SplashColor rgb, SplashColorPtr cmyk) {
483 GfxColorComp c, m, y, k;
485 c = clip01(gfxColorComp1 - byteToCol(rgb[0]));
486 m = clip01(gfxColorComp1 - byteToCol(rgb[1]));
487 y = clip01(gfxColorComp1 - byteToCol(rgb[2]));
488 k = c;
489 if (m < k) {
490 k = m;
492 if (y < k) {
493 k = y;
495 cmyk[0] = colToByte(c - k);
496 cmyk[1] = colToByte(m - k);
497 cmyk[2] = colToByte(y - k);
498 cmyk[3] = colToByte(k);
501 #endif
503 //------------------------------------------------------------------------
504 // Blend functions
505 //------------------------------------------------------------------------
507 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
508 SplashColorPtr blend, SplashColorMode cm) {
509 int i;
511 #if SPLASH_CMYK
512 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
513 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
514 dest[i] = 255 - dest[i];
515 src[i] = 255 - src[i];
518 #endif
520 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
521 blend[i] = (dest[i] * src[i]) / 255;
524 #if SPLASH_CMYK
525 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
526 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
527 dest[i] = 255 - dest[i];
528 src[i] = 255 - src[i];
529 blend[i] = 255 - blend[i];
532 #endif
535 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
536 SplashColorPtr blend, SplashColorMode cm) {
537 int i;
539 #if SPLASH_CMYK
540 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
541 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
542 dest[i] = 255 - dest[i];
543 src[i] = 255 - src[i];
546 #endif
548 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
549 blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
552 #if SPLASH_CMYK
553 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
554 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
555 dest[i] = 255 - dest[i];
556 src[i] = 255 - src[i];
557 blend[i] = 255 - blend[i];
560 #endif
563 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
564 SplashColorPtr blend, SplashColorMode cm) {
565 int i;
567 #if SPLASH_CMYK
568 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
569 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
570 dest[i] = 255 - dest[i];
571 src[i] = 255 - src[i];
574 #endif
576 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
577 blend[i] = dest[i] < 0x80
578 ? (src[i] * 2 * dest[i]) / 255
579 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
582 #if SPLASH_CMYK
583 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
584 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
585 dest[i] = 255 - dest[i];
586 src[i] = 255 - src[i];
587 blend[i] = 255 - blend[i];
590 #endif
593 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
594 SplashColorPtr blend, SplashColorMode cm) {
595 int i;
597 #if SPLASH_CMYK
598 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
599 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
600 dest[i] = 255 - dest[i];
601 src[i] = 255 - src[i];
604 #endif
606 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
607 blend[i] = dest[i] < src[i] ? dest[i] : src[i];
610 #if SPLASH_CMYK
611 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
612 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
613 dest[i] = 255 - dest[i];
614 src[i] = 255 - src[i];
615 blend[i] = 255 - blend[i];
618 #endif
621 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
622 SplashColorPtr blend, SplashColorMode cm) {
623 int i;
625 #if SPLASH_CMYK
626 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
627 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
628 dest[i] = 255 - dest[i];
629 src[i] = 255 - src[i];
632 #endif
634 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
635 blend[i] = dest[i] > src[i] ? dest[i] : src[i];
638 #if SPLASH_CMYK
639 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
640 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
641 dest[i] = 255 - dest[i];
642 src[i] = 255 - src[i];
643 blend[i] = 255 - blend[i];
646 #endif
649 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
650 SplashColorPtr blend,
651 SplashColorMode cm) {
652 int i, x;
654 #if SPLASH_CMYK
655 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
656 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
657 dest[i] = 255 - dest[i];
658 src[i] = 255 - src[i];
661 #endif
663 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
664 if (src[i] == 255) {
665 blend[i] = 255;
666 } else {
667 x = (dest[i] * 255) / (255 - src[i]);
668 blend[i] = x <= 255 ? x : 255;
672 #if SPLASH_CMYK
673 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
674 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
675 dest[i] = 255 - dest[i];
676 src[i] = 255 - src[i];
677 blend[i] = 255 - blend[i];
680 #endif
683 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
684 SplashColorPtr blend, SplashColorMode cm) {
685 int i, x;
687 #if SPLASH_CMYK
688 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
689 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
690 dest[i] = 255 - dest[i];
691 src[i] = 255 - src[i];
694 #endif
696 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
697 if (src[i] == 0) {
698 blend[i] = 0;
699 } else {
700 x = ((255 - dest[i]) * 255) / src[i];
701 blend[i] = x <= 255 ? 255 - x : 0;
705 #if SPLASH_CMYK
706 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
707 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
708 dest[i] = 255 - dest[i];
709 src[i] = 255 - src[i];
710 blend[i] = 255 - blend[i];
713 #endif
716 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
717 SplashColorPtr blend, SplashColorMode cm) {
718 int i;
720 #if SPLASH_CMYK
721 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
722 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
723 dest[i] = 255 - dest[i];
724 src[i] = 255 - src[i];
727 #endif
729 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
730 blend[i] = src[i] < 0x80
731 ? (dest[i] * 2 * src[i]) / 255
732 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
735 #if SPLASH_CMYK
736 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
737 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
738 dest[i] = 255 - dest[i];
739 src[i] = 255 - src[i];
740 blend[i] = 255 - blend[i];
743 #endif
746 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
747 SplashColorPtr blend, SplashColorMode cm) {
748 int i, x;
750 #if SPLASH_CMYK
751 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
752 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
753 dest[i] = 255 - dest[i];
754 src[i] = 255 - src[i];
757 #endif
759 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
760 if (src[i] < 0x80) {
761 blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255);
762 } else {
763 if (dest[i] < 0x40) {
764 x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255;
765 } else {
766 x = (int)sqrt(255.0 * dest[i]);
768 blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
772 #if SPLASH_CMYK
773 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
774 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
775 dest[i] = 255 - dest[i];
776 src[i] = 255 - src[i];
777 blend[i] = 255 - blend[i];
780 #endif
783 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
784 SplashColorPtr blend,
785 SplashColorMode cm) {
786 int i;
788 #if SPLASH_CMYK
789 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
790 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
791 dest[i] = 255 - dest[i];
792 src[i] = 255 - src[i];
795 #endif
797 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
798 blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
801 #if SPLASH_CMYK
802 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
803 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
804 dest[i] = 255 - dest[i];
805 src[i] = 255 - src[i];
806 blend[i] = 255 - blend[i];
809 if (cm == splashModeDeviceN8) {
810 for (i = 4; i < splashColorModeNComps[cm]; ++i) {
811 if (dest[i] == 0 && src[i] == 0)
812 blend[i] = 0;
815 #endif
818 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
819 SplashColorPtr blend, SplashColorMode cm) {
820 int i;
822 #if SPLASH_CMYK
823 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
824 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
825 dest[i] = 255 - dest[i];
826 src[i] = 255 - src[i];
829 #endif
831 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
832 blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
835 #if SPLASH_CMYK
836 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
837 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
838 dest[i] = 255 - dest[i];
839 src[i] = 255 - src[i];
840 blend[i] = 255 - blend[i];
843 if (cm == splashModeDeviceN8) {
844 for (i = 4; i < splashColorModeNComps[cm]; ++i) {
845 if (dest[i] == 0 && src[i] == 0)
846 blend[i] = 0;
849 #endif
852 static int getLum(int r, int g, int b) {
853 return (int)(0.3 * r + 0.59 * g + 0.11 * b);
856 static int getSat(int r, int g, int b) {
857 int rgbMin, rgbMax;
859 rgbMin = rgbMax = r;
860 if (g < rgbMin) {
861 rgbMin = g;
862 } else if (g > rgbMax) {
863 rgbMax = g;
865 if (b < rgbMin) {
866 rgbMin = b;
867 } else if (b > rgbMax) {
868 rgbMax = b;
870 return rgbMax - rgbMin;
873 static void clipColor(int rIn, int gIn, int bIn,
874 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
875 int lum, rgbMin, rgbMax;
877 lum = getLum(rIn, gIn, bIn);
878 rgbMin = rgbMax = rIn;
879 if (gIn < rgbMin) {
880 rgbMin = gIn;
881 } else if (gIn > rgbMax) {
882 rgbMax = gIn;
884 if (bIn < rgbMin) {
885 rgbMin = bIn;
886 } else if (bIn > rgbMax) {
887 rgbMax = bIn;
889 if (rgbMin < 0) {
890 *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin));
891 *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin));
892 *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin));
893 } else if (rgbMax > 255) {
894 *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum));
895 *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum));
896 *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum));
897 } else {
898 *rOut = rIn;
899 *gOut = gIn;
900 *bOut = bIn;
904 static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
905 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
906 int d;
908 d = lum - getLum(rIn, gIn, bIn);
909 clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
912 static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
913 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
914 int rgbMin, rgbMid, rgbMax;
915 Guchar *minOut, *midOut, *maxOut;
917 if (rIn < gIn) {
918 rgbMin = rIn; minOut = rOut;
919 rgbMid = gIn; midOut = gOut;
920 } else {
921 rgbMin = gIn; minOut = gOut;
922 rgbMid = rIn; midOut = rOut;
924 if (bIn > rgbMid) {
925 rgbMax = bIn; maxOut = bOut;
926 } else if (bIn > rgbMin) {
927 rgbMax = rgbMid; maxOut = midOut;
928 rgbMid = bIn; midOut = bOut;
929 } else {
930 rgbMax = rgbMid; maxOut = midOut;
931 rgbMid = rgbMin; midOut = minOut;
932 rgbMin = bIn; minOut = bOut;
934 if (rgbMax > rgbMin) {
935 *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin);
936 *maxOut = (Guchar)sat;
937 } else {
938 *midOut = *maxOut = 0;
940 *minOut = 0;
943 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
944 SplashColorPtr blend, SplashColorMode cm) {
945 Guchar r0, g0, b0;
946 #if SPLASH_CMYK
947 Guchar r1, g1, b1;
948 int i;
949 SplashColor src2, dest2;
950 #endif
952 switch (cm) {
953 case splashModeMono1:
954 case splashModeMono8:
955 blend[0] = dest[0];
956 break;
957 case splashModeXBGR8:
958 src[3] = 255;
959 case splashModeRGB8:
960 case splashModeBGR8:
961 setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
962 &r0, &g0, &b0);
963 setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
964 &blend[0], &blend[1], &blend[2]);
965 break;
966 #if SPLASH_CMYK
967 case splashModeCMYK8:
968 case splashModeDeviceN8:
969 for (i = 0; i < 4; i++) {
970 // convert to additive
971 src2[i] = 0xff - src[i];
972 dest2[i] = 0xff - dest[i];
974 // NB: inputs have already been converted to additive mode
975 setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]),
976 &r0, &g0, &b0);
977 setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]),
978 &r1, &g1, &b1);
979 blend[0] = r1;
980 blend[1] = g1;
981 blend[2] = b1;
982 blend[3] = dest2[3];
983 for (i = 0; i < 4; i++) {
984 // convert back to subtractive
985 blend[i] = 0xff - blend[i];
987 break;
988 #endif
992 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
993 SplashColorPtr blend,
994 SplashColorMode cm) {
995 Guchar r0, g0, b0;
996 #if SPLASH_CMYK
997 Guchar r1, g1, b1;
998 int i;
999 SplashColor src2, dest2;
1000 #endif
1002 switch (cm) {
1003 case splashModeMono1:
1004 case splashModeMono8:
1005 blend[0] = dest[0];
1006 break;
1007 case splashModeXBGR8:
1008 src[3] = 255;
1009 case splashModeRGB8:
1010 case splashModeBGR8:
1011 setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
1012 &r0, &g0, &b0);
1013 setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
1014 &blend[0], &blend[1], &blend[2]);
1015 break;
1016 #if SPLASH_CMYK
1017 case splashModeCMYK8:
1018 case splashModeDeviceN8:
1019 for (i = 0; i < 4; i++) {
1020 // convert to additive
1021 src2[i] = 0xff - src[i];
1022 dest2[i] = 0xff - dest[i];
1024 setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]),
1025 &r0, &g0, &b0);
1026 setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]),
1027 &r1, &g1, &b1);
1028 blend[0] = r1;
1029 blend[1] = g1;
1030 blend[2] = b1;
1031 blend[3] = dest2[3];
1032 for (i = 0; i < 4; i++) {
1033 // convert back to subtractive
1034 blend[i] = 0xff - blend[i];
1036 break;
1037 #endif
1041 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
1042 SplashColorPtr blend, SplashColorMode cm) {
1043 #if SPLASH_CMYK
1044 Guchar r, g, b;
1045 int i;
1046 SplashColor src2, dest2;
1047 #endif
1049 switch (cm) {
1050 case splashModeMono1:
1051 case splashModeMono8:
1052 blend[0] = dest[0];
1053 break;
1054 case splashModeXBGR8:
1055 src[3] = 255;
1056 case splashModeRGB8:
1057 case splashModeBGR8:
1058 setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
1059 &blend[0], &blend[1], &blend[2]);
1060 break;
1061 #if SPLASH_CMYK
1062 case splashModeCMYK8:
1063 case splashModeDeviceN8:
1064 for (i = 0; i < 4; i++) {
1065 // convert to additive
1066 src2[i] = 0xff - src[i];
1067 dest2[i] = 0xff - dest[i];
1069 setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]),
1070 &r, &g, &b);
1071 blend[0] = r;
1072 blend[1] = g;
1073 blend[2] = b;
1074 blend[3] = dest2[3];
1075 for (i = 0; i < 4; i++) {
1076 // convert back to subtractive
1077 blend[i] = 0xff - blend[i];
1079 break;
1080 #endif
1084 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
1085 SplashColorPtr blend,
1086 SplashColorMode cm) {
1087 #if SPLASH_CMYK
1088 Guchar r, g, b;
1089 int i;
1090 SplashColor src2, dest2;
1091 #endif
1093 switch (cm) {
1094 case splashModeMono1:
1095 case splashModeMono8:
1096 blend[0] = dest[0];
1097 break;
1098 case splashModeXBGR8:
1099 src[3] = 255;
1100 case splashModeRGB8:
1101 case splashModeBGR8:
1102 setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
1103 &blend[0], &blend[1], &blend[2]);
1104 break;
1105 #if SPLASH_CMYK
1106 case splashModeCMYK8:
1107 case splashModeDeviceN8:
1108 for (i = 0; i < 4; i++) {
1109 // convert to additive
1110 src2[i] = 0xff - src[i];
1111 dest2[i] = 0xff - dest[i];
1113 setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]),
1114 &r, &g, &b);
1115 blend[0] = r;
1116 blend[1] = g;
1117 blend[2] = b;
1118 blend[3] = src2[3];
1119 for (i = 0; i < 4; i++) {
1120 // convert back to subtractive
1121 blend[i] = 0xff - blend[i];
1123 break;
1124 #endif
1128 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
1129 static const SplashBlendFunc splashOutBlendFuncs[] = {
1130 NULL,
1131 &splashOutBlendMultiply,
1132 &splashOutBlendScreen,
1133 &splashOutBlendOverlay,
1134 &splashOutBlendDarken,
1135 &splashOutBlendLighten,
1136 &splashOutBlendColorDodge,
1137 &splashOutBlendColorBurn,
1138 &splashOutBlendHardLight,
1139 &splashOutBlendSoftLight,
1140 &splashOutBlendDifference,
1141 &splashOutBlendExclusion,
1142 &splashOutBlendHue,
1143 &splashOutBlendSaturation,
1144 &splashOutBlendColor,
1145 &splashOutBlendLuminosity
1148 //------------------------------------------------------------------------
1149 // SplashOutFontFileID
1150 //------------------------------------------------------------------------
1152 class SplashOutFontFileID: public SplashFontFileID {
1153 public:
1155 SplashOutFontFileID(Ref *rA) { r = *rA; }
1157 ~SplashOutFontFileID() {}
1159 GBool matches(SplashFontFileID *id) {
1160 return ((SplashOutFontFileID *)id)->r.num == r.num &&
1161 ((SplashOutFontFileID *)id)->r.gen == r.gen;
1164 private:
1166 Ref r;
1169 //------------------------------------------------------------------------
1170 // T3FontCache
1171 //------------------------------------------------------------------------
1173 struct T3FontCacheTag {
1174 Gushort code;
1175 Gushort mru; // valid bit (0x8000) and MRU index
1178 class T3FontCache {
1179 public:
1181 T3FontCache(Ref *fontID, double m11A, double m12A,
1182 double m21A, double m22A,
1183 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1184 GBool aa, GBool validBBoxA);
1185 ~T3FontCache();
1186 GBool matches(Ref *idA, double m11A, double m12A,
1187 double m21A, double m22A)
1188 { return fontID.num == idA->num && fontID.gen == idA->gen &&
1189 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1191 Ref fontID; // PDF font ID
1192 double m11, m12, m21, m22; // transform matrix
1193 int glyphX, glyphY; // pixel offset of glyph bitmaps
1194 int glyphW, glyphH; // size of glyph bitmaps, in pixels
1195 GBool validBBox; // false if the bbox was [0 0 0 0]
1196 int glyphSize; // size of glyph bitmaps, in bytes
1197 int cacheSets; // number of sets in cache
1198 int cacheAssoc; // cache associativity (glyphs per set)
1199 Guchar *cacheData; // glyph pixmap cache
1200 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
1203 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1204 double m21A, double m22A,
1205 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1206 GBool validBBoxA, GBool aa) {
1207 int i;
1209 fontID = *fontIDA;
1210 m11 = m11A;
1211 m12 = m12A;
1212 m21 = m21A;
1213 m22 = m22A;
1214 glyphX = glyphXA;
1215 glyphY = glyphYA;
1216 glyphW = glyphWA;
1217 glyphH = glyphHA;
1218 validBBox = validBBoxA;
1219 // sanity check for excessively large glyphs (which most likely
1220 // indicate an incorrect BBox)
1221 i = glyphW * glyphH;
1222 if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
1223 glyphW = glyphH = 100;
1224 validBBox = gFalse;
1226 if (aa) {
1227 glyphSize = glyphW * glyphH;
1228 } else {
1229 glyphSize = ((glyphW + 7) >> 3) * glyphH;
1231 cacheAssoc = type3FontCacheAssoc;
1232 for (cacheSets = type3FontCacheMaxSets;
1233 cacheSets > 1 &&
1234 cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
1235 cacheSets >>= 1) ;
1236 if (glyphSize < 10485760 / cacheAssoc / cacheSets) {
1237 cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
1238 } else {
1239 error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n"
1240 " This could teoretically result in wrong rendering,\n"
1241 " but most probably the document is bogus.\n"
1242 " Please report a bug if you think the rendering may be wrong because of this.");
1243 cacheData = NULL;
1245 if (cacheData != NULL)
1247 cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
1248 sizeof(T3FontCacheTag));
1249 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1250 cacheTags[i].mru = i & (cacheAssoc - 1);
1253 else
1255 cacheTags = NULL;
1259 T3FontCache::~T3FontCache() {
1260 gfree(cacheData);
1261 gfree(cacheTags);
1264 struct T3GlyphStack {
1265 Gushort code; // character code
1267 //----- cache info
1268 T3FontCache *cache; // font cache for the current font
1269 T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
1270 Guchar *cacheData; // pointer to cache data for the glyph
1272 //----- saved state
1273 SplashBitmap *origBitmap;
1274 Splash *origSplash;
1275 double origCTM4, origCTM5;
1277 T3GlyphStack *next; // next object on stack
1280 //------------------------------------------------------------------------
1281 // SplashTransparencyGroup
1282 //------------------------------------------------------------------------
1284 struct SplashTransparencyGroup {
1285 int tx, ty; // translation coordinates
1286 SplashBitmap *tBitmap; // bitmap for transparency group
1287 SplashBitmap *softmask; // bitmap for softmasks
1288 GfxColorSpace *blendingColorSpace;
1289 GBool isolated;
1291 //----- for knockout
1292 SplashBitmap *shape;
1293 GBool knockout;
1294 SplashCoord knockoutOpacity;
1295 GBool fontAA;
1297 //----- saved state
1298 SplashBitmap *origBitmap;
1299 Splash *origSplash;
1301 SplashTransparencyGroup *next;
1304 //------------------------------------------------------------------------
1305 // SplashOutputDev
1306 //------------------------------------------------------------------------
1308 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
1309 int bitmapRowPadA,
1310 GBool reverseVideoA,
1311 SplashColorPtr paperColorA,
1312 GBool bitmapTopDownA,
1313 SplashThinLineMode thinLineMode,
1314 GBool overprintPreviewA) {
1315 colorMode = colorModeA;
1316 bitmapRowPad = bitmapRowPadA;
1317 bitmapTopDown = bitmapTopDownA;
1318 bitmapUpsideDown = gFalse;
1319 fontAntialias = gTrue;
1320 vectorAntialias = gTrue;
1321 overprintPreview = overprintPreviewA;
1322 enableFreeTypeHinting = gFalse;
1323 enableSlightHinting = gFalse;
1324 setupScreenParams(72.0, 72.0);
1325 reverseVideo = reverseVideoA;
1326 if (paperColorA != NULL) {
1327 splashColorCopy(paperColor, paperColorA);
1328 } else {
1329 splashClearColor(paperColor);
1331 skipHorizText = gFalse;
1332 skipRotatedText = gFalse;
1333 keepAlphaChannel = paperColorA == NULL;
1335 doc = NULL;
1337 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
1338 colorMode != splashModeMono1, bitmapTopDown);
1339 splash = new Splash(bitmap, vectorAntialias, &screenParams);
1340 splash->setMinLineWidth(globalParams->getMinLineWidth());
1341 splash->setThinLineMode(thinLineMode);
1342 splash->clear(paperColor, 0);
1344 fontEngine = NULL;
1346 nT3Fonts = 0;
1347 t3GlyphStack = NULL;
1349 font = NULL;
1350 needFontUpdate = gFalse;
1351 textClipPath = NULL;
1352 transpGroupStack = NULL;
1353 nestCount = 0;
1354 xref = NULL;
1357 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
1358 screenParams.size = globalParams->getScreenSize();
1359 screenParams.dotRadius = globalParams->getScreenDotRadius();
1360 screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
1361 screenParams.blackThreshold =
1362 (SplashCoord)globalParams->getScreenBlackThreshold();
1363 screenParams.whiteThreshold =
1364 (SplashCoord)globalParams->getScreenWhiteThreshold();
1365 switch (globalParams->getScreenType()) {
1366 case screenDispersed:
1367 screenParams.type = splashScreenDispersed;
1368 if (screenParams.size < 0) {
1369 screenParams.size = 4;
1371 break;
1372 case screenClustered:
1373 screenParams.type = splashScreenClustered;
1374 if (screenParams.size < 0) {
1375 screenParams.size = 10;
1377 break;
1378 case screenStochasticClustered:
1379 screenParams.type = splashScreenStochasticClustered;
1380 if (screenParams.size < 0) {
1381 screenParams.size = 64;
1383 if (screenParams.dotRadius < 0) {
1384 screenParams.dotRadius = 2;
1386 break;
1387 case screenUnset:
1388 default:
1389 // use clustered dithering for resolution >= 300 dpi
1390 // (compare to 299.9 to avoid floating point issues)
1391 if (hDPI > 299.9 && vDPI > 299.9) {
1392 screenParams.type = splashScreenStochasticClustered;
1393 if (screenParams.size < 0) {
1394 screenParams.size = 64;
1396 if (screenParams.dotRadius < 0) {
1397 screenParams.dotRadius = 2;
1399 } else {
1400 screenParams.type = splashScreenDispersed;
1401 if (screenParams.size < 0) {
1402 screenParams.size = 4;
1408 SplashOutputDev::~SplashOutputDev() {
1409 int i;
1411 for (i = 0; i < nT3Fonts; ++i) {
1412 delete t3FontCache[i];
1414 if (fontEngine) {
1415 delete fontEngine;
1417 if (splash) {
1418 delete splash;
1420 if (bitmap) {
1421 delete bitmap;
1425 void SplashOutputDev::startDoc(PDFDoc *docA) {
1426 int i;
1428 doc = docA;
1429 if (fontEngine) {
1430 delete fontEngine;
1432 fontEngine = new SplashFontEngine(
1433 #if HAVE_T1LIB_H
1434 globalParams->getEnableT1lib(),
1435 #endif
1436 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
1437 globalParams->getEnableFreeType(),
1438 enableFreeTypeHinting,
1439 enableSlightHinting,
1440 #endif
1441 getFontAntialias() &&
1442 colorMode != splashModeMono1);
1443 for (i = 0; i < nT3Fonts; ++i) {
1444 delete t3FontCache[i];
1446 nT3Fonts = 0;
1449 void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) {
1450 int w, h;
1451 double *ctm;
1452 SplashCoord mat[6];
1453 SplashColor color;
1455 xref = xrefA;
1456 if (state) {
1457 setupScreenParams(state->getHDPI(), state->getVDPI());
1458 w = (int)(state->getPageWidth() + 0.5);
1459 if (w <= 0) {
1460 w = 1;
1462 h = (int)(state->getPageHeight() + 0.5);
1463 if (h <= 0) {
1464 h = 1;
1466 } else {
1467 w = h = 1;
1469 SplashThinLineMode thinLineMode = splashThinLineDefault;
1470 if (splash) {
1471 thinLineMode = splash->getThinLineMode();
1472 delete splash;
1473 splash = NULL;
1475 if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
1476 if (bitmap) {
1477 delete bitmap;
1478 bitmap = NULL;
1480 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1481 colorMode != splashModeMono1, bitmapTopDown);
1482 if (!bitmap->getDataPtr()) {
1483 delete bitmap;
1484 w = h = 1;
1485 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1486 colorMode != splashModeMono1, bitmapTopDown);
1489 splash = new Splash(bitmap, vectorAntialias, &screenParams);
1490 splash->setThinLineMode(thinLineMode);
1491 splash->setMinLineWidth(globalParams->getMinLineWidth());
1492 if (state) {
1493 ctm = state->getCTM();
1494 mat[0] = (SplashCoord)ctm[0];
1495 mat[1] = (SplashCoord)ctm[1];
1496 mat[2] = (SplashCoord)ctm[2];
1497 mat[3] = (SplashCoord)ctm[3];
1498 mat[4] = (SplashCoord)ctm[4];
1499 mat[5] = (SplashCoord)ctm[5];
1500 splash->setMatrix(mat);
1502 switch (colorMode) {
1503 case splashModeMono1:
1504 case splashModeMono8:
1505 color[0] = 0;
1506 break;
1507 case splashModeXBGR8:
1508 color[3] = 255;
1509 case splashModeRGB8:
1510 case splashModeBGR8:
1511 color[0] = color[1] = color[2] = 0;
1512 break;
1513 #if SPLASH_CMYK
1514 case splashModeCMYK8:
1515 color[0] = color[1] = color[2] = color[3] = 0;
1516 break;
1517 case splashModeDeviceN8:
1518 for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
1519 color[i] = 0;
1520 break;
1521 #endif
1523 splash->setStrokePattern(new SplashSolidColor(color));
1524 splash->setFillPattern(new SplashSolidColor(color));
1525 splash->setLineCap(splashLineCapButt);
1526 splash->setLineJoin(splashLineJoinMiter);
1527 splash->setLineDash(NULL, 0, 0);
1528 splash->setMiterLimit(10);
1529 splash->setFlatness(1);
1530 // the SA parameter supposedly defaults to false, but Acrobat
1531 // apparently hardwires it to true
1532 splash->setStrokeAdjust(globalParams->getStrokeAdjust());
1533 splash->clear(paperColor, 0);
1536 void SplashOutputDev::endPage() {
1537 if (colorMode != splashModeMono1 && !keepAlphaChannel) {
1538 splash->compositeBackground(paperColor);
1542 void SplashOutputDev::saveState(GfxState *state) {
1543 splash->saveState();
1546 void SplashOutputDev::restoreState(GfxState *state) {
1547 splash->restoreState();
1548 needFontUpdate = gTrue;
1551 void SplashOutputDev::updateAll(GfxState *state) {
1552 updateLineDash(state);
1553 updateLineJoin(state);
1554 updateLineCap(state);
1555 updateLineWidth(state);
1556 updateFlatness(state);
1557 updateMiterLimit(state);
1558 updateStrokeAdjust(state);
1559 updateFillColorSpace(state);
1560 updateFillColor(state);
1561 updateStrokeColorSpace(state);
1562 updateStrokeColor(state);
1563 needFontUpdate = gTrue;
1566 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
1567 double m21, double m22,
1568 double m31, double m32) {
1569 double *ctm;
1570 SplashCoord mat[6];
1572 ctm = state->getCTM();
1573 mat[0] = (SplashCoord)ctm[0];
1574 mat[1] = (SplashCoord)ctm[1];
1575 mat[2] = (SplashCoord)ctm[2];
1576 mat[3] = (SplashCoord)ctm[3];
1577 mat[4] = (SplashCoord)ctm[4];
1578 mat[5] = (SplashCoord)ctm[5];
1579 splash->setMatrix(mat);
1582 void SplashOutputDev::updateLineDash(GfxState *state) {
1583 double *dashPattern;
1584 int dashLength;
1585 double dashStart;
1586 SplashCoord dash[20];
1587 int i;
1589 state->getLineDash(&dashPattern, &dashLength, &dashStart);
1590 if (dashLength > 20) {
1591 dashLength = 20;
1593 for (i = 0; i < dashLength; ++i) {
1594 dash[i] = (SplashCoord)dashPattern[i];
1595 if (dash[i] < 0) {
1596 dash[i] = 0;
1599 splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
1602 void SplashOutputDev::updateFlatness(GfxState *state) {
1603 #if 0 // Acrobat ignores the flatness setting, and always renders curves
1604 // with a fairly small flatness value
1605 splash->setFlatness(state->getFlatness());
1606 #endif
1609 void SplashOutputDev::updateLineJoin(GfxState *state) {
1610 splash->setLineJoin(state->getLineJoin());
1613 void SplashOutputDev::updateLineCap(GfxState *state) {
1614 splash->setLineCap(state->getLineCap());
1617 void SplashOutputDev::updateMiterLimit(GfxState *state) {
1618 splash->setMiterLimit(state->getMiterLimit());
1621 void SplashOutputDev::updateLineWidth(GfxState *state) {
1622 splash->setLineWidth(state->getLineWidth());
1625 void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) {
1626 #if 0 // the SA parameter supposedly defaults to false, but Acrobat
1627 // apparently hardwires it to true
1628 splash->setStrokeAdjust(state->getStrokeAdjust());
1629 #endif
1632 void SplashOutputDev::updateFillColorSpace(GfxState *state) {
1633 #if SPLASH_CMYK
1634 if (colorMode == splashModeDeviceN8)
1635 state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
1636 #endif
1639 void SplashOutputDev::updateStrokeColorSpace(GfxState *state) {
1640 #if SPLASH_CMYK
1641 if (colorMode == splashModeDeviceN8)
1642 state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
1643 #endif
1646 void SplashOutputDev::updateFillColor(GfxState *state) {
1647 GfxGray gray;
1648 GfxRGB rgb;
1649 #if SPLASH_CMYK
1650 GfxCMYK cmyk;
1651 GfxColor deviceN;
1652 #endif
1654 switch (colorMode) {
1655 case splashModeMono1:
1656 case splashModeMono8:
1657 state->getFillGray(&gray);
1658 splash->setFillPattern(getColor(gray));
1659 break;
1660 case splashModeXBGR8:
1661 case splashModeRGB8:
1662 case splashModeBGR8:
1663 state->getFillRGB(&rgb);
1664 splash->setFillPattern(getColor(&rgb));
1665 break;
1666 #if SPLASH_CMYK
1667 case splashModeCMYK8:
1668 state->getFillCMYK(&cmyk);
1669 splash->setFillPattern(getColor(&cmyk));
1670 break;
1671 case splashModeDeviceN8:
1672 state->getFillDeviceN(&deviceN);
1673 splash->setFillPattern(getColor(&deviceN));
1674 break;
1675 #endif
1679 void SplashOutputDev::updateStrokeColor(GfxState *state) {
1680 GfxGray gray;
1681 GfxRGB rgb;
1682 #if SPLASH_CMYK
1683 GfxCMYK cmyk;
1684 GfxColor deviceN;
1685 #endif
1687 switch (colorMode) {
1688 case splashModeMono1:
1689 case splashModeMono8:
1690 state->getStrokeGray(&gray);
1691 splash->setStrokePattern(getColor(gray));
1692 break;
1693 case splashModeXBGR8:
1694 case splashModeRGB8:
1695 case splashModeBGR8:
1696 state->getStrokeRGB(&rgb);
1697 splash->setStrokePattern(getColor(&rgb));
1698 break;
1699 #if SPLASH_CMYK
1700 case splashModeCMYK8:
1701 state->getStrokeCMYK(&cmyk);
1702 splash->setStrokePattern(getColor(&cmyk));
1703 break;
1704 case splashModeDeviceN8:
1705 state->getStrokeDeviceN(&deviceN);
1706 splash->setStrokePattern(getColor(&deviceN));
1707 break;
1708 #endif
1712 SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
1713 SplashColor color;
1715 if (reverseVideo) {
1716 gray = gfxColorComp1 - gray;
1718 color[0] = colToByte(gray);
1719 return new SplashSolidColor(color);
1722 SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
1723 GfxColorComp r, g, b;
1724 SplashColor color;
1726 if (reverseVideo) {
1727 r = gfxColorComp1 - rgb->r;
1728 g = gfxColorComp1 - rgb->g;
1729 b = gfxColorComp1 - rgb->b;
1730 } else {
1731 r = rgb->r;
1732 g = rgb->g;
1733 b = rgb->b;
1735 color[0] = colToByte(r);
1736 color[1] = colToByte(g);
1737 color[2] = colToByte(b);
1738 if (colorMode == splashModeXBGR8) color[3] = 255;
1739 return new SplashSolidColor(color);
1742 #if SPLASH_CMYK
1743 SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
1744 SplashColor color;
1746 color[0] = colToByte(cmyk->c);
1747 color[1] = colToByte(cmyk->m);
1748 color[2] = colToByte(cmyk->y);
1749 color[3] = colToByte(cmyk->k);
1750 return new SplashSolidColor(color);
1753 SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) {
1754 SplashColor color;
1756 for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
1757 color[i] = colToByte(deviceN->c[i]);
1758 return new SplashSolidColor(color);
1760 #endif
1762 void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
1763 GBool overprintFlag,
1764 int overprintMode,
1765 GfxColor *singleColor,
1766 GBool grayIndexed) {
1767 #if SPLASH_CMYK
1768 Guint mask;
1769 GfxCMYK cmyk;
1770 GBool additive = gFalse;
1771 int i;
1773 if (colorSpace->getMode() == csIndexed) {
1774 setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(),
1775 overprintFlag,
1776 overprintMode,
1777 singleColor,
1778 grayIndexed);
1779 return;
1781 if (overprintFlag && overprintPreview) {
1782 mask = colorSpace->getOverprintMask();
1783 if (singleColor && overprintMode &&
1784 colorSpace->getMode() == csDeviceCMYK) {
1785 colorSpace->getCMYK(singleColor, &cmyk);
1786 if (cmyk.c == 0) {
1787 mask &= ~1;
1789 if (cmyk.m == 0) {
1790 mask &= ~2;
1792 if (cmyk.y == 0) {
1793 mask &= ~4;
1795 if (cmyk.k == 0) {
1796 mask &= ~8;
1799 if (grayIndexed) {
1800 mask &= ~7;
1801 } else if (colorSpace->getMode() == csSeparation) {
1802 GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace;
1803 additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking();
1804 } else if (colorSpace->getMode() == csDeviceN) {
1805 GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
1806 additive = mask == 0x0f && !deviceNCS->isNonMarking();
1807 for (i = 0; i < deviceNCS->getNComps() && additive; i++) {
1808 if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) {
1809 additive = gFalse;
1810 } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) {
1811 additive = gFalse;
1812 } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) {
1813 additive = gFalse;
1814 } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) {
1815 additive = gFalse;
1819 } else {
1820 mask = 0xffffffff;
1822 splash->setOverprintMask(mask, additive);
1823 #endif
1826 void SplashOutputDev::updateBlendMode(GfxState *state) {
1827 splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1830 void SplashOutputDev::updateFillOpacity(GfxState *state) {
1831 splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1832 if (transpGroupStack != NULL && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) {
1833 transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity();
1837 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1838 splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1839 if (transpGroupStack != NULL && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) {
1840 transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity();
1844 void SplashOutputDev::updateFillOverprint(GfxState *state) {
1845 splash->setFillOverprint(state->getFillOverprint());
1848 void SplashOutputDev::updateStrokeOverprint(GfxState *state) {
1849 splash->setStrokeOverprint(state->getStrokeOverprint());
1852 void SplashOutputDev::updateOverprintMode(GfxState *state) {
1853 splash->setOverprintMode(state->getOverprintMode());
1856 void SplashOutputDev::updateTransfer(GfxState *state) {
1857 Function **transfer;
1858 Guchar red[256], green[256], blue[256], gray[256];
1859 double x, y;
1860 int i;
1862 transfer = state->getTransfer();
1863 if (transfer[0] &&
1864 transfer[0]->getInputSize() == 1 &&
1865 transfer[0]->getOutputSize() == 1) {
1866 if (transfer[1] &&
1867 transfer[1]->getInputSize() == 1 &&
1868 transfer[1]->getOutputSize() == 1 &&
1869 transfer[2] &&
1870 transfer[2]->getInputSize() == 1 &&
1871 transfer[2]->getOutputSize() == 1 &&
1872 transfer[3] &&
1873 transfer[3]->getInputSize() == 1 &&
1874 transfer[3]->getOutputSize() == 1) {
1875 for (i = 0; i < 256; ++i) {
1876 x = i / 255.0;
1877 transfer[0]->transform(&x, &y);
1878 red[i] = (Guchar)(y * 255.0 + 0.5);
1879 transfer[1]->transform(&x, &y);
1880 green[i] = (Guchar)(y * 255.0 + 0.5);
1881 transfer[2]->transform(&x, &y);
1882 blue[i] = (Guchar)(y * 255.0 + 0.5);
1883 transfer[3]->transform(&x, &y);
1884 gray[i] = (Guchar)(y * 255.0 + 0.5);
1886 } else {
1887 for (i = 0; i < 256; ++i) {
1888 x = i / 255.0;
1889 transfer[0]->transform(&x, &y);
1890 red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1893 } else {
1894 for (i = 0; i < 256; ++i) {
1895 red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1898 splash->setTransfer(red, green, blue, gray);
1901 void SplashOutputDev::updateFont(GfxState * /*state*/) {
1902 needFontUpdate = gTrue;
1905 void SplashOutputDev::doUpdateFont(GfxState *state) {
1906 GfxFont *gfxFont;
1907 GfxFontLoc *fontLoc;
1908 GfxFontType fontType;
1909 SplashOutFontFileID *id;
1910 SplashFontFile *fontFile;
1911 SplashFontSrc *fontsrc = NULL;
1912 FoFiTrueType *ff;
1913 Object refObj, strObj;
1914 GooString *fileName;
1915 char *tmpBuf;
1916 int tmpBufLen;
1917 int *codeToGID;
1918 double *textMat;
1919 double m11, m12, m21, m22, fontSize;
1920 int faceIndex = 0;
1921 SplashCoord mat[4];
1922 int n, i;
1923 GBool recreateFont = gFalse;
1924 GBool doAdjustFontMatrix = gFalse;
1926 needFontUpdate = gFalse;
1927 font = NULL;
1928 fileName = NULL;
1929 tmpBuf = NULL;
1930 fontLoc = NULL;
1932 if (!(gfxFont = state->getFont())) {
1933 goto err1;
1935 fontType = gfxFont->getType();
1936 if (fontType == fontType3) {
1937 goto err1;
1940 // sanity-check the font size - skip anything larger than 10 inches
1941 // (this avoids problems allocating memory for the font cache)
1942 if (state->getTransformedFontSize()
1943 > 10 * (state->getHDPI() + state->getVDPI())) {
1944 goto err1;
1947 // check the font file cache
1948 id = new SplashOutFontFileID(gfxFont->getID());
1949 if ((fontFile = fontEngine->getFontFile(id))) {
1950 delete id;
1952 } else {
1954 if (!(fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), NULL))) {
1955 error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1956 gfxFont->getName() ? gfxFont->getName()->getCString()
1957 : "(unnamed)");
1958 goto err2;
1961 // embedded font
1962 if (fontLoc->locType == gfxFontLocEmbedded) {
1963 // if there is an embedded font, read it to memory
1964 tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef(), &tmpBufLen);
1965 if (! tmpBuf)
1966 goto err2;
1968 // external font
1969 } else { // gfxFontLocExternal
1970 fileName = fontLoc->path;
1971 fontType = fontLoc->fontType;
1972 doAdjustFontMatrix = gTrue;
1975 fontsrc = new SplashFontSrc;
1976 if (fileName)
1977 fontsrc->setFile(fileName, gFalse);
1978 else
1979 fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue);
1981 // load the font file
1982 switch (fontType) {
1983 case fontType1:
1984 if (!(fontFile = fontEngine->loadType1Font(
1986 fontsrc,
1987 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1988 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
1989 gfxFont->getName() ? gfxFont->getName()->getCString()
1990 : "(unnamed)");
1991 goto err2;
1993 break;
1994 case fontType1C:
1995 if (!(fontFile = fontEngine->loadType1CFont(
1997 fontsrc,
1998 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
1999 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2000 gfxFont->getName() ? gfxFont->getName()->getCString()
2001 : "(unnamed)");
2002 goto err2;
2004 break;
2005 case fontType1COT:
2006 if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
2008 fontsrc,
2009 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
2010 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2011 gfxFont->getName() ? gfxFont->getName()->getCString()
2012 : "(unnamed)");
2013 goto err2;
2015 break;
2016 case fontTrueType:
2017 case fontTrueTypeOT:
2018 if (fileName)
2019 ff = FoFiTrueType::load(fileName->getCString());
2020 else
2021 ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
2022 if (ff) {
2023 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
2024 n = 256;
2025 delete ff;
2026 // if we're substituting for a non-TrueType font, we need to mark
2027 // all notdef codes as "do not draw" (rather than drawing TrueType
2028 // notdef glyphs)
2029 if (gfxFont->getType() != fontTrueType &&
2030 gfxFont->getType() != fontTrueTypeOT) {
2031 for (i = 0; i < 256; ++i) {
2032 if (codeToGID[i] == 0) {
2033 codeToGID[i] = -1;
2037 } else {
2038 codeToGID = NULL;
2039 n = 0;
2041 if (!(fontFile = fontEngine->loadTrueTypeFont(
2043 fontsrc,
2044 codeToGID, n))) {
2045 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2046 gfxFont->getName() ? gfxFont->getName()->getCString()
2047 : "(unnamed)");
2048 goto err2;
2050 break;
2051 case fontCIDType0:
2052 case fontCIDType0C:
2053 if (!(fontFile = fontEngine->loadCIDFont(
2055 fontsrc))) {
2056 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2057 gfxFont->getName() ? gfxFont->getName()->getCString()
2058 : "(unnamed)");
2059 goto err2;
2061 break;
2062 case fontCIDType0COT:
2063 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
2064 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
2065 codeToGID = (int *)gmallocn(n, sizeof(int));
2066 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
2067 n * sizeof(int));
2068 } else {
2069 codeToGID = NULL;
2070 n = 0;
2072 if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
2074 fontsrc,
2075 codeToGID, n))) {
2076 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2077 gfxFont->getName() ? gfxFont->getName()->getCString()
2078 : "(unnamed)");
2079 goto err2;
2081 break;
2082 case fontCIDType2:
2083 case fontCIDType2OT:
2084 codeToGID = NULL;
2085 n = 0;
2086 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
2087 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
2088 if (n) {
2089 codeToGID = (int *)gmallocn(n, sizeof(int));
2090 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
2091 n * sizeof(int));
2093 } else {
2094 if (fileName)
2095 ff = FoFiTrueType::load(fileName->getCString());
2096 else
2097 ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
2098 if (! ff)
2100 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2101 gfxFont->getName() ? gfxFont->getName()->getCString()
2102 : "(unnamed)");
2103 goto err2;
2105 codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
2106 delete ff;
2108 if (!(fontFile = fontEngine->loadTrueTypeFont(
2110 fontsrc,
2111 codeToGID, n, faceIndex))) {
2112 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2113 gfxFont->getName() ? gfxFont->getName()->getCString()
2114 : "(unnamed)");
2115 goto err2;
2117 break;
2118 default:
2119 // this shouldn't happen
2120 goto err2;
2122 fontFile->doAdjustMatrix = doAdjustFontMatrix;
2125 // get the font matrix
2126 textMat = state->getTextMat();
2127 fontSize = state->getFontSize();
2128 m11 = textMat[0] * fontSize * state->getHorizScaling();
2129 m12 = textMat[1] * fontSize * state->getHorizScaling();
2130 m21 = textMat[2] * fontSize;
2131 m22 = textMat[3] * fontSize;
2133 // create the scaled font
2134 mat[0] = m11; mat[1] = m12;
2135 mat[2] = m21; mat[3] = m22;
2136 font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
2138 // for substituted fonts: adjust the font matrix -- compare the
2139 // width of 'm' in the original font and the substituted font
2140 if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) {
2141 double w1, w2;
2142 CharCode code;
2143 char *name;
2144 for (code = 0; code < 256; ++code) {
2145 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
2146 name[0] == 'm' && name[1] == '\0') {
2147 break;
2150 if (code < 256) {
2151 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
2152 w2 = font->getGlyphAdvance(code);
2153 if (!gfxFont->isSymbolic() && w2 > 0) {
2154 // if real font is substantially narrower than substituted
2155 // font, reduce the font size accordingly
2156 if (w1 > 0.01 && w1 < 0.9 * w2) {
2157 w1 /= w2;
2158 m11 *= w1;
2159 m21 *= w1;
2160 recreateFont = gTrue;
2166 if (recreateFont)
2168 mat[0] = m11; mat[1] = m12;
2169 mat[2] = m21; mat[3] = m22;
2170 font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
2173 delete fontLoc;
2174 if (fontsrc && !fontsrc->isFile)
2175 fontsrc->unref();
2176 return;
2178 err2:
2179 delete id;
2180 delete fontLoc;
2181 err1:
2182 if (fontsrc && !fontsrc->isFile)
2183 fontsrc->unref();
2184 return;
2187 void SplashOutputDev::stroke(GfxState *state) {
2188 SplashPath *path;
2190 if (state->getStrokeColorSpace()->isNonMarking()) {
2191 return;
2193 setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(),
2194 state->getOverprintMode(), state->getStrokeColor());
2195 path = convertPath(state, state->getPath(), gFalse);
2196 splash->stroke(path);
2197 delete path;
2200 void SplashOutputDev::fill(GfxState *state) {
2201 SplashPath *path;
2203 if (state->getFillColorSpace()->isNonMarking()) {
2204 return;
2206 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2207 state->getOverprintMode(), state->getFillColor());
2208 path = convertPath(state, state->getPath(), gTrue);
2209 splash->fill(path, gFalse);
2210 delete path;
2213 void SplashOutputDev::eoFill(GfxState *state) {
2214 SplashPath *path;
2216 if (state->getFillColorSpace()->isNonMarking()) {
2217 return;
2219 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2220 state->getOverprintMode(), state->getFillColor());
2221 path = convertPath(state, state->getPath(), gTrue);
2222 splash->fill(path, gTrue);
2223 delete path;
2226 void SplashOutputDev::clip(GfxState *state) {
2227 SplashPath *path;
2229 path = convertPath(state, state->getPath(), gTrue);
2230 splash->clipToPath(path, gFalse);
2231 delete path;
2234 void SplashOutputDev::eoClip(GfxState *state) {
2235 SplashPath *path;
2237 path = convertPath(state, state->getPath(), gTrue);
2238 splash->clipToPath(path, gTrue);
2239 delete path;
2242 void SplashOutputDev::clipToStrokePath(GfxState *state) {
2243 SplashPath *path, *path2;
2245 path = convertPath(state, state->getPath(), gFalse);
2246 path2 = splash->makeStrokePath(path, state->getLineWidth());
2247 delete path;
2248 splash->clipToPath(path2, gFalse);
2249 delete path2;
2252 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
2253 GBool dropEmptySubpaths) {
2254 SplashPath *sPath;
2255 GfxSubpath *subpath;
2256 int n, i, j;
2258 n = dropEmptySubpaths ? 1 : 0;
2259 sPath = new SplashPath();
2260 for (i = 0; i < path->getNumSubpaths(); ++i) {
2261 subpath = path->getSubpath(i);
2262 if (subpath->getNumPoints() > n) {
2263 sPath->moveTo((SplashCoord)subpath->getX(0),
2264 (SplashCoord)subpath->getY(0));
2265 j = 1;
2266 while (j < subpath->getNumPoints()) {
2267 if (subpath->getCurve(j)) {
2268 sPath->curveTo((SplashCoord)subpath->getX(j),
2269 (SplashCoord)subpath->getY(j),
2270 (SplashCoord)subpath->getX(j+1),
2271 (SplashCoord)subpath->getY(j+1),
2272 (SplashCoord)subpath->getX(j+2),
2273 (SplashCoord)subpath->getY(j+2));
2274 j += 3;
2275 } else {
2276 sPath->lineTo((SplashCoord)subpath->getX(j),
2277 (SplashCoord)subpath->getY(j));
2278 ++j;
2281 if (subpath->isClosed()) {
2282 sPath->close();
2286 return sPath;
2289 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
2290 double dx, double dy,
2291 double originX, double originY,
2292 CharCode code, int nBytes,
2293 Unicode *u, int uLen) {
2294 SplashPath *path;
2295 int render;
2296 GBool doFill, doStroke, doClip, strokeAdjust;
2297 double m[4];
2298 GBool horiz;
2300 if (skipHorizText || skipRotatedText) {
2301 state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2302 horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2303 fabs(m[2]) < 0.001 && m[3] < 0;
2304 if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2305 return;
2309 // check for invisible text -- this is used by Acrobat Capture
2310 render = state->getRender();
2311 if (render == 3) {
2312 return;
2315 if (needFontUpdate) {
2316 doUpdateFont(state);
2318 if (!font) {
2319 return;
2322 x -= originX;
2323 y -= originY;
2325 doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
2326 doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
2327 !state->getStrokeColorSpace()->isNonMarking();
2328 doClip = render & 4;
2330 path = NULL;
2331 if (doStroke || doClip) {
2332 if ((path = font->getGlyphPath(code))) {
2333 path->offset((SplashCoord)x, (SplashCoord)y);
2337 // don't use stroke adjustment when stroking text -- the results
2338 // tend to be ugly (because characters with horizontal upper or
2339 // lower edges get misaligned relative to the other characters)
2340 strokeAdjust = gFalse; // make gcc happy
2341 if (doStroke) {
2342 strokeAdjust = splash->getStrokeAdjust();
2343 splash->setStrokeAdjust(gFalse);
2346 // fill and stroke
2347 if (doFill && doStroke) {
2348 if (path) {
2349 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2350 state->getOverprintMode(), state->getFillColor());
2351 splash->fill(path, gFalse);
2352 setOverprintMask(state->getStrokeColorSpace(),
2353 state->getStrokeOverprint(),
2354 state->getOverprintMode(),
2355 state->getStrokeColor());
2356 splash->stroke(path);
2359 // fill
2360 } else if (doFill) {
2361 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2362 state->getOverprintMode(), state->getFillColor());
2363 splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
2365 // stroke
2366 } else if (doStroke) {
2367 if (path) {
2368 setOverprintMask(state->getStrokeColorSpace(),
2369 state->getStrokeOverprint(),
2370 state->getOverprintMode(),
2371 state->getStrokeColor());
2372 splash->stroke(path);
2376 // clip
2377 if (doClip) {
2378 if (path) {
2379 if (textClipPath) {
2380 textClipPath->append(path);
2381 } else {
2382 textClipPath = path;
2383 path = NULL;
2388 if (doStroke) {
2389 splash->setStrokeAdjust(strokeAdjust);
2392 if (path) {
2393 delete path;
2397 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
2398 double dx, double dy,
2399 CharCode code, Unicode *u, int uLen) {
2400 GfxFont *gfxFont;
2401 Ref *fontID;
2402 double *ctm, *bbox;
2403 T3FontCache *t3Font;
2404 T3GlyphStack *t3gs;
2405 GBool validBBox;
2406 double m[4];
2407 GBool horiz;
2408 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2409 int i, j;
2411 if (skipHorizText || skipRotatedText) {
2412 state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2413 horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2414 fabs(m[2]) < 0.001 && m[3] < 0;
2415 if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2416 return gTrue;
2420 if (!(gfxFont = state->getFont())) {
2421 return gFalse;
2423 fontID = gfxFont->getID();
2424 ctm = state->getCTM();
2425 state->transform(0, 0, &xt, &yt);
2427 // is it the first (MRU) font in the cache?
2428 if (!(nT3Fonts > 0 &&
2429 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2431 // is the font elsewhere in the cache?
2432 for (i = 1; i < nT3Fonts; ++i) {
2433 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2434 t3Font = t3FontCache[i];
2435 for (j = i; j > 0; --j) {
2436 t3FontCache[j] = t3FontCache[j - 1];
2438 t3FontCache[0] = t3Font;
2439 break;
2442 if (i >= nT3Fonts) {
2444 // create new entry in the font cache
2445 if (nT3Fonts == splashOutT3FontCacheSize) {
2446 t3gs = t3GlyphStack;
2447 while (t3gs != NULL) {
2448 if (t3gs->cache == t3FontCache[nT3Fonts - 1]) {
2449 error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char");
2450 return gTrue;
2452 t3gs = t3gs->next;
2454 delete t3FontCache[nT3Fonts - 1];
2455 --nT3Fonts;
2457 for (j = nT3Fonts; j > 0; --j) {
2458 t3FontCache[j] = t3FontCache[j - 1];
2460 ++nT3Fonts;
2461 bbox = gfxFont->getFontBBox();
2462 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2463 // unspecified bounding box -- just take a guess
2464 xMin = xt - 5;
2465 xMax = xMin + 30;
2466 yMax = yt + 15;
2467 yMin = yMax - 45;
2468 validBBox = gFalse;
2469 } else {
2470 state->transform(bbox[0], bbox[1], &x1, &y1);
2471 xMin = xMax = x1;
2472 yMin = yMax = y1;
2473 state->transform(bbox[0], bbox[3], &x1, &y1);
2474 if (x1 < xMin) {
2475 xMin = x1;
2476 } else if (x1 > xMax) {
2477 xMax = x1;
2479 if (y1 < yMin) {
2480 yMin = y1;
2481 } else if (y1 > yMax) {
2482 yMax = y1;
2484 state->transform(bbox[2], bbox[1], &x1, &y1);
2485 if (x1 < xMin) {
2486 xMin = x1;
2487 } else if (x1 > xMax) {
2488 xMax = x1;
2490 if (y1 < yMin) {
2491 yMin = y1;
2492 } else if (y1 > yMax) {
2493 yMax = y1;
2495 state->transform(bbox[2], bbox[3], &x1, &y1);
2496 if (x1 < xMin) {
2497 xMin = x1;
2498 } else if (x1 > xMax) {
2499 xMax = x1;
2501 if (y1 < yMin) {
2502 yMin = y1;
2503 } else if (y1 > yMax) {
2504 yMax = y1;
2506 validBBox = gTrue;
2508 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2509 (int)floor(xMin - xt) - 2,
2510 (int)floor(yMin - yt) - 2,
2511 (int)ceil(xMax) - (int)floor(xMin) + 4,
2512 (int)ceil(yMax) - (int)floor(yMin) + 4,
2513 validBBox,
2514 colorMode != splashModeMono1);
2517 t3Font = t3FontCache[0];
2519 // is the glyph in the cache?
2520 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2521 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2522 if (t3Font->cacheTags != NULL) {
2523 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2524 t3Font->cacheTags[i+j].code == code) {
2525 drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2526 t3Font->cacheData + (i+j) * t3Font->glyphSize);
2527 return gTrue;
2532 // push a new Type 3 glyph record
2533 t3gs = new T3GlyphStack();
2534 t3gs->next = t3GlyphStack;
2535 t3GlyphStack = t3gs;
2536 t3GlyphStack->code = code;
2537 t3GlyphStack->cache = t3Font;
2538 t3GlyphStack->cacheTag = NULL;
2539 t3GlyphStack->cacheData = NULL;
2541 haveT3Dx = gFalse;
2543 return gFalse;
2546 void SplashOutputDev::endType3Char(GfxState *state) {
2547 T3GlyphStack *t3gs;
2548 double *ctm;
2550 if (t3GlyphStack->cacheTag) {
2551 --nestCount;
2552 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2553 t3GlyphStack->cache->glyphSize);
2554 delete bitmap;
2555 delete splash;
2556 bitmap = t3GlyphStack->origBitmap;
2557 splash = t3GlyphStack->origSplash;
2558 ctm = state->getCTM();
2559 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2560 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2561 updateCTM(state, 0, 0, 0, 0, 0, 0);
2562 drawType3Glyph(state, t3GlyphStack->cache,
2563 t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2565 t3gs = t3GlyphStack;
2566 t3GlyphStack = t3gs->next;
2567 delete t3gs;
2570 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2571 haveT3Dx = gTrue;
2574 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2575 double llx, double lly, double urx, double ury) {
2576 double *ctm;
2577 T3FontCache *t3Font;
2578 SplashColor color;
2579 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2580 int i, j;
2582 // ignore multiple d0/d1 operators
2583 if (haveT3Dx) {
2584 return;
2586 haveT3Dx = gTrue;
2588 if (unlikely(t3GlyphStack == NULL)) {
2589 error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1");
2590 return;
2593 if (unlikely(t3GlyphStack->origBitmap != NULL)) {
2594 error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1");
2595 return;
2598 if (unlikely(t3GlyphStack->origSplash != NULL)) {
2599 error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1");
2600 return;
2603 t3Font = t3GlyphStack->cache;
2605 // check for a valid bbox
2606 state->transform(0, 0, &xt, &yt);
2607 state->transform(llx, lly, &x1, &y1);
2608 xMin = xMax = x1;
2609 yMin = yMax = y1;
2610 state->transform(llx, ury, &x1, &y1);
2611 if (x1 < xMin) {
2612 xMin = x1;
2613 } else if (x1 > xMax) {
2614 xMax = x1;
2616 if (y1 < yMin) {
2617 yMin = y1;
2618 } else if (y1 > yMax) {
2619 yMax = y1;
2621 state->transform(urx, lly, &x1, &y1);
2622 if (x1 < xMin) {
2623 xMin = x1;
2624 } else if (x1 > xMax) {
2625 xMax = x1;
2627 if (y1 < yMin) {
2628 yMin = y1;
2629 } else if (y1 > yMax) {
2630 yMax = y1;
2632 state->transform(urx, ury, &x1, &y1);
2633 if (x1 < xMin) {
2634 xMin = x1;
2635 } else if (x1 > xMax) {
2636 xMax = x1;
2638 if (y1 < yMin) {
2639 yMin = y1;
2640 } else if (y1 > yMax) {
2641 yMax = y1;
2643 if (xMin - xt < t3Font->glyphX ||
2644 yMin - yt < t3Font->glyphY ||
2645 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2646 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2647 if (t3Font->validBBox) {
2648 error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2650 return;
2653 if (t3Font->cacheTags == NULL)
2654 return;
2656 // allocate a cache entry
2657 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2658 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2659 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2660 t3Font->cacheTags[i+j].mru = 0x8000;
2661 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2662 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2663 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2664 } else {
2665 ++t3Font->cacheTags[i+j].mru;
2669 // save state
2670 t3GlyphStack->origBitmap = bitmap;
2671 t3GlyphStack->origSplash = splash;
2672 ctm = state->getCTM();
2673 t3GlyphStack->origCTM4 = ctm[4];
2674 t3GlyphStack->origCTM5 = ctm[5];
2676 // create the temporary bitmap
2677 if (colorMode == splashModeMono1) {
2678 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2679 splashModeMono1, gFalse);
2680 splash = new Splash(bitmap, gFalse,
2681 t3GlyphStack->origSplash->getScreen());
2682 color[0] = 0;
2683 splash->clear(color);
2684 color[0] = 0xff;
2685 } else {
2686 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2687 splashModeMono8, gFalse);
2688 splash = new Splash(bitmap, vectorAntialias,
2689 t3GlyphStack->origSplash->getScreen());
2690 color[0] = 0x00;
2691 splash->clear(color);
2692 color[0] = 0xff;
2694 splash->setMinLineWidth(globalParams->getMinLineWidth());
2695 splash->setThinLineMode(splashThinLineDefault);
2696 splash->setFillPattern(new SplashSolidColor(color));
2697 splash->setStrokePattern(new SplashSolidColor(color));
2698 //~ this should copy other state from t3GlyphStack->origSplash?
2699 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2700 -t3Font->glyphX, -t3Font->glyphY);
2701 updateCTM(state, 0, 0, 0, 0, 0, 0);
2702 ++nestCount;
2705 void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2706 T3FontCacheTag * /*tag*/, Guchar *data) {
2707 SplashGlyphBitmap glyph;
2709 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2710 state->getOverprintMode(), state->getFillColor());
2711 glyph.x = -t3Font->glyphX;
2712 glyph.y = -t3Font->glyphY;
2713 glyph.w = t3Font->glyphW;
2714 glyph.h = t3Font->glyphH;
2715 glyph.aa = colorMode != splashModeMono1;
2716 glyph.data = data;
2717 glyph.freeData = gFalse;
2718 splash->fillGlyph(0, 0, &glyph);
2721 void SplashOutputDev::beginTextObject(GfxState *state) {
2724 void SplashOutputDev::endTextObject(GfxState *state) {
2725 if (textClipPath) {
2726 splash->clipToPath(textClipPath, gFalse);
2727 delete textClipPath;
2728 textClipPath = NULL;
2732 struct SplashOutImageMaskData {
2733 ImageStream *imgStr;
2734 GBool invert;
2735 int width, height, y;
2738 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
2739 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2740 Guchar *p;
2741 SplashColorPtr q;
2742 int x;
2744 if (imgMaskData->y == imgMaskData->height) {
2745 return gFalse;
2747 if (!(p = imgMaskData->imgStr->getLine())) {
2748 return gFalse;
2750 for (x = 0, q = line; x < imgMaskData->width; ++x) {
2751 *q++ = *p++ ^ imgMaskData->invert;
2753 ++imgMaskData->y;
2754 return gTrue;
2757 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2758 int width, int height, GBool invert,
2759 GBool interpolate, GBool inlineImg) {
2760 double *ctm;
2761 SplashCoord mat[6];
2762 SplashOutImageMaskData imgMaskData;
2764 if (state->getFillColorSpace()->isNonMarking()) {
2765 return;
2767 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2768 state->getOverprintMode(), state->getFillColor());
2770 ctm = state->getCTM();
2771 for (int i = 0; i < 6; ++i) {
2772 if (!isfinite(ctm[i])) return;
2774 mat[0] = ctm[0];
2775 mat[1] = ctm[1];
2776 mat[2] = -ctm[2];
2777 mat[3] = -ctm[3];
2778 mat[4] = ctm[2] + ctm[4];
2779 mat[5] = ctm[3] + ctm[5];
2781 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2782 imgMaskData.imgStr->reset();
2783 imgMaskData.invert = invert ? 0 : 1;
2784 imgMaskData.width = width;
2785 imgMaskData.height = height;
2786 imgMaskData.y = 0;
2788 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2789 if (inlineImg) {
2790 while (imgMaskData.y < height) {
2791 imgMaskData.imgStr->getLine();
2792 ++imgMaskData.y;
2796 delete imgMaskData.imgStr;
2797 str->close();
2800 void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2801 Object *ref, Stream *str,
2802 int width, int height,
2803 GBool invert,
2804 GBool inlineImg, double *baseMatrix) {
2805 double *ctm;
2806 SplashCoord mat[6];
2807 SplashOutImageMaskData imgMaskData;
2808 Splash *maskSplash;
2809 SplashColor maskColor;
2810 double bbox[4] = {0, 0, 1, 1}; // default;
2812 if (state->getFillColorSpace()->isNonMarking()) {
2813 return;
2816 ctm = state->getCTM();
2817 for (int i = 0; i < 6; ++i) {
2818 if (!isfinite(ctm[i])) return;
2821 beginTransparencyGroup(state, bbox, NULL, gFalse, gFalse, gFalse);
2822 baseMatrix[4] -= transpGroupStack->tx;
2823 baseMatrix[5] -= transpGroupStack->ty;
2825 ctm = state->getCTM();
2826 mat[0] = ctm[0];
2827 mat[1] = ctm[1];
2828 mat[2] = -ctm[2];
2829 mat[3] = -ctm[3];
2830 mat[4] = ctm[2] + ctm[4];
2831 mat[5] = ctm[3] + ctm[5];
2832 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2833 imgMaskData.imgStr->reset();
2834 imgMaskData.invert = invert ? 0 : 1;
2835 imgMaskData.width = width;
2836 imgMaskData.height = height;
2837 imgMaskData.y = 0;
2839 transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
2840 maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias);
2841 maskColor[0] = 0;
2842 maskSplash->clear(maskColor);
2843 maskColor[0] = 0xff;
2844 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2845 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2846 delete maskSplash;
2847 delete imgMaskData.imgStr;
2848 str->close();
2851 void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {
2852 double bbox[4] = {0,0,1,1}; // dummy
2854 /* transfer mask to alpha channel! */
2855 // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
2856 // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
2857 if (transpGroupStack->softmask != NULL) {
2858 Guchar *dest = bitmap->getAlphaPtr();
2859 Guchar *src = transpGroupStack->softmask->getDataPtr();
2860 for (int c= 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) {
2861 dest[c] = src[c];
2863 delete transpGroupStack->softmask;
2864 transpGroupStack->softmask = NULL;
2866 endTransparencyGroup(state);
2867 baseMatrix[4] += transpGroupStack->tx;
2868 baseMatrix[5] += transpGroupStack->ty;
2869 paintTransparencyGroup(state, bbox);
2872 struct SplashOutImageData {
2873 ImageStream *imgStr;
2874 GfxImageColorMap *colorMap;
2875 SplashColorPtr lookup;
2876 int *maskColors;
2877 SplashColorMode colorMode;
2878 int width, height, y;
2881 #ifdef USE_CMS
2882 GBool SplashOutputDev::useIccImageSrc(void *data) {
2883 SplashOutImageData *imgData = (SplashOutImageData *)data;
2885 if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased) {
2886 GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *) imgData->colorMap->getColorSpace();
2887 switch (imgData->colorMode) {
2888 case splashModeMono1:
2889 case splashModeMono8:
2890 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceGray)
2891 return gTrue;
2892 break;
2893 case splashModeXBGR8:
2894 case splashModeRGB8:
2895 case splashModeBGR8:
2896 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceRGB)
2897 return gTrue;
2898 break;
2899 #if SPLASH_CMYK
2900 case splashModeCMYK8:
2901 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceCMYK)
2902 return gTrue;
2903 break;
2904 #endif
2908 return gFalse;
2910 #endif
2912 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2913 Guchar * /*alphaLine*/) {
2914 SplashOutImageData *imgData = (SplashOutImageData *)data;
2915 Guchar *p;
2916 SplashColorPtr q, col;
2917 GfxRGB rgb;
2918 GfxGray gray;
2919 #if SPLASH_CMYK
2920 GfxCMYK cmyk;
2921 GfxColor deviceN;
2922 #endif
2923 int nComps, x;
2925 if (imgData->y == imgData->height) {
2926 return gFalse;
2928 if (!(p = imgData->imgStr->getLine())) {
2929 int destComps = 1;
2930 if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8)
2931 destComps = 3;
2932 else if (imgData->colorMode == splashModeXBGR8)
2933 destComps = 4;
2934 #if SPLASH_CMYK
2935 else if (imgData->colorMode == splashModeCMYK8)
2936 destComps = 4;
2937 else if (imgData->colorMode == splashModeDeviceN8)
2938 destComps = SPOT_NCOMPS + 4;
2939 #endif
2940 memset(colorLine, 0, imgData->width * destComps);
2941 return gFalse;
2944 nComps = imgData->colorMap->getNumPixelComps();
2946 if (imgData->lookup) {
2947 switch (imgData->colorMode) {
2948 case splashModeMono1:
2949 case splashModeMono8:
2950 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2951 *q++ = imgData->lookup[*p];
2953 break;
2954 case splashModeRGB8:
2955 case splashModeBGR8:
2956 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2957 col = &imgData->lookup[3 * *p];
2958 *q++ = col[0];
2959 *q++ = col[1];
2960 *q++ = col[2];
2962 break;
2963 case splashModeXBGR8:
2964 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2965 col = &imgData->lookup[4 * *p];
2966 *q++ = col[0];
2967 *q++ = col[1];
2968 *q++ = col[2];
2969 *q++ = col[3];
2971 break;
2972 #if SPLASH_CMYK
2973 case splashModeCMYK8:
2974 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2975 col = &imgData->lookup[4 * *p];
2976 *q++ = col[0];
2977 *q++ = col[1];
2978 *q++ = col[2];
2979 *q++ = col[3];
2981 break;
2982 case splashModeDeviceN8:
2983 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2984 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
2985 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
2986 *q++ = col[cp];
2988 break;
2989 #endif
2991 } else {
2992 switch (imgData->colorMode) {
2993 case splashModeMono1:
2994 case splashModeMono8:
2995 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
2996 imgData->colorMap->getGray(p, &gray);
2997 *q++ = colToByte(gray);
2999 break;
3000 case splashModeRGB8:
3001 case splashModeBGR8:
3002 if (imgData->colorMap->useRGBLine()) {
3003 imgData->colorMap->getRGBLine(p, (Guchar *) colorLine, imgData->width);
3004 } else {
3005 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3006 imgData->colorMap->getRGB(p, &rgb);
3007 *q++ = colToByte(rgb.r);
3008 *q++ = colToByte(rgb.g);
3009 *q++ = colToByte(rgb.b);
3012 break;
3013 case splashModeXBGR8:
3014 if (imgData->colorMap->useRGBLine()) {
3015 imgData->colorMap->getRGBXLine(p, (Guchar *) colorLine, imgData->width);
3016 } else {
3017 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3018 imgData->colorMap->getRGB(p, &rgb);
3019 *q++ = colToByte(rgb.r);
3020 *q++ = colToByte(rgb.g);
3021 *q++ = colToByte(rgb.b);
3022 *q++ = 255;
3025 break;
3026 #if SPLASH_CMYK
3027 case splashModeCMYK8:
3028 if (imgData->colorMap->useCMYKLine()) {
3029 imgData->colorMap->getCMYKLine(p, (Guchar *) colorLine, imgData->width);
3030 } else {
3031 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3032 imgData->colorMap->getCMYK(p, &cmyk);
3033 *q++ = colToByte(cmyk.c);
3034 *q++ = colToByte(cmyk.m);
3035 *q++ = colToByte(cmyk.y);
3036 *q++ = colToByte(cmyk.k);
3039 break;
3040 case splashModeDeviceN8:
3041 if (imgData->colorMap->useDeviceNLine()) {
3042 imgData->colorMap->getDeviceNLine(p, (Guchar *) colorLine, imgData->width);
3043 } else {
3044 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3045 imgData->colorMap->getDeviceN(p, &deviceN);
3046 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3047 *q++ = colToByte(deviceN.c[cp]);
3050 break;
3051 #endif
3055 ++imgData->y;
3056 return gTrue;
3059 #ifdef USE_CMS
3060 GBool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine,
3061 Guchar * /*alphaLine*/) {
3062 SplashOutImageData *imgData = (SplashOutImageData *)data;
3063 Guchar *p;
3064 int nComps;
3066 if (imgData->y == imgData->height) {
3067 return gFalse;
3069 if (!(p = imgData->imgStr->getLine())) {
3070 int destComps = 1;
3071 if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8)
3072 destComps = 3;
3073 else if (imgData->colorMode == splashModeXBGR8)
3074 destComps = 4;
3075 #if SPLASH_CMYK
3076 else if (imgData->colorMode == splashModeCMYK8)
3077 destComps = 4;
3078 else if (imgData->colorMode == splashModeDeviceN8)
3079 destComps = SPOT_NCOMPS + 4;
3080 #endif
3081 memset(colorLine, 0, imgData->width * destComps);
3082 return gFalse;
3085 if (imgData->colorMode == splashModeXBGR8) {
3086 SplashColorPtr q;
3087 int x;
3088 for (x = 0, q = colorLine; x < imgData->width; ++x) {
3089 *q++ = *p++;
3090 *q++ = *p++;
3091 *q++ = *p++;
3092 *q++ = 255;
3094 } else {
3095 nComps = imgData->colorMap->getNumPixelComps();
3096 memcpy(colorLine, p, imgData->width * nComps);
3099 ++imgData->y;
3100 return gTrue;
3103 void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) {
3104 SplashOutImageData *imgData = (SplashOutImageData *)data;
3105 int nComps = imgData->colorMap->getNumPixelComps();
3107 Guchar *colorLine = (Guchar *) gmalloc(nComps * bitmap->getWidth());
3108 Guchar *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (Guchar *) gmalloc(3 * bitmap->getWidth()) : NULL;
3109 for (int i = 0; i < bitmap->getHeight(); i++) {
3110 Guchar *p = bitmap->getDataPtr() + i * bitmap->getRowSize();
3111 switch (imgData->colorMode) {
3112 case splashModeMono1:
3113 case splashModeMono8:
3114 imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth());
3115 memcpy(p, colorLine, nComps * bitmap->getWidth());
3116 break;
3117 case splashModeRGB8:
3118 case splashModeBGR8:
3119 imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth());
3120 memcpy(p, colorLine, nComps * bitmap->getWidth());
3121 break;
3122 #if SPLASH_CMYK
3123 case splashModeCMYK8:
3124 imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth());
3125 memcpy(p, colorLine, nComps * bitmap->getWidth());
3126 break;
3127 #endif
3128 case splashModeXBGR8:
3129 Guchar *q;
3130 Guchar *b = p;
3131 int x;
3132 for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, ++b) {
3133 *q++ = *b++;
3134 *q++ = *b++;
3135 *q++ = *b++;
3137 imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth());
3138 b = p;
3139 for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, ++b) {
3140 *b++ = *q++;
3141 *b++ = *q++;
3142 *b++ = *q++;
3144 break;
3147 gfree(colorLine);
3148 if (rgbxLine != NULL)
3149 gfree(rgbxLine);
3151 #endif
3153 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
3154 Guchar *alphaLine) {
3155 SplashOutImageData *imgData = (SplashOutImageData *)data;
3156 Guchar *p, *aq;
3157 SplashColorPtr q, col;
3158 GfxRGB rgb;
3159 GfxGray gray;
3160 #if SPLASH_CMYK
3161 GfxCMYK cmyk;
3162 GfxColor deviceN;
3163 #endif
3164 Guchar alpha;
3165 int nComps, x, i;
3167 if (imgData->y == imgData->height) {
3168 return gFalse;
3170 if (!(p = imgData->imgStr->getLine())) {
3171 return gFalse;
3174 nComps = imgData->colorMap->getNumPixelComps();
3176 for (x = 0, q = colorLine, aq = alphaLine;
3177 x < imgData->width;
3178 ++x, p += nComps) {
3179 alpha = 0;
3180 for (i = 0; i < nComps; ++i) {
3181 if (p[i] < imgData->maskColors[2*i] ||
3182 p[i] > imgData->maskColors[2*i+1]) {
3183 alpha = 0xff;
3184 break;
3187 if (imgData->lookup) {
3188 switch (imgData->colorMode) {
3189 case splashModeMono1:
3190 case splashModeMono8:
3191 *q++ = imgData->lookup[*p];
3192 break;
3193 case splashModeRGB8:
3194 case splashModeBGR8:
3195 col = &imgData->lookup[3 * *p];
3196 *q++ = col[0];
3197 *q++ = col[1];
3198 *q++ = col[2];
3199 break;
3200 case splashModeXBGR8:
3201 col = &imgData->lookup[4 * *p];
3202 *q++ = col[0];
3203 *q++ = col[1];
3204 *q++ = col[2];
3205 *q++ = 255;
3206 break;
3207 #if SPLASH_CMYK
3208 case splashModeCMYK8:
3209 col = &imgData->lookup[4 * *p];
3210 *q++ = col[0];
3211 *q++ = col[1];
3212 *q++ = col[2];
3213 *q++ = col[3];
3214 break;
3215 case splashModeDeviceN8:
3216 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
3217 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3218 *q++ = col[cp];
3219 break;
3220 #endif
3222 *aq++ = alpha;
3223 } else {
3224 switch (imgData->colorMode) {
3225 case splashModeMono1:
3226 case splashModeMono8:
3227 imgData->colorMap->getGray(p, &gray);
3228 *q++ = colToByte(gray);
3229 break;
3230 case splashModeXBGR8:
3231 case splashModeRGB8:
3232 case splashModeBGR8:
3233 imgData->colorMap->getRGB(p, &rgb);
3234 *q++ = colToByte(rgb.r);
3235 *q++ = colToByte(rgb.g);
3236 *q++ = colToByte(rgb.b);
3237 if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
3238 break;
3239 #if SPLASH_CMYK
3240 case splashModeCMYK8:
3241 imgData->colorMap->getCMYK(p, &cmyk);
3242 *q++ = colToByte(cmyk.c);
3243 *q++ = colToByte(cmyk.m);
3244 *q++ = colToByte(cmyk.y);
3245 *q++ = colToByte(cmyk.k);
3246 break;
3247 case splashModeDeviceN8:
3248 imgData->colorMap->getDeviceN(p, &deviceN);
3249 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3250 *q++ = colToByte(deviceN.c[cp]);
3251 break;
3252 #endif
3254 *aq++ = alpha;
3258 ++imgData->y;
3259 return gTrue;
3262 struct TilingSplashOutBitmap {
3263 SplashBitmap *bitmap;
3264 SplashPattern *pattern;
3265 SplashColorMode colorMode;
3266 int paintType;
3267 int repeatX;
3268 int repeatY;
3269 int y;
3272 GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
3273 Guchar *alphaLine) {
3274 TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data;
3276 if (imgData->y == imgData->bitmap->getHeight()) {
3277 imgData->repeatY--;
3278 if (imgData->repeatY == 0)
3279 return gFalse;
3280 imgData->y = 0;
3283 if (imgData->paintType == 1) {
3284 const SplashColorMode cMode = imgData->bitmap->getMode();
3285 SplashColorPtr q = colorLine;
3286 // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel
3287 // for the others we can use raw access
3288 if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) {
3289 for (int m = 0; m < imgData->repeatX; m++) {
3290 for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
3291 imgData->bitmap->getPixel(x, imgData->y, q);
3292 q += splashColorModeNComps[cMode];
3295 } else {
3296 const int n = imgData->bitmap->getRowSize();
3297 SplashColorPtr p;
3298 for (int m = 0; m < imgData->repeatX; m++) {
3299 p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize();
3300 for (int x = 0; x < n; ++x) {
3301 *q++ = *p++;
3305 if (alphaLine != NULL) {
3306 SplashColorPtr aq = alphaLine;
3307 SplashColorPtr p;
3308 const int n = imgData->bitmap->getWidth() - 1;
3309 for (int m = 0; m < imgData->repeatX; m++) {
3310 p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth();
3311 for (int x = 0; x < n; ++x) {
3312 *aq++ = *p++;
3314 // This is a hack, because of how Splash antialias works if we overwrite the
3315 // last alpha pixel of the tile most/all of the files look much better
3316 *aq++ = (n == 0) ? *p : *(p - 1);
3319 } else {
3320 SplashColor col, pat;
3321 SplashColorPtr dest = colorLine;
3322 for (int m = 0; m < imgData->repeatX; m++) {
3323 for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
3324 imgData->bitmap->getPixel(x, imgData->y, col);
3325 imgData->pattern->getColor(x, imgData->y, pat);
3326 for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) {
3327 #if SPLASH_CMYK
3328 if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8)
3329 dest[i] = div255(pat[i] * (255 - col[0]));
3330 else
3331 #endif
3332 dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0]));
3334 dest += splashColorModeNComps[imgData->colorMode];
3337 if (alphaLine != NULL) {
3338 const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y;
3339 SplashColorPtr aq = alphaLine;
3340 SplashColorPtr p;
3341 const int n = imgData->bitmap->getWidth();
3342 for (int m = 0; m < imgData->repeatX; m++) {
3343 p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth();
3344 for (int x = 0; x < n; ++x) {
3345 *aq++ = *p++;
3350 ++imgData->y;
3351 return gTrue;
3354 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3355 int width, int height,
3356 GfxImageColorMap *colorMap,
3357 GBool interpolate,
3358 int *maskColors, GBool inlineImg) {
3359 double *ctm;
3360 SplashCoord mat[6];
3361 SplashOutImageData imgData;
3362 SplashColorMode srcMode;
3363 SplashImageSource src;
3364 SplashICCTransform tf;
3365 GfxGray gray;
3366 GfxRGB rgb;
3367 #if SPLASH_CMYK
3368 GfxCMYK cmyk;
3369 GBool grayIndexed = gFalse;
3370 GfxColor deviceN;
3371 #endif
3372 Guchar pix;
3373 int n, i;
3375 ctm = state->getCTM();
3376 for (i = 0; i < 6; ++i) {
3377 if (!isfinite(ctm[i])) return;
3379 mat[0] = ctm[0];
3380 mat[1] = ctm[1];
3381 mat[2] = -ctm[2];
3382 mat[3] = -ctm[3];
3383 mat[4] = ctm[2] + ctm[4];
3384 mat[5] = ctm[3] + ctm[5];
3386 imgData.imgStr = new ImageStream(str, width,
3387 colorMap->getNumPixelComps(),
3388 colorMap->getBits());
3389 imgData.imgStr->reset();
3390 imgData.colorMap = colorMap;
3391 imgData.maskColors = maskColors;
3392 imgData.colorMode = colorMode;
3393 imgData.width = width;
3394 imgData.height = height;
3395 imgData.y = 0;
3397 // special case for one-channel (monochrome/gray/separation) images:
3398 // build a lookup table here
3399 imgData.lookup = NULL;
3400 if (colorMap->getNumPixelComps() == 1) {
3401 n = 1 << colorMap->getBits();
3402 switch (colorMode) {
3403 case splashModeMono1:
3404 case splashModeMono8:
3405 imgData.lookup = (SplashColorPtr)gmalloc(n);
3406 for (i = 0; i < n; ++i) {
3407 pix = (Guchar)i;
3408 colorMap->getGray(&pix, &gray);
3409 imgData.lookup[i] = colToByte(gray);
3411 break;
3412 case splashModeRGB8:
3413 case splashModeBGR8:
3414 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3415 for (i = 0; i < n; ++i) {
3416 pix = (Guchar)i;
3417 colorMap->getRGB(&pix, &rgb);
3418 imgData.lookup[3*i] = colToByte(rgb.r);
3419 imgData.lookup[3*i+1] = colToByte(rgb.g);
3420 imgData.lookup[3*i+2] = colToByte(rgb.b);
3422 break;
3423 case splashModeXBGR8:
3424 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3425 for (i = 0; i < n; ++i) {
3426 pix = (Guchar)i;
3427 colorMap->getRGB(&pix, &rgb);
3428 imgData.lookup[4*i] = colToByte(rgb.r);
3429 imgData.lookup[4*i+1] = colToByte(rgb.g);
3430 imgData.lookup[4*i+2] = colToByte(rgb.b);
3431 imgData.lookup[4*i+3] = 255;
3433 break;
3434 #if SPLASH_CMYK
3435 case splashModeCMYK8:
3436 grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
3437 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3438 for (i = 0; i < n; ++i) {
3439 pix = (Guchar)i;
3440 colorMap->getCMYK(&pix, &cmyk);
3441 if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
3442 grayIndexed = gFalse;
3444 imgData.lookup[4*i] = colToByte(cmyk.c);
3445 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3446 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3447 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3449 break;
3450 case splashModeDeviceN8:
3451 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3452 grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
3453 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3454 for (i = 0; i < n; ++i) {
3455 pix = (Guchar)i;
3456 colorMap->getCMYK(&pix, &cmyk);
3457 if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
3458 grayIndexed = gFalse;
3460 colorMap->getDeviceN(&pix, &deviceN);
3461 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3462 imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]);
3464 break;
3465 #endif
3469 #if SPLASH_CMYK
3470 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3471 state->getOverprintMode(), NULL, grayIndexed);
3472 #else
3473 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3474 state->getOverprintMode(), NULL);
3475 #endif
3477 if (colorMode == splashModeMono1) {
3478 srcMode = splashModeMono8;
3479 } else {
3480 srcMode = colorMode;
3482 #ifdef USE_CMS
3483 src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc;
3484 tf = maskColors == NULL && useIccImageSrc(&imgData) ? &iccTransform : NULL;
3485 #else
3486 src = maskColors ? &alphaImageSrc : &imageSrc;
3487 tf = NULL;
3488 #endif
3489 splash->drawImage(src, tf, &imgData, srcMode, maskColors ? gTrue : gFalse,
3490 width, height, mat, interpolate);
3491 if (inlineImg) {
3492 while (imgData.y < height) {
3493 imgData.imgStr->getLine();
3494 ++imgData.y;
3498 gfree(imgData.lookup);
3499 delete imgData.imgStr;
3500 str->close();
3503 struct SplashOutMaskedImageData {
3504 ImageStream *imgStr;
3505 GfxImageColorMap *colorMap;
3506 SplashBitmap *mask;
3507 SplashColorPtr lookup;
3508 SplashColorMode colorMode;
3509 int width, height, y;
3512 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
3513 Guchar *alphaLine) {
3514 SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
3515 Guchar *p, *aq;
3516 SplashColorPtr q, col;
3517 GfxRGB rgb;
3518 GfxGray gray;
3519 #if SPLASH_CMYK
3520 GfxCMYK cmyk;
3521 GfxColor deviceN;
3522 #endif
3523 Guchar alpha;
3524 Guchar *maskPtr;
3525 int maskBit;
3526 int nComps, x;
3528 if (imgData->y == imgData->height) {
3529 return gFalse;
3531 if (!(p = imgData->imgStr->getLine())) {
3532 return gFalse;
3535 nComps = imgData->colorMap->getNumPixelComps();
3537 maskPtr = imgData->mask->getDataPtr() +
3538 imgData->y * imgData->mask->getRowSize();
3539 maskBit = 0x80;
3540 for (x = 0, q = colorLine, aq = alphaLine;
3541 x < imgData->width;
3542 ++x, p += nComps) {
3543 alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
3544 if (!(maskBit >>= 1)) {
3545 ++maskPtr;
3546 maskBit = 0x80;
3548 if (imgData->lookup) {
3549 switch (imgData->colorMode) {
3550 case splashModeMono1:
3551 case splashModeMono8:
3552 *q++ = imgData->lookup[*p];
3553 break;
3554 case splashModeRGB8:
3555 case splashModeBGR8:
3556 col = &imgData->lookup[3 * *p];
3557 *q++ = col[0];
3558 *q++ = col[1];
3559 *q++ = col[2];
3560 break;
3561 case splashModeXBGR8:
3562 col = &imgData->lookup[4 * *p];
3563 *q++ = col[0];
3564 *q++ = col[1];
3565 *q++ = col[2];
3566 *q++ = 255;
3567 break;
3568 #if SPLASH_CMYK
3569 case splashModeCMYK8:
3570 col = &imgData->lookup[4 * *p];
3571 *q++ = col[0];
3572 *q++ = col[1];
3573 *q++ = col[2];
3574 *q++ = col[3];
3575 break;
3576 case splashModeDeviceN8:
3577 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
3578 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3579 *q++ = col[cp];
3580 break;
3581 #endif
3583 *aq++ = alpha;
3584 } else {
3585 switch (imgData->colorMode) {
3586 case splashModeMono1:
3587 case splashModeMono8:
3588 imgData->colorMap->getGray(p, &gray);
3589 *q++ = colToByte(gray);
3590 break;
3591 case splashModeXBGR8:
3592 case splashModeRGB8:
3593 case splashModeBGR8:
3594 imgData->colorMap->getRGB(p, &rgb);
3595 *q++ = colToByte(rgb.r);
3596 *q++ = colToByte(rgb.g);
3597 *q++ = colToByte(rgb.b);
3598 if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
3599 break;
3600 #if SPLASH_CMYK
3601 case splashModeCMYK8:
3602 imgData->colorMap->getCMYK(p, &cmyk);
3603 *q++ = colToByte(cmyk.c);
3604 *q++ = colToByte(cmyk.m);
3605 *q++ = colToByte(cmyk.y);
3606 *q++ = colToByte(cmyk.k);
3607 break;
3608 case splashModeDeviceN8:
3609 imgData->colorMap->getDeviceN(p, &deviceN);
3610 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3611 *q++ = colToByte(deviceN.c[cp]);
3612 break;
3613 #endif
3615 *aq++ = alpha;
3619 ++imgData->y;
3620 return gTrue;
3623 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
3624 Stream *str, int width, int height,
3625 GfxImageColorMap *colorMap,
3626 GBool interpolate,
3627 Stream *maskStr, int maskWidth,
3628 int maskHeight, GBool maskInvert,
3629 GBool maskInterpolate) {
3630 GfxImageColorMap *maskColorMap;
3631 Object maskDecode, decodeLow, decodeHigh;
3632 double *ctm;
3633 SplashCoord mat[6];
3634 SplashOutMaskedImageData imgData;
3635 SplashOutImageMaskData imgMaskData;
3636 SplashColorMode srcMode;
3637 SplashBitmap *maskBitmap;
3638 Splash *maskSplash;
3639 SplashColor maskColor;
3640 GfxGray gray;
3641 GfxRGB rgb;
3642 #if SPLASH_CMYK
3643 GfxCMYK cmyk;
3644 GfxColor deviceN;
3645 #endif
3646 Guchar pix;
3647 int n, i;
3649 #if SPLASH_CMYK
3650 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3651 #endif
3652 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3653 state->getOverprintMode(), NULL);
3655 // If the mask is higher resolution than the image, use
3656 // drawSoftMaskedImage() instead.
3657 if (maskWidth > width || maskHeight > height) {
3658 decodeLow.initInt(maskInvert ? 0 : 1);
3659 decodeHigh.initInt(maskInvert ? 1 : 0);
3660 maskDecode.initArray((xref) ? xref : doc->getXRef());
3661 maskDecode.arrayAdd(&decodeLow);
3662 maskDecode.arrayAdd(&decodeHigh);
3663 maskColorMap = new GfxImageColorMap(1, &maskDecode,
3664 new GfxDeviceGrayColorSpace());
3665 maskDecode.free();
3666 drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3667 maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
3668 delete maskColorMap;
3670 } else {
3671 //----- scale the mask image to the same size as the source image
3673 mat[0] = (SplashCoord)width;
3674 mat[1] = 0;
3675 mat[2] = 0;
3676 mat[3] = (SplashCoord)height;
3677 mat[4] = 0;
3678 mat[5] = 0;
3679 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3680 imgMaskData.imgStr->reset();
3681 imgMaskData.invert = maskInvert ? 0 : 1;
3682 imgMaskData.width = maskWidth;
3683 imgMaskData.height = maskHeight;
3684 imgMaskData.y = 0;
3685 maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
3686 maskSplash = new Splash(maskBitmap, gFalse);
3687 maskColor[0] = 0;
3688 maskSplash->clear(maskColor);
3689 maskColor[0] = 0xff;
3690 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
3691 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
3692 maskWidth, maskHeight, mat, gFalse);
3693 delete imgMaskData.imgStr;
3694 maskStr->close();
3695 delete maskSplash;
3697 //----- draw the source image
3699 ctm = state->getCTM();
3700 for (i = 0; i < 6; ++i) {
3701 if (!isfinite(ctm[i])) {
3702 delete maskBitmap;
3703 return;
3706 mat[0] = ctm[0];
3707 mat[1] = ctm[1];
3708 mat[2] = -ctm[2];
3709 mat[3] = -ctm[3];
3710 mat[4] = ctm[2] + ctm[4];
3711 mat[5] = ctm[3] + ctm[5];
3713 imgData.imgStr = new ImageStream(str, width,
3714 colorMap->getNumPixelComps(),
3715 colorMap->getBits());
3716 imgData.imgStr->reset();
3717 imgData.colorMap = colorMap;
3718 imgData.mask = maskBitmap;
3719 imgData.colorMode = colorMode;
3720 imgData.width = width;
3721 imgData.height = height;
3722 imgData.y = 0;
3724 // special case for one-channel (monochrome/gray/separation) images:
3725 // build a lookup table here
3726 imgData.lookup = NULL;
3727 if (colorMap->getNumPixelComps() == 1) {
3728 n = 1 << colorMap->getBits();
3729 switch (colorMode) {
3730 case splashModeMono1:
3731 case splashModeMono8:
3732 imgData.lookup = (SplashColorPtr)gmalloc(n);
3733 for (i = 0; i < n; ++i) {
3734 pix = (Guchar)i;
3735 colorMap->getGray(&pix, &gray);
3736 imgData.lookup[i] = colToByte(gray);
3738 break;
3739 case splashModeRGB8:
3740 case splashModeBGR8:
3741 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3742 for (i = 0; i < n; ++i) {
3743 pix = (Guchar)i;
3744 colorMap->getRGB(&pix, &rgb);
3745 imgData.lookup[3*i] = colToByte(rgb.r);
3746 imgData.lookup[3*i+1] = colToByte(rgb.g);
3747 imgData.lookup[3*i+2] = colToByte(rgb.b);
3749 break;
3750 case splashModeXBGR8:
3751 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3752 for (i = 0; i < n; ++i) {
3753 pix = (Guchar)i;
3754 colorMap->getRGB(&pix, &rgb);
3755 imgData.lookup[4*i] = colToByte(rgb.r);
3756 imgData.lookup[4*i+1] = colToByte(rgb.g);
3757 imgData.lookup[4*i+2] = colToByte(rgb.b);
3758 imgData.lookup[4*i+3] = 255;
3760 break;
3761 #if SPLASH_CMYK
3762 case splashModeCMYK8:
3763 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3764 for (i = 0; i < n; ++i) {
3765 pix = (Guchar)i;
3766 colorMap->getCMYK(&pix, &cmyk);
3767 imgData.lookup[4*i] = colToByte(cmyk.c);
3768 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3769 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3770 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3772 break;
3773 case splashModeDeviceN8:
3774 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3775 for (i = 0; i < n; ++i) {
3776 pix = (Guchar)i;
3777 colorMap->getDeviceN(&pix, &deviceN);
3778 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3779 imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
3781 break;
3782 #endif
3786 if (colorMode == splashModeMono1) {
3787 srcMode = splashModeMono8;
3788 } else {
3789 srcMode = colorMode;
3791 splash->drawImage(&maskedImageSrc, NULL, &imgData, srcMode, gTrue,
3792 width, height, mat, interpolate);
3793 delete maskBitmap;
3794 gfree(imgData.lookup);
3795 delete imgData.imgStr;
3796 str->close();
3800 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
3801 Stream *str, int width, int height,
3802 GfxImageColorMap *colorMap,
3803 GBool interpolate,
3804 Stream *maskStr,
3805 int maskWidth, int maskHeight,
3806 GfxImageColorMap *maskColorMap,
3807 GBool maskInterpolate) {
3808 double *ctm;
3809 SplashCoord mat[6];
3810 SplashOutImageData imgData;
3811 SplashOutImageData imgMaskData;
3812 SplashColorMode srcMode;
3813 SplashBitmap *maskBitmap;
3814 Splash *maskSplash;
3815 SplashColor maskColor;
3816 GfxGray gray;
3817 GfxRGB rgb;
3818 #if SPLASH_CMYK
3819 GfxCMYK cmyk;
3820 GfxColor deviceN;
3821 #endif
3822 Guchar pix;
3823 int n, i;
3825 #if SPLASH_CMYK
3826 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3827 #endif
3828 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3829 state->getOverprintMode(), NULL);
3831 ctm = state->getCTM();
3832 for (i = 0; i < 6; ++i) {
3833 if (!isfinite(ctm[i])) return;
3835 mat[0] = ctm[0];
3836 mat[1] = ctm[1];
3837 mat[2] = -ctm[2];
3838 mat[3] = -ctm[3];
3839 mat[4] = ctm[2] + ctm[4];
3840 mat[5] = ctm[3] + ctm[5];
3842 //----- set up the soft mask
3844 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
3845 maskColorMap->getNumPixelComps(),
3846 maskColorMap->getBits());
3847 imgMaskData.imgStr->reset();
3848 imgMaskData.colorMap = maskColorMap;
3849 imgMaskData.maskColors = NULL;
3850 imgMaskData.colorMode = splashModeMono8;
3851 imgMaskData.width = maskWidth;
3852 imgMaskData.height = maskHeight;
3853 imgMaskData.y = 0;
3854 n = 1 << maskColorMap->getBits();
3855 imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
3856 for (i = 0; i < n; ++i) {
3857 pix = (Guchar)i;
3858 maskColorMap->getGray(&pix, &gray);
3859 imgMaskData.lookup[i] = colToByte(gray);
3861 maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3862 1, splashModeMono8, gFalse);
3863 maskSplash = new Splash(maskBitmap, vectorAntialias);
3864 maskColor[0] = 0;
3865 maskSplash->clear(maskColor);
3866 maskSplash->drawImage(&imageSrc, NULL, &imgMaskData, splashModeMono8, gFalse,
3867 maskWidth, maskHeight, mat, maskInterpolate);
3868 delete imgMaskData.imgStr;
3869 maskStr->close();
3870 gfree(imgMaskData.lookup);
3871 delete maskSplash;
3872 splash->setSoftMask(maskBitmap);
3874 //----- draw the source image
3876 imgData.imgStr = new ImageStream(str, width,
3877 colorMap->getNumPixelComps(),
3878 colorMap->getBits());
3879 imgData.imgStr->reset();
3880 imgData.colorMap = colorMap;
3881 imgData.maskColors = NULL;
3882 imgData.colorMode = colorMode;
3883 imgData.width = width;
3884 imgData.height = height;
3885 imgData.y = 0;
3887 // special case for one-channel (monochrome/gray/separation) images:
3888 // build a lookup table here
3889 imgData.lookup = NULL;
3890 if (colorMap->getNumPixelComps() == 1) {
3891 n = 1 << colorMap->getBits();
3892 switch (colorMode) {
3893 case splashModeMono1:
3894 case splashModeMono8:
3895 imgData.lookup = (SplashColorPtr)gmalloc(n);
3896 for (i = 0; i < n; ++i) {
3897 pix = (Guchar)i;
3898 colorMap->getGray(&pix, &gray);
3899 imgData.lookup[i] = colToByte(gray);
3901 break;
3902 case splashModeRGB8:
3903 case splashModeBGR8:
3904 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3905 for (i = 0; i < n; ++i) {
3906 pix = (Guchar)i;
3907 colorMap->getRGB(&pix, &rgb);
3908 imgData.lookup[3*i] = colToByte(rgb.r);
3909 imgData.lookup[3*i+1] = colToByte(rgb.g);
3910 imgData.lookup[3*i+2] = colToByte(rgb.b);
3912 break;
3913 case splashModeXBGR8:
3914 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3915 for (i = 0; i < n; ++i) {
3916 pix = (Guchar)i;
3917 colorMap->getRGB(&pix, &rgb);
3918 imgData.lookup[4*i] = colToByte(rgb.r);
3919 imgData.lookup[4*i+1] = colToByte(rgb.g);
3920 imgData.lookup[4*i+2] = colToByte(rgb.b);
3921 imgData.lookup[4*i+3] = 255;
3923 break;
3924 #if SPLASH_CMYK
3925 case splashModeCMYK8:
3926 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3927 for (i = 0; i < n; ++i) {
3928 pix = (Guchar)i;
3929 colorMap->getCMYK(&pix, &cmyk);
3930 imgData.lookup[4*i] = colToByte(cmyk.c);
3931 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3932 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3933 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3935 break;
3936 case splashModeDeviceN8:
3937 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3938 for (i = 0; i < n; ++i) {
3939 pix = (Guchar)i;
3940 colorMap->getDeviceN(&pix, &deviceN);
3941 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3942 imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
3944 break;
3945 #endif
3949 if (colorMode == splashModeMono1) {
3950 srcMode = splashModeMono8;
3951 } else {
3952 srcMode = colorMode;
3954 splash->drawImage(&imageSrc, NULL, &imgData, srcMode, gFalse, width, height, mat, interpolate);
3955 splash->setSoftMask(NULL);
3956 gfree(imgData.lookup);
3957 delete imgData.imgStr;
3958 str->close();
3961 GBool SplashOutputDev::checkTransparencyGroup(GfxState *state, GBool knockout) {
3962 if (state->getFillOpacity() != 1 ||
3963 state->getStrokeOpacity() != 1 ||
3964 state->getAlphaIsShape() ||
3965 state->getBlendMode() != gfxBlendNormal ||
3966 splash->getSoftMask() != NULL ||
3967 knockout)
3968 return gTrue;
3969 return transpGroupStack != NULL && transpGroupStack->shape != NULL;
3972 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
3973 GfxColorSpace *blendingColorSpace,
3974 GBool isolated, GBool knockout,
3975 GBool forSoftMask) {
3976 SplashTransparencyGroup *transpGroup;
3977 SplashColor color;
3978 double xMin, yMin, xMax, yMax, x, y;
3979 int tx, ty, w, h, i;
3981 // transform the bbox
3982 state->transform(bbox[0], bbox[1], &x, &y);
3983 xMin = xMax = x;
3984 yMin = yMax = y;
3985 state->transform(bbox[0], bbox[3], &x, &y);
3986 if (x < xMin) {
3987 xMin = x;
3988 } else if (x > xMax) {
3989 xMax = x;
3991 if (y < yMin) {
3992 yMin = y;
3993 } else if (y > yMax) {
3994 yMax = y;
3996 state->transform(bbox[2], bbox[1], &x, &y);
3997 if (x < xMin) {
3998 xMin = x;
3999 } else if (x > xMax) {
4000 xMax = x;
4002 if (y < yMin) {
4003 yMin = y;
4004 } else if (y > yMax) {
4005 yMax = y;
4007 state->transform(bbox[2], bbox[3], &x, &y);
4008 if (x < xMin) {
4009 xMin = x;
4010 } else if (x > xMax) {
4011 xMax = x;
4013 if (y < yMin) {
4014 yMin = y;
4015 } else if (y > yMax) {
4016 yMax = y;
4018 tx = (int)floor(xMin);
4019 if (tx < 0) {
4020 tx = 0;
4021 } else if (tx >= bitmap->getWidth()) {
4022 tx = bitmap->getWidth() - 1;
4024 ty = (int)floor(yMin);
4025 if (ty < 0) {
4026 ty = 0;
4027 } else if (ty >= bitmap->getHeight()) {
4028 ty = bitmap->getHeight() - 1;
4030 w = (int)ceil(xMax) - tx + 1;
4031 if (tx + w > bitmap->getWidth()) {
4032 w = bitmap->getWidth() - tx;
4034 if (w < 1) {
4035 w = 1;
4037 h = (int)ceil(yMax) - ty + 1;
4038 if (ty + h > bitmap->getHeight()) {
4039 h = bitmap->getHeight() - ty;
4041 if (h < 1) {
4042 h = 1;
4045 // push a new stack entry
4046 transpGroup = new SplashTransparencyGroup();
4047 transpGroup->softmask = NULL;
4048 transpGroup->tx = tx;
4049 transpGroup->ty = ty;
4050 transpGroup->blendingColorSpace = blendingColorSpace;
4051 transpGroup->isolated = isolated;
4052 transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : NULL;
4053 transpGroup->knockout = (knockout && isolated);
4054 transpGroup->knockoutOpacity = 1.0;
4055 transpGroup->next = transpGroupStack;
4056 transpGroupStack = transpGroup;
4058 // save state
4059 transpGroup->origBitmap = bitmap;
4060 transpGroup->origSplash = splash;
4061 transpGroup->fontAA = fontEngine->getAA();
4063 //~ this handles the blendingColorSpace arg for soft masks, but
4064 //~ not yet for transparency groups
4066 // switch to the blending color space
4067 if (forSoftMask && isolated && blendingColorSpace) {
4068 if (blendingColorSpace->getMode() == csDeviceGray ||
4069 blendingColorSpace->getMode() == csCalGray ||
4070 (blendingColorSpace->getMode() == csICCBased &&
4071 blendingColorSpace->getNComps() == 1)) {
4072 colorMode = splashModeMono8;
4073 } else if (blendingColorSpace->getMode() == csDeviceRGB ||
4074 blendingColorSpace->getMode() == csCalRGB ||
4075 (blendingColorSpace->getMode() == csICCBased &&
4076 blendingColorSpace->getNComps() == 3)) {
4077 //~ does this need to use BGR8?
4078 colorMode = splashModeRGB8;
4079 #if SPLASH_CMYK
4080 } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
4081 (blendingColorSpace->getMode() == csICCBased &&
4082 blendingColorSpace->getNComps() == 4)) {
4083 colorMode = splashModeCMYK8;
4084 #endif
4088 // create the temporary bitmap
4089 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
4090 bitmapTopDown, bitmap->getSeparationList());
4091 splash = new Splash(bitmap, vectorAntialias,
4092 transpGroup->origSplash->getScreen());
4093 if (transpGroup->next != NULL && transpGroup->next->knockout) {
4094 fontEngine->setAA(gFalse);
4096 splash->setThinLineMode(transpGroup->origSplash->getThinLineMode());
4097 splash->setMinLineWidth(globalParams->getMinLineWidth());
4098 //~ Acrobat apparently copies at least the fill and stroke colors, and
4099 //~ maybe other state(?) -- but not the clipping path (and not sure
4100 //~ what else)
4101 //~ [this is likely the same situation as in type3D1()]
4102 splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy());
4103 splash->setStrokePattern(
4104 transpGroup->origSplash->getStrokePattern()->copy());
4105 if (isolated) {
4106 for (i = 0; i < splashMaxColorComps; ++i) {
4107 color[i] = 0;
4109 if (colorMode == splashModeXBGR8) color[3] = 255;
4110 splash->clear(color, 0);
4111 } else {
4112 SplashBitmap *shape = (knockout) ? transpGroup->shape :
4113 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->shape : transpGroup->origBitmap;
4114 int shapeTx = (knockout) ? tx :
4115 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->tx + tx : tx;
4116 int shapeTy = (knockout) ? ty :
4117 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->ty + ty : ty;
4118 splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
4119 splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy);
4121 transpGroup->tBitmap = bitmap;
4122 state->shiftCTMAndClip(-tx, -ty);
4123 updateCTM(state, 0, 0, 0, 0, 0, 0);
4124 ++nestCount;
4127 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
4128 // restore state
4129 --nestCount;
4130 delete splash;
4131 bitmap = transpGroupStack->origBitmap;
4132 colorMode = bitmap->getMode();
4133 splash = transpGroupStack->origSplash;
4134 state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty);
4135 updateCTM(state, 0, 0, 0, 0, 0, 0);
4138 void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
4139 SplashBitmap *tBitmap;
4140 SplashTransparencyGroup *transpGroup;
4141 GBool isolated;
4142 int tx, ty;
4144 tx = transpGroupStack->tx;
4145 ty = transpGroupStack->ty;
4146 tBitmap = transpGroupStack->tBitmap;
4147 isolated = transpGroupStack->isolated;
4149 // paint the transparency group onto the parent bitmap
4150 // - the clip path was set in the parent's state)
4151 if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
4152 SplashCoord knockoutOpacity = (transpGroupStack->next != NULL) ? transpGroupStack->next->knockoutOpacity
4153 : transpGroupStack->knockoutOpacity;
4154 splash->setOverprintMask(0xffffffff, gFalse);
4155 splash->composite(tBitmap, 0, 0, tx, ty,
4156 tBitmap->getWidth(), tBitmap->getHeight(),
4157 gFalse, !isolated, transpGroupStack->next != NULL && transpGroupStack->next->knockout, knockoutOpacity);
4158 fontEngine->setAA(transpGroupStack->fontAA);
4159 if (transpGroupStack->next != NULL && transpGroupStack->next->shape != NULL) {
4160 transpGroupStack->next->knockout = gTrue;
4164 // pop the stack
4165 transpGroup = transpGroupStack;
4166 transpGroupStack = transpGroup->next;
4167 if (transpGroupStack != NULL && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) {
4168 transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity;
4170 delete transpGroup->shape;
4171 delete transpGroup;
4173 delete tBitmap;
4176 void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
4177 GBool alpha, Function *transferFunc,
4178 GfxColor *backdropColor) {
4179 SplashBitmap *softMask, *tBitmap;
4180 Splash *tSplash;
4181 SplashTransparencyGroup *transpGroup;
4182 SplashColor color;
4183 SplashColorPtr p;
4184 GfxGray gray;
4185 GfxRGB rgb;
4186 #if SPLASH_CMYK
4187 GfxCMYK cmyk;
4188 GfxColor deviceN;
4189 #endif
4190 double lum, lum2;
4191 int tx, ty, x, y;
4193 tx = transpGroupStack->tx;
4194 ty = transpGroupStack->ty;
4195 tBitmap = transpGroupStack->tBitmap;
4197 // composite with backdrop color
4198 if (!alpha && tBitmap->getMode() != splashModeMono1) {
4199 //~ need to correctly handle the case where no blending color
4200 //~ space is given
4201 if (transpGroupStack->blendingColorSpace) {
4202 tSplash = new Splash(tBitmap, vectorAntialias,
4203 transpGroupStack->origSplash->getScreen());
4204 switch (tBitmap->getMode()) {
4205 case splashModeMono1:
4206 // transparency is not supported in mono1 mode
4207 break;
4208 case splashModeMono8:
4209 transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
4210 color[0] = colToByte(gray);
4211 tSplash->compositeBackground(color);
4212 break;
4213 case splashModeXBGR8:
4214 color[3] = 255;
4215 case splashModeRGB8:
4216 case splashModeBGR8:
4217 transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
4218 color[0] = colToByte(rgb.r);
4219 color[1] = colToByte(rgb.g);
4220 color[2] = colToByte(rgb.b);
4221 tSplash->compositeBackground(color);
4222 break;
4223 #if SPLASH_CMYK
4224 case splashModeCMYK8:
4225 transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
4226 color[0] = colToByte(cmyk.c);
4227 color[1] = colToByte(cmyk.m);
4228 color[2] = colToByte(cmyk.y);
4229 color[3] = colToByte(cmyk.k);
4230 tSplash->compositeBackground(color);
4231 break;
4232 case splashModeDeviceN8:
4233 transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN);
4234 for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
4235 color[cp] = colToByte(deviceN.c[cp]);
4236 tSplash->compositeBackground(color);
4237 break;
4238 #endif
4240 delete tSplash;
4244 softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
4245 1, splashModeMono8, gFalse);
4246 unsigned char fill = 0;
4247 if (transpGroupStack->blendingColorSpace) {
4248 transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
4249 fill = colToByte(gray);
4251 memset(softMask->getDataPtr(), fill,
4252 softMask->getRowSize() * softMask->getHeight());
4253 p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
4254 int xMax = tBitmap->getWidth();
4255 int yMax = tBitmap->getHeight();
4256 if (xMax > bitmap->getWidth() - tx) xMax = bitmap->getWidth() - tx;
4257 if (yMax > bitmap->getHeight() - ty) yMax = bitmap->getHeight() - ty;
4258 for (y = 0; y < yMax; ++y) {
4259 for (x = 0; x < xMax; ++x) {
4260 if (alpha) {
4261 if (transferFunc) {
4262 lum = tBitmap->getAlpha(x, y) / 255.0;
4263 transferFunc->transform(&lum, &lum2);
4264 p[x] = (int)(lum2 * 255.0 + 0.5);
4265 } else
4266 p[x] = tBitmap->getAlpha(x, y);
4267 } else {
4268 tBitmap->getPixel(x, y, color);
4269 // convert to luminosity
4270 switch (tBitmap->getMode()) {
4271 case splashModeMono1:
4272 case splashModeMono8:
4273 lum = color[0] / 255.0;
4274 break;
4275 case splashModeXBGR8:
4276 case splashModeRGB8:
4277 case splashModeBGR8:
4278 lum = (0.3 / 255.0) * color[0] +
4279 (0.59 / 255.0) * color[1] +
4280 (0.11 / 255.0) * color[2];
4281 break;
4282 #if SPLASH_CMYK
4283 case splashModeCMYK8:
4284 case splashModeDeviceN8:
4285 lum = (1 - color[3] / 255.0)
4286 - (0.3 / 255.0) * color[0]
4287 - (0.59 / 255.0) * color[1]
4288 - (0.11 / 255.0) * color[2];
4289 if (lum < 0) {
4290 lum = 0;
4292 break;
4293 #endif
4295 if (transferFunc) {
4296 transferFunc->transform(&lum, &lum2);
4297 } else {
4298 lum2 = lum;
4300 p[x] = (int)(lum2 * 255.0 + 0.5);
4303 p += softMask->getRowSize();
4305 splash->setSoftMask(softMask);
4307 // pop the stack
4308 transpGroup = transpGroupStack;
4309 transpGroupStack = transpGroup->next;
4310 delete transpGroup;
4312 delete tBitmap;
4315 void SplashOutputDev::clearSoftMask(GfxState *state) {
4316 splash->setSoftMask(NULL);
4319 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
4320 splashColorCopy(paperColor, paperColorA);
4323 int SplashOutputDev::getBitmapWidth() {
4324 return bitmap->getWidth();
4327 int SplashOutputDev::getBitmapHeight() {
4328 return bitmap->getHeight();
4331 SplashBitmap *SplashOutputDev::takeBitmap() {
4332 SplashBitmap *ret;
4334 ret = bitmap;
4335 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
4336 colorMode != splashModeMono1, bitmapTopDown);
4337 return ret;
4340 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
4341 int *xMax, int *yMax) {
4342 splash->getModRegion(xMin, yMin, xMax, yMax);
4345 void SplashOutputDev::clearModRegion() {
4346 splash->clearModRegion();
4349 #if 1 //~tmp: turn off anti-aliasing temporarily
4350 GBool SplashOutputDev::getVectorAntialias() {
4351 return splash->getVectorAntialias();
4354 void SplashOutputDev::setVectorAntialias(GBool vaa) {
4355 vaa = vaa && colorMode != splashModeMono1;
4356 vectorAntialias = vaa;
4357 splash->setVectorAntialias(vaa);
4359 #endif
4361 void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHintingA)
4363 enableFreeTypeHinting = enable;
4364 enableSlightHinting = enableSlightHintingA;
4367 GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str,
4368 double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
4369 double *mat, double *bbox,
4370 int x0, int y0, int x1, int y1,
4371 double xStep, double yStep)
4373 PDFRectangle box;
4374 Gfx *gfx;
4375 Splash *formerSplash = splash;
4376 SplashBitmap *formerBitmap = bitmap;
4377 double width, height;
4378 int surface_width, surface_height, result_width, result_height, i;
4379 int repeatX, repeatY;
4380 SplashCoord matc[6];
4381 Matrix m1;
4382 double *ctm, savedCTM[6];
4383 double kx, ky, sx, sy;
4384 GBool retValue = gFalse;
4386 width = bbox[2] - bbox[0];
4387 height = bbox[3] - bbox[1];
4389 if (xStep != width || yStep != height)
4390 return gFalse;
4392 // calculate offsets
4393 ctm = state->getCTM();
4394 for (i = 0; i < 6; ++i) {
4395 savedCTM[i] = ctm[i];
4397 state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4398 state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]);
4399 ctm = state->getCTM();
4400 for (i = 0; i < 6; ++i) {
4401 if (!isfinite(ctm[i])) {
4402 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4403 return gFalse;
4406 matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4];
4407 matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5];
4408 if (splashAbs(ctm[1]) > splashAbs(ctm[0])) {
4409 kx = -ctm[1];
4410 ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1];
4411 } else {
4412 kx = ctm[0];
4413 ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0];
4415 result_width = (int) ceil(fabs(kx * width * (x1 - x0)));
4416 result_height = (int) ceil(fabs(ky * height * (y1 - y0)));
4417 kx = state->getHDPI() / 72.0;
4418 ky = state->getVDPI() / 72.0;
4419 m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx;
4420 m1.m[1] = 0;
4421 m1.m[2] = 0;
4422 m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky;
4423 m1.m[4] = 0;
4424 m1.m[5] = 0;
4425 m1.transform(width, height, &kx, &ky);
4426 surface_width = (int) ceil (fabs(kx));
4427 surface_height = (int) ceil (fabs(ky));
4429 sx = (double) result_width / (surface_width * (x1 - x0));
4430 sy = (double) result_height / (surface_height * (y1 - y0));
4431 m1.m[0] *= sx;
4432 m1.m[3] *= sy;
4433 m1.transform(width, height, &kx, &ky);
4435 if(fabs(kx) < 1 && fabs(ky) < 1) {
4436 kx = std::min<double>(kx, ky);
4437 ky = 2 / kx;
4438 m1.m[0] *= ky;
4439 m1.m[3] *= ky;
4440 m1.transform(width, height, &kx, &ky);
4441 surface_width = (int) ceil (fabs(kx));
4442 surface_height = (int) ceil (fabs(ky));
4443 repeatX = x1 - x0;
4444 repeatY = y1 - y0;
4445 } else {
4446 if ((unsigned long) result_width * result_height > 0x800000L) {
4447 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4448 return gFalse;
4450 while(fabs(kx) > 16384 || fabs(ky) > 16384) {
4451 // limit pattern bitmap size
4452 m1.m[0] /= 2;
4453 m1.m[3] /= 2;
4454 m1.transform(width, height, &kx, &ky);
4456 surface_width = (int) ceil (fabs(kx));
4457 surface_height = (int) ceil (fabs(ky));
4458 // adjust repeat values to completely fill region
4459 repeatX = result_width / surface_width;
4460 repeatY = result_height / surface_height;
4461 if (surface_width * repeatX < result_width)
4462 repeatX++;
4463 if (surface_height * repeatY < result_height)
4464 repeatY++;
4465 if (x1 - x0 > repeatX)
4466 repeatX = x1 - x0;
4467 if (y1 - y0 > repeatY)
4468 repeatY = y1 - y0;
4470 // restore CTM and calculate rotate and scale with rounded matric
4471 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4472 state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4473 state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]);
4474 ctm = state->getCTM();
4475 matc[0] = ctm[0];
4476 matc[1] = ctm[1];
4477 matc[2] = ctm[2];
4478 matc[3] = ctm[3];
4480 if (surface_width == 0 || surface_height == 0) {
4481 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4482 return gFalse;
4484 m1.transform(bbox[0], bbox[1], &kx, &ky);
4485 m1.m[4] = -kx;
4486 m1.m[5] = -ky;
4488 bitmap = new SplashBitmap(surface_width, surface_height, 1,
4489 (paintType == 1) ? colorMode : splashModeMono8, gTrue);
4490 splash = new Splash(bitmap, gTrue);
4491 if (paintType == 2) {
4492 SplashColor clearColor;
4493 #if SPLASH_CMYK
4494 clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF;
4495 #else
4496 clearColor[0] = 0xFF;
4497 #endif
4498 splash->clear(clearColor, 0);
4499 } else {
4500 splash->clear(paperColor, 0);
4502 splash->setThinLineMode(formerSplash->getThinLineMode());
4503 splash->setMinLineWidth(globalParams->getMinLineWidth());
4505 box.x1 = bbox[0]; box.y1 = bbox[1];
4506 box.x2 = bbox[2]; box.y2 = bbox[3];
4507 gfx = new Gfx(doc, this, resDict, &box, NULL, NULL, NULL, gfxA->getXRef());
4508 // set pattern transformation matrix
4509 gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4510 updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4511 gfx->display(str);
4512 delete splash;
4513 splash = formerSplash;
4514 TilingSplashOutBitmap imgData;
4515 imgData.bitmap = bitmap;
4516 imgData.paintType = paintType;
4517 imgData.pattern = splash->getFillPattern();
4518 imgData.colorMode = colorMode;
4519 imgData.y = 0;
4520 imgData.repeatX = repeatX;
4521 imgData.repeatY = repeatY;
4522 SplashBitmap *tBitmap = bitmap;
4523 bitmap = formerBitmap;
4524 result_width = tBitmap->getWidth() * imgData.repeatX;
4525 result_height = tBitmap->getHeight() * imgData.repeatY;
4527 if (splashAbs(matc[1]) > splashAbs(matc[0])) {
4528 kx = -matc[1];
4529 ky = matc[2] - (matc[0] * matc[3]) / matc[1];
4530 } else {
4531 kx = matc[0];
4532 ky = matc[3] - (matc[1] * matc[2]) / matc[0];
4534 kx = result_width / (fabs(kx) + 1);
4535 ky = result_height / (fabs(ky) + 1);
4536 state->concatCTM(kx, 0, 0, ky, 0, 0);
4537 ctm = state->getCTM();
4538 matc[0] = ctm[0];
4539 matc[1] = ctm[1];
4540 matc[2] = ctm[2];
4541 matc[3] = ctm[3];
4542 GBool minorAxisZero = matc[1] == 0 && matc[2] == 0;
4543 if (matc[0] > 0 && minorAxisZero && matc[3] > 0) {
4544 // draw the tiles
4545 for (int y = 0; y < imgData.repeatY; ++y) {
4546 for (int x = 0; x < imgData.repeatX; ++x) {
4547 x0 = splashFloor(matc[4]) + x * tBitmap->getWidth();
4548 y0 = splashFloor(matc[5]) + y * tBitmap->getHeight();
4549 splash->blitImage(tBitmap, gTrue, x0, y0);
4552 retValue = gTrue;
4553 } else {
4554 retValue = splash->drawImage(&tilingBitmapSrc, NULL, &imgData, colorMode, gTrue, result_width, result_height, matc, gFalse, gTrue) == splashOk;
4556 delete tBitmap;
4557 delete gfx;
4558 return retValue;
4561 GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
4563 GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode();
4564 GBool bDirectColorTranslation = gFalse; // triggers an optimization.
4565 switch (colorMode) {
4566 case splashModeRGB8:
4567 bDirectColorTranslation = (shadingMode == csDeviceRGB);
4568 break;
4569 #if SPLASH_CMYK
4570 case splashModeCMYK8:
4571 case splashModeDeviceN8:
4572 bDirectColorTranslation = (shadingMode == csDeviceCMYK);
4573 break;
4574 #endif
4575 default:
4576 break;
4578 SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading, colorMode);
4579 // restore vector antialias because we support it here
4580 if (shading->isParameterized()) {
4581 GBool vaa = getVectorAntialias();
4582 GBool retVal = gFalse;
4583 setVectorAntialias(gTrue);
4584 retVal = splash->gouraudTriangleShadedFill(splashShading);
4585 setVectorAntialias(vaa);
4586 return retVal;
4588 delete splashShading;
4589 return gFalse;
4592 GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) {
4593 double xMin, yMin, xMax, yMax;
4594 SplashPath *path;
4595 GBool vaa = getVectorAntialias();
4596 // restore vector antialias because we support it here
4597 setVectorAntialias(gTrue);
4599 GBool retVal = gFalse;
4600 // get the clip region bbox
4601 if (pattern->getShading()->getHasBBox()) {
4602 pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax);
4603 } else {
4604 state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
4606 xMin = floor (xMin);
4607 yMin = floor (yMin);
4608 xMax = ceil (xMax);
4609 yMax = ceil (yMax);
4612 Matrix ctm, ictm;
4613 double x[4], y[4];
4614 int i;
4616 state->getCTM(&ctm);
4617 ctm.invertTo(&ictm);
4619 ictm.transform(xMin, yMin, &x[0], &y[0]);
4620 ictm.transform(xMax, yMin, &x[1], &y[1]);
4621 ictm.transform(xMin, yMax, &x[2], &y[2]);
4622 ictm.transform(xMax, yMax, &x[3], &y[3]);
4624 xMin = xMax = x[0];
4625 yMin = yMax = y[0];
4626 for (i = 1; i < 4; i++) {
4627 xMin = std::min<double>(xMin, x[i]);
4628 yMin = std::min<double>(yMin, y[i]);
4629 xMax = std::max<double>(xMax, x[i]);
4630 yMax = std::max<double>(yMax, y[i]);
4635 // fill the region
4636 state->moveTo(xMin, yMin);
4637 state->lineTo(xMax, yMin);
4638 state->lineTo(xMax, yMax);
4639 state->lineTo(xMin, yMax);
4640 state->closePath();
4641 path = convertPath(state, state->getPath(), gTrue);
4643 #if SPLASH_CMYK
4644 pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
4645 #endif
4646 setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(),
4647 state->getOverprintMode(), NULL);
4648 retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
4649 state->clearPath();
4650 setVectorAntialias(vaa);
4651 delete path;
4653 return retVal;
4656 GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
4657 SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
4658 GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4660 delete pattern;
4662 return retVal;
4665 GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) {
4666 SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading);
4667 GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4669 delete pattern;
4671 return retVal;