beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / SplashOutputDev.cc
blobac0f9ac19ff61bb306eccb6276c21f7cd7ef70c8
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-2016 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>
38 // Copyright (C) 2015 Kenji Uno <ku@digitaldolphins.jp>
40 // To see a description of the changes please see the Changelog file that
41 // came with your tarball or type make ChangeLog if you are building from git
43 //========================================================================
45 #include <config.h>
47 #ifdef USE_GCC_PRAGMAS
48 #pragma implementation
49 #endif
51 #include <string.h>
52 #include <math.h>
53 #include "goo/gfile.h"
54 #include "GlobalParams.h"
55 #include "Error.h"
56 #include "Object.h"
57 #include "Gfx.h"
58 #include "GfxFont.h"
59 #include "Page.h"
60 #include "PDFDoc.h"
61 #include "Link.h"
62 #include "FontEncodingTables.h"
63 #include "fofi/FoFiTrueType.h"
64 #include "splash/SplashBitmap.h"
65 #include "splash/SplashGlyphBitmap.h"
66 #include "splash/SplashPattern.h"
67 #include "splash/SplashScreen.h"
68 #include "splash/SplashPath.h"
69 #include "splash/SplashState.h"
70 #include "splash/SplashErrorCodes.h"
71 #include "splash/SplashFontEngine.h"
72 #include "splash/SplashFont.h"
73 #include "splash/SplashFontFile.h"
74 #include "splash/SplashFontFileID.h"
75 #include "splash/Splash.h"
76 #include "SplashOutputDev.h"
77 #include <algorithm>
79 #ifdef VMS
80 #if (__VMS_VER < 70000000)
81 extern "C" int unlink(char *filename);
82 #endif
83 #endif
85 #ifdef _MSC_VER
86 #include <float.h>
87 #define isfinite(x) _finite(x)
88 #endif
90 #ifdef __sun
91 #include <ieeefp.h>
92 #ifndef isfinite
93 #define isfinite(x) finite(x)
94 #endif
95 #endif
97 static inline void convertGfxColor(SplashColorPtr dest,
98 SplashColorMode colorMode,
99 GfxColorSpace *colorSpace,
100 GfxColor *src) {
101 SplashColor color;
102 GfxGray gray;
103 GfxRGB rgb;
104 #if SPLASH_CMYK
105 GfxCMYK cmyk;
106 GfxColor deviceN;
107 #endif
109 // make gcc happy
110 color[0] = color[1] = color[2] = 0;
111 #if SPLASH_CMYK
112 color[3] = 0;
113 #endif
114 switch (colorMode) {
115 case splashModeMono1:
116 case splashModeMono8:
117 colorSpace->getGray(src, &gray);
118 color[0] = colToByte(gray);
119 break;
120 case splashModeXBGR8:
121 color[3] = 255;
122 case splashModeBGR8:
123 case splashModeRGB8:
124 colorSpace->getRGB(src, &rgb);
125 color[0] = colToByte(rgb.r);
126 color[1] = colToByte(rgb.g);
127 color[2] = colToByte(rgb.b);
128 break;
129 #if SPLASH_CMYK
130 case splashModeCMYK8:
131 colorSpace->getCMYK(src, &cmyk);
132 color[0] = colToByte(cmyk.c);
133 color[1] = colToByte(cmyk.m);
134 color[2] = colToByte(cmyk.y);
135 color[3] = colToByte(cmyk.k);
136 break;
137 case splashModeDeviceN8:
138 colorSpace->getDeviceN(src, &deviceN);
139 for (int i = 0; i < SPOT_NCOMPS + 4; i++)
140 color[i] = colToByte(deviceN.c[i]);
141 break;
142 #endif
144 splashColorCopy(dest, color);
147 // Copy a color according to the color mode.
148 // Use convertGfxShortColor() below when the destination is a bitmap
149 // to avoid overwriting cells.
150 // Calling this in SplashGouraudPattern::getParameterizedColor() fixes bug 90570.
151 // Use convertGfxColor() above when the destination is an array of SPOT_NCOMPS+4 bytes,
152 // to ensure that everything is initialized.
154 static inline void convertGfxShortColor(SplashColorPtr dest,
155 SplashColorMode colorMode,
156 GfxColorSpace *colorSpace,
157 GfxColor *src) {
158 switch (colorMode) {
159 case splashModeMono1:
160 case splashModeMono8:
162 GfxGray gray;
163 colorSpace->getGray(src, &gray);
164 dest[0] = colToByte(gray);
166 break;
167 case splashModeXBGR8:
168 dest[3] = 255;
169 case splashModeBGR8:
170 case splashModeRGB8:
172 GfxRGB rgb;
173 colorSpace->getRGB(src, &rgb);
174 dest[0] = colToByte(rgb.r);
175 dest[1] = colToByte(rgb.g);
176 dest[2] = colToByte(rgb.b);
178 break;
179 #if SPLASH_CMYK
180 case splashModeCMYK8:
182 GfxCMYK cmyk;
183 colorSpace->getCMYK(src, &cmyk);
184 dest[0] = colToByte(cmyk.c);
185 dest[1] = colToByte(cmyk.m);
186 dest[2] = colToByte(cmyk.y);
187 dest[3] = colToByte(cmyk.k);
189 break;
190 case splashModeDeviceN8:
192 GfxColor deviceN;
193 colorSpace->getDeviceN(src, &deviceN);
194 for (int i = 0; i < SPOT_NCOMPS + 4; i++)
195 dest[i] = colToByte(deviceN.c[i]);
197 break;
198 #endif
202 //------------------------------------------------------------------------
203 // SplashGouraudPattern
204 //------------------------------------------------------------------------
205 SplashGouraudPattern::SplashGouraudPattern(GBool bDirectColorTranslationA,
206 GfxState *stateA, GfxGouraudTriangleShading *shadingA, SplashColorMode modeA) {
207 SplashColor defaultColor;
208 GfxColor srcColor;
209 state = stateA;
210 shading = shadingA;
211 mode = modeA;
212 bDirectColorTranslation = bDirectColorTranslationA;
213 shadingA->getColorSpace()->getDefaultColor(&srcColor);
214 convertGfxColor(defaultColor, mode, shadingA->getColorSpace(), &srcColor);
215 gfxMode = shadingA->getColorSpace()->getMode();
218 SplashGouraudPattern::~SplashGouraudPattern() {
221 void SplashGouraudPattern::getParameterizedColor(double colorinterp, SplashColorMode mode, SplashColorPtr dest) {
222 GfxColor src;
223 GfxColorSpace* srcColorSpace = shading->getColorSpace();
224 int colorComps = 3;
225 #if SPLASH_CMYK
226 if (mode == splashModeCMYK8)
227 colorComps=4;
228 else if (mode == splashModeDeviceN8)
229 colorComps=4 + SPOT_NCOMPS;
230 #endif
232 shading->getParameterizedColor(colorinterp, &src);
234 if (bDirectColorTranslation) {
235 for (int m = 0; m < colorComps; ++m)
236 dest[m] = colToByte(src.c[m]);
237 } else {
238 convertGfxShortColor(dest, mode, srcColorSpace, &src);
242 //------------------------------------------------------------------------
243 // SplashUnivariatePattern
244 //------------------------------------------------------------------------
246 SplashUnivariatePattern::SplashUnivariatePattern(SplashColorMode colorModeA, GfxState *stateA, GfxUnivariateShading *shadingA) {
247 Matrix ctm;
248 double xMin, yMin, xMax, yMax;
250 shading = shadingA;
251 state = stateA;
252 colorMode = colorModeA;
254 state->getCTM(&ctm);
255 ctm.invertTo(&ictm);
257 // get the function domain
258 t0 = shading->getDomain0();
259 t1 = shading->getDomain1();
260 dt = t1 - t0;
262 stateA->getUserClipBBox(&xMin, &yMin, &xMax, &yMax);
263 shadingA->setupCache(&ctm, xMin, yMin, xMax, yMax);
264 gfxMode = shadingA->getColorSpace()->getMode();
267 SplashUnivariatePattern::~SplashUnivariatePattern() {
270 GBool SplashUnivariatePattern::getColor(int x, int y, SplashColorPtr c) {
271 GfxColor gfxColor;
272 double xc, yc, t;
274 ictm.transform(x, y, &xc, &yc);
275 if (! getParameter (xc, yc, &t))
276 return gFalse;
278 shading->getColor(t, &gfxColor);
279 convertGfxColor(c, colorMode, shading->getColorSpace(), &gfxColor);
280 return gTrue;
283 GBool SplashUnivariatePattern::testPosition(int x, int y) {
284 double xc, yc, t;
286 ictm.transform(x, y, &xc, &yc);
287 if (! getParameter (xc, yc, &t))
288 return gFalse;
289 return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0);
293 //------------------------------------------------------------------------
294 // SplashRadialPattern
295 //------------------------------------------------------------------------
296 #define RADIAL_EPSILON (1. / 1024 / 1024)
298 SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA):
299 SplashUnivariatePattern(colorModeA, stateA, shadingA)
301 SplashColor defaultColor;
302 GfxColor srcColor;
304 shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr);
305 dx -= x0;
306 dy -= y0;
307 dr -= r0;
308 a = dx*dx + dy*dy - dr*dr;
309 if (fabs(a) > RADIAL_EPSILON)
310 inva = 1.0 / a;
311 shadingA->getColorSpace()->getDefaultColor(&srcColor);
312 convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
315 SplashRadialPattern::~SplashRadialPattern() {
318 GBool SplashRadialPattern::getParameter(double xs, double ys, double *t) {
319 double b, c, s0, s1;
321 // We want to solve this system of equations:
323 // 1. (x - xc(s))^2 + (y -yc(s))^2 = rc(s)^2
324 // 2. xc(s) = x0 + s * (x1 - xo)
325 // 3. yc(s) = y0 + s * (y1 - yo)
326 // 4. rc(s) = r0 + s * (r1 - ro)
328 // To simplify the system a little, we translate
329 // our coordinates to have the origin in (x0,y0)
331 xs -= x0;
332 ys -= y0;
334 // Then we have to solve the equation:
335 // A*s^2 - 2*B*s + C = 0
336 // where
337 // A = dx^2 + dy^2 - dr^2
338 // B = xs*dx + ys*dy + r0*dr
339 // C = xs^2 + ys^2 - r0^2
341 b = xs*dx + ys*dy + r0*dr;
342 c = xs*xs + ys*ys - r0*r0;
344 if (fabs(a) <= RADIAL_EPSILON) {
345 // A is 0, thus the equation simplifies to:
346 // -2*B*s + C = 0
347 // If B is 0, we can either have no solution or an indeterminate
348 // equation, thus we behave as if we had an invalid solution
349 if (fabs(b) <= RADIAL_EPSILON)
350 return gFalse;
352 s0 = s1 = 0.5 * c / b;
353 } else {
354 double d;
356 d = b*b - a*c;
357 if (d < 0)
358 return gFalse;
360 d = sqrt (d);
361 s0 = b + d;
362 s1 = b - d;
364 // If A < 0, one of the two solutions will have negative radius,
365 // thus it will be ignored. Otherwise we know that s1 <= s0
366 // (because d >=0 implies b - d <= b + d), so if both are valid it
367 // will be the true solution.
368 s0 *= inva;
369 s1 *= inva;
372 if (r0 + s0 * dr >= 0) {
373 if (0 <= s0 && s0 <= 1) {
374 *t = t0 + dt * s0;
375 return gTrue;
376 } else if (s0 < 0 && shading->getExtend0()) {
377 *t = t0;
378 return gTrue;
379 } else if (s0 > 1 && shading->getExtend1()) {
380 *t = t1;
381 return gTrue;
385 if (r0 + s1 * dr >= 0) {
386 if (0 <= s1 && s1 <= 1) {
387 *t = t0 + dt * s1;
388 return gTrue;
389 } else if (s1 < 0 && shading->getExtend0()) {
390 *t = t0;
391 return gTrue;
392 } else if (s1 > 1 && shading->getExtend1()) {
393 *t = t1;
394 return gTrue;
398 return gFalse;
401 #undef RADIAL_EPSILON
403 //------------------------------------------------------------------------
404 // SplashAxialPattern
405 //------------------------------------------------------------------------
407 SplashAxialPattern::SplashAxialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxAxialShading *shadingA):
408 SplashUnivariatePattern(colorModeA, stateA, shadingA)
410 SplashColor defaultColor;
411 GfxColor srcColor;
413 shadingA->getCoords(&x0, &y0, &x1, &y1);
414 dx = x1 - x0;
415 dy = y1 - y0;
416 mul = 1 / (dx * dx + dy * dy);
417 shadingA->getColorSpace()->getDefaultColor(&srcColor);
418 convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
421 SplashAxialPattern::~SplashAxialPattern() {
424 GBool SplashAxialPattern::getParameter(double xc, double yc, double *t) {
425 double s;
427 xc -= x0;
428 yc -= y0;
430 s = (xc * dx + yc * dy) * mul;
431 if (0 <= s && s <= 1) {
432 *t = t0 + dt * s;
433 } else if (s < 0 && shading->getExtend0()) {
434 *t = t0;
435 } else if (s > 1 && shading->getExtend1()) {
436 *t = t1;
437 } else {
438 return gFalse;
441 return gTrue;
444 //------------------------------------------------------------------------
445 // Type 3 font cache size parameters
446 #define type3FontCacheAssoc 8
447 #define type3FontCacheMaxSets 8
448 #define type3FontCacheSize (128*1024)
450 //------------------------------------------------------------------------
451 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
452 static inline Guchar div255(int x) {
453 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
456 #if SPLASH_CMYK
458 #include "GfxState_helpers.h"
460 //-------------------------------------------------------------------------
461 // helper for Blend functions (convert CMYK to RGB, do blend, convert back)
462 //-------------------------------------------------------------------------
464 // based in GfxState.cc
466 static void cmykToRGB(SplashColorPtr cmyk, SplashColor rgb) {
467 double c, m, y, k, c1, m1, y1, k1, r, g, b;
469 c = colToDbl(byteToCol(cmyk[0]));
470 m = colToDbl(byteToCol(cmyk[1]));
471 y = colToDbl(byteToCol(cmyk[2]));
472 k = colToDbl(byteToCol(cmyk[3]));
473 c1 = 1 - c;
474 m1 = 1 - m;
475 y1 = 1 - y;
476 k1 = 1 - k;
477 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
478 rgb[0] = colToByte(clip01(dblToCol(r)));
479 rgb[1] = colToByte(clip01(dblToCol(g)));
480 rgb[2] = colToByte(clip01(dblToCol(b)));
483 static void rgbToCMYK(SplashColor rgb, SplashColorPtr cmyk) {
484 GfxColorComp c, m, y, k;
486 c = clip01(gfxColorComp1 - byteToCol(rgb[0]));
487 m = clip01(gfxColorComp1 - byteToCol(rgb[1]));
488 y = clip01(gfxColorComp1 - byteToCol(rgb[2]));
489 k = c;
490 if (m < k) {
491 k = m;
493 if (y < k) {
494 k = y;
496 cmyk[0] = colToByte(c - k);
497 cmyk[1] = colToByte(m - k);
498 cmyk[2] = colToByte(y - k);
499 cmyk[3] = colToByte(k);
502 #endif
504 //------------------------------------------------------------------------
505 // Blend functions
506 //------------------------------------------------------------------------
508 static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
509 SplashColorPtr blend, SplashColorMode cm) {
510 int i;
512 #if SPLASH_CMYK
513 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
514 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
515 dest[i] = 255 - dest[i];
516 src[i] = 255 - src[i];
519 #endif
521 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
522 blend[i] = (dest[i] * src[i]) / 255;
525 #if SPLASH_CMYK
526 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
527 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
528 dest[i] = 255 - dest[i];
529 src[i] = 255 - src[i];
530 blend[i] = 255 - blend[i];
533 #endif
536 static void splashOutBlendScreen(SplashColorPtr src, SplashColorPtr dest,
537 SplashColorPtr blend, SplashColorMode cm) {
538 int i;
540 #if SPLASH_CMYK
541 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
542 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
543 dest[i] = 255 - dest[i];
544 src[i] = 255 - src[i];
547 #endif
549 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
550 blend[i] = dest[i] + src[i] - (dest[i] * src[i]) / 255;
553 #if SPLASH_CMYK
554 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
555 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
556 dest[i] = 255 - dest[i];
557 src[i] = 255 - src[i];
558 blend[i] = 255 - blend[i];
561 #endif
564 static void splashOutBlendOverlay(SplashColorPtr src, SplashColorPtr dest,
565 SplashColorPtr blend, SplashColorMode cm) {
566 int i;
568 #if SPLASH_CMYK
569 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
570 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
571 dest[i] = 255 - dest[i];
572 src[i] = 255 - src[i];
575 #endif
577 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
578 blend[i] = dest[i] < 0x80
579 ? (src[i] * 2 * dest[i]) / 255
580 : 255 - 2 * ((255 - src[i]) * (255 - dest[i])) / 255;
583 #if SPLASH_CMYK
584 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
585 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
586 dest[i] = 255 - dest[i];
587 src[i] = 255 - src[i];
588 blend[i] = 255 - blend[i];
591 #endif
594 static void splashOutBlendDarken(SplashColorPtr src, SplashColorPtr dest,
595 SplashColorPtr blend, SplashColorMode cm) {
596 int i;
598 #if SPLASH_CMYK
599 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
600 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
601 dest[i] = 255 - dest[i];
602 src[i] = 255 - src[i];
605 #endif
607 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
608 blend[i] = dest[i] < src[i] ? dest[i] : src[i];
611 #if SPLASH_CMYK
612 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
613 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
614 dest[i] = 255 - dest[i];
615 src[i] = 255 - src[i];
616 blend[i] = 255 - blend[i];
619 #endif
622 static void splashOutBlendLighten(SplashColorPtr src, SplashColorPtr dest,
623 SplashColorPtr blend, SplashColorMode cm) {
624 int i;
626 #if SPLASH_CMYK
627 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
628 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
629 dest[i] = 255 - dest[i];
630 src[i] = 255 - src[i];
633 #endif
635 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
636 blend[i] = dest[i] > src[i] ? dest[i] : src[i];
639 #if SPLASH_CMYK
640 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
641 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
642 dest[i] = 255 - dest[i];
643 src[i] = 255 - src[i];
644 blend[i] = 255 - blend[i];
647 #endif
650 static void splashOutBlendColorDodge(SplashColorPtr src, SplashColorPtr dest,
651 SplashColorPtr blend,
652 SplashColorMode cm) {
653 int i, x;
655 #if SPLASH_CMYK
656 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
657 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
658 dest[i] = 255 - dest[i];
659 src[i] = 255 - src[i];
662 #endif
664 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
665 if (src[i] == 255) {
666 blend[i] = 255;
667 } else {
668 x = (dest[i] * 255) / (255 - src[i]);
669 blend[i] = x <= 255 ? x : 255;
673 #if SPLASH_CMYK
674 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
675 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
676 dest[i] = 255 - dest[i];
677 src[i] = 255 - src[i];
678 blend[i] = 255 - blend[i];
681 #endif
684 static void splashOutBlendColorBurn(SplashColorPtr src, SplashColorPtr dest,
685 SplashColorPtr blend, SplashColorMode cm) {
686 int i, x;
688 #if SPLASH_CMYK
689 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
690 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
691 dest[i] = 255 - dest[i];
692 src[i] = 255 - src[i];
695 #endif
697 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
698 if (src[i] == 0) {
699 blend[i] = 0;
700 } else {
701 x = ((255 - dest[i]) * 255) / src[i];
702 blend[i] = x <= 255 ? 255 - x : 0;
706 #if SPLASH_CMYK
707 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
708 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
709 dest[i] = 255 - dest[i];
710 src[i] = 255 - src[i];
711 blend[i] = 255 - blend[i];
714 #endif
717 static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
718 SplashColorPtr blend, SplashColorMode cm) {
719 int i;
721 #if SPLASH_CMYK
722 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
723 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
724 dest[i] = 255 - dest[i];
725 src[i] = 255 - src[i];
728 #endif
730 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
731 blend[i] = src[i] < 0x80
732 ? (dest[i] * 2 * src[i]) / 255
733 : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
736 #if SPLASH_CMYK
737 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
738 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
739 dest[i] = 255 - dest[i];
740 src[i] = 255 - src[i];
741 blend[i] = 255 - blend[i];
744 #endif
747 static void splashOutBlendSoftLight(SplashColorPtr src, SplashColorPtr dest,
748 SplashColorPtr blend, SplashColorMode cm) {
749 int i, x;
751 #if SPLASH_CMYK
752 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
753 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
754 dest[i] = 255 - dest[i];
755 src[i] = 255 - src[i];
758 #endif
760 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
761 if (src[i] < 0x80) {
762 blend[i] = dest[i] - (255 - 2 * src[i]) * dest[i] * (255 - dest[i]) / (255 * 255);
763 } else {
764 if (dest[i] < 0x40) {
765 x = (((((16 * dest[i] - 12 * 255) * dest[i]) / 255) + 4 * 255) * dest[i]) / 255;
766 } else {
767 x = (int)sqrt(255.0 * dest[i]);
769 blend[i] = dest[i] + (2 * src[i] - 255) * (x - dest[i]) / 255;
773 #if SPLASH_CMYK
774 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
775 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
776 dest[i] = 255 - dest[i];
777 src[i] = 255 - src[i];
778 blend[i] = 255 - blend[i];
781 #endif
784 static void splashOutBlendDifference(SplashColorPtr src, SplashColorPtr dest,
785 SplashColorPtr blend,
786 SplashColorMode cm) {
787 int i;
789 #if SPLASH_CMYK
790 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
791 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
792 dest[i] = 255 - dest[i];
793 src[i] = 255 - src[i];
796 #endif
798 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
799 blend[i] = dest[i] < src[i] ? src[i] - dest[i] : dest[i] - src[i];
802 #if SPLASH_CMYK
803 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
804 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
805 dest[i] = 255 - dest[i];
806 src[i] = 255 - src[i];
807 blend[i] = 255 - blend[i];
810 if (cm == splashModeDeviceN8) {
811 for (i = 4; i < splashColorModeNComps[cm]; ++i) {
812 if (dest[i] == 0 && src[i] == 0)
813 blend[i] = 0;
816 #endif
819 static void splashOutBlendExclusion(SplashColorPtr src, SplashColorPtr dest,
820 SplashColorPtr blend, SplashColorMode cm) {
821 int i;
823 #if SPLASH_CMYK
824 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
825 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
826 dest[i] = 255 - dest[i];
827 src[i] = 255 - src[i];
830 #endif
832 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
833 blend[i] = dest[i] + src[i] - (2 * dest[i] * src[i]) / 255;
836 #if SPLASH_CMYK
837 if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
838 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
839 dest[i] = 255 - dest[i];
840 src[i] = 255 - src[i];
841 blend[i] = 255 - blend[i];
844 if (cm == splashModeDeviceN8) {
845 for (i = 4; i < splashColorModeNComps[cm]; ++i) {
846 if (dest[i] == 0 && src[i] == 0)
847 blend[i] = 0;
850 #endif
853 static int getLum(int r, int g, int b) {
854 return (int)(0.3 * r + 0.59 * g + 0.11 * b);
857 static int getSat(int r, int g, int b) {
858 int rgbMin, rgbMax;
860 rgbMin = rgbMax = r;
861 if (g < rgbMin) {
862 rgbMin = g;
863 } else if (g > rgbMax) {
864 rgbMax = g;
866 if (b < rgbMin) {
867 rgbMin = b;
868 } else if (b > rgbMax) {
869 rgbMax = b;
871 return rgbMax - rgbMin;
874 static void clipColor(int rIn, int gIn, int bIn,
875 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
876 int lum, rgbMin, rgbMax;
878 lum = getLum(rIn, gIn, bIn);
879 rgbMin = rgbMax = rIn;
880 if (gIn < rgbMin) {
881 rgbMin = gIn;
882 } else if (gIn > rgbMax) {
883 rgbMax = gIn;
885 if (bIn < rgbMin) {
886 rgbMin = bIn;
887 } else if (bIn > rgbMax) {
888 rgbMax = bIn;
890 if (rgbMin < 0) {
891 *rOut = (Guchar)(lum + ((rIn - lum) * lum) / (lum - rgbMin));
892 *gOut = (Guchar)(lum + ((gIn - lum) * lum) / (lum - rgbMin));
893 *bOut = (Guchar)(lum + ((bIn - lum) * lum) / (lum - rgbMin));
894 } else if (rgbMax > 255) {
895 *rOut = (Guchar)(lum + ((rIn - lum) * (255 - lum)) / (rgbMax - lum));
896 *gOut = (Guchar)(lum + ((gIn - lum) * (255 - lum)) / (rgbMax - lum));
897 *bOut = (Guchar)(lum + ((bIn - lum) * (255 - lum)) / (rgbMax - lum));
898 } else {
899 *rOut = rIn;
900 *gOut = gIn;
901 *bOut = bIn;
905 static void setLum(Guchar rIn, Guchar gIn, Guchar bIn, int lum,
906 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
907 int d;
909 d = lum - getLum(rIn, gIn, bIn);
910 clipColor(rIn + d, gIn + d, bIn + d, rOut, gOut, bOut);
913 static void setSat(Guchar rIn, Guchar gIn, Guchar bIn, int sat,
914 Guchar *rOut, Guchar *gOut, Guchar *bOut) {
915 int rgbMin, rgbMid, rgbMax;
916 Guchar *minOut, *midOut, *maxOut;
918 if (rIn < gIn) {
919 rgbMin = rIn; minOut = rOut;
920 rgbMid = gIn; midOut = gOut;
921 } else {
922 rgbMin = gIn; minOut = gOut;
923 rgbMid = rIn; midOut = rOut;
925 if (bIn > rgbMid) {
926 rgbMax = bIn; maxOut = bOut;
927 } else if (bIn > rgbMin) {
928 rgbMax = rgbMid; maxOut = midOut;
929 rgbMid = bIn; midOut = bOut;
930 } else {
931 rgbMax = rgbMid; maxOut = midOut;
932 rgbMid = rgbMin; midOut = minOut;
933 rgbMin = bIn; minOut = bOut;
935 if (rgbMax > rgbMin) {
936 *midOut = (Guchar)((rgbMid - rgbMin) * sat) / (rgbMax - rgbMin);
937 *maxOut = (Guchar)sat;
938 } else {
939 *midOut = *maxOut = 0;
941 *minOut = 0;
944 static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
945 SplashColorPtr blend, SplashColorMode cm) {
946 Guchar r0, g0, b0;
947 #if SPLASH_CMYK
948 Guchar r1, g1, b1;
949 int i;
950 SplashColor src2, dest2;
951 #endif
953 switch (cm) {
954 case splashModeMono1:
955 case splashModeMono8:
956 blend[0] = dest[0];
957 break;
958 case splashModeXBGR8:
959 src[3] = 255;
960 case splashModeRGB8:
961 case splashModeBGR8:
962 setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
963 &r0, &g0, &b0);
964 setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
965 &blend[0], &blend[1], &blend[2]);
966 break;
967 #if SPLASH_CMYK
968 case splashModeCMYK8:
969 case splashModeDeviceN8:
970 for (i = 0; i < 4; i++) {
971 // convert to additive
972 src2[i] = 0xff - src[i];
973 dest2[i] = 0xff - dest[i];
975 // NB: inputs have already been converted to additive mode
976 setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]),
977 &r0, &g0, &b0);
978 setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]),
979 &r1, &g1, &b1);
980 blend[0] = r1;
981 blend[1] = g1;
982 blend[2] = b1;
983 blend[3] = dest2[3];
984 for (i = 0; i < 4; i++) {
985 // convert back to subtractive
986 blend[i] = 0xff - blend[i];
988 break;
989 #endif
993 static void splashOutBlendSaturation(SplashColorPtr src, SplashColorPtr dest,
994 SplashColorPtr blend,
995 SplashColorMode cm) {
996 Guchar r0, g0, b0;
997 #if SPLASH_CMYK
998 Guchar r1, g1, b1;
999 int i;
1000 SplashColor src2, dest2;
1001 #endif
1003 switch (cm) {
1004 case splashModeMono1:
1005 case splashModeMono8:
1006 blend[0] = dest[0];
1007 break;
1008 case splashModeXBGR8:
1009 src[3] = 255;
1010 case splashModeRGB8:
1011 case splashModeBGR8:
1012 setSat(dest[0], dest[1], dest[2], getSat(src[0], src[1], src[2]),
1013 &r0, &g0, &b0);
1014 setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
1015 &blend[0], &blend[1], &blend[2]);
1016 break;
1017 #if SPLASH_CMYK
1018 case splashModeCMYK8:
1019 case splashModeDeviceN8:
1020 for (i = 0; i < 4; i++) {
1021 // convert to additive
1022 src2[i] = 0xff - src[i];
1023 dest2[i] = 0xff - dest[i];
1025 setSat(dest2[0], dest2[1], dest2[2], getSat(src2[0], src2[1], src2[2]),
1026 &r0, &g0, &b0);
1027 setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]),
1028 &r1, &g1, &b1);
1029 blend[0] = r1;
1030 blend[1] = g1;
1031 blend[2] = b1;
1032 blend[3] = dest2[3];
1033 for (i = 0; i < 4; i++) {
1034 // convert back to subtractive
1035 blend[i] = 0xff - blend[i];
1037 break;
1038 #endif
1042 static void splashOutBlendColor(SplashColorPtr src, SplashColorPtr dest,
1043 SplashColorPtr blend, SplashColorMode cm) {
1044 #if SPLASH_CMYK
1045 Guchar r, g, b;
1046 int i;
1047 SplashColor src2, dest2;
1048 #endif
1050 switch (cm) {
1051 case splashModeMono1:
1052 case splashModeMono8:
1053 blend[0] = dest[0];
1054 break;
1055 case splashModeXBGR8:
1056 src[3] = 255;
1057 case splashModeRGB8:
1058 case splashModeBGR8:
1059 setLum(src[0], src[1], src[2], getLum(dest[0], dest[1], dest[2]),
1060 &blend[0], &blend[1], &blend[2]);
1061 break;
1062 #if SPLASH_CMYK
1063 case splashModeCMYK8:
1064 case splashModeDeviceN8:
1065 for (i = 0; i < 4; i++) {
1066 // convert to additive
1067 src2[i] = 0xff - src[i];
1068 dest2[i] = 0xff - dest[i];
1070 setLum(src2[0], src2[1], src2[2], getLum(dest2[0], dest2[1], dest2[2]),
1071 &r, &g, &b);
1072 blend[0] = r;
1073 blend[1] = g;
1074 blend[2] = b;
1075 blend[3] = dest2[3];
1076 for (i = 0; i < 4; i++) {
1077 // convert back to subtractive
1078 blend[i] = 0xff - blend[i];
1080 break;
1081 #endif
1085 static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
1086 SplashColorPtr blend,
1087 SplashColorMode cm) {
1088 #if SPLASH_CMYK
1089 Guchar r, g, b;
1090 int i;
1091 SplashColor src2, dest2;
1092 #endif
1094 switch (cm) {
1095 case splashModeMono1:
1096 case splashModeMono8:
1097 blend[0] = dest[0];
1098 break;
1099 case splashModeXBGR8:
1100 src[3] = 255;
1101 case splashModeRGB8:
1102 case splashModeBGR8:
1103 setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
1104 &blend[0], &blend[1], &blend[2]);
1105 break;
1106 #if SPLASH_CMYK
1107 case splashModeCMYK8:
1108 case splashModeDeviceN8:
1109 for (i = 0; i < 4; i++) {
1110 // convert to additive
1111 src2[i] = 0xff - src[i];
1112 dest2[i] = 0xff - dest[i];
1114 setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]),
1115 &r, &g, &b);
1116 blend[0] = r;
1117 blend[1] = g;
1118 blend[2] = b;
1119 blend[3] = src2[3];
1120 for (i = 0; i < 4; i++) {
1121 // convert back to subtractive
1122 blend[i] = 0xff - blend[i];
1124 break;
1125 #endif
1129 // NB: This must match the GfxBlendMode enum defined in GfxState.h.
1130 static const SplashBlendFunc splashOutBlendFuncs[] = {
1131 NULL,
1132 &splashOutBlendMultiply,
1133 &splashOutBlendScreen,
1134 &splashOutBlendOverlay,
1135 &splashOutBlendDarken,
1136 &splashOutBlendLighten,
1137 &splashOutBlendColorDodge,
1138 &splashOutBlendColorBurn,
1139 &splashOutBlendHardLight,
1140 &splashOutBlendSoftLight,
1141 &splashOutBlendDifference,
1142 &splashOutBlendExclusion,
1143 &splashOutBlendHue,
1144 &splashOutBlendSaturation,
1145 &splashOutBlendColor,
1146 &splashOutBlendLuminosity
1149 //------------------------------------------------------------------------
1150 // SplashOutFontFileID
1151 //------------------------------------------------------------------------
1153 class SplashOutFontFileID: public SplashFontFileID {
1154 public:
1156 SplashOutFontFileID(Ref *rA) { r = *rA; }
1158 ~SplashOutFontFileID() {}
1160 GBool matches(SplashFontFileID *id) {
1161 return ((SplashOutFontFileID *)id)->r.num == r.num &&
1162 ((SplashOutFontFileID *)id)->r.gen == r.gen;
1165 private:
1167 Ref r;
1170 //------------------------------------------------------------------------
1171 // T3FontCache
1172 //------------------------------------------------------------------------
1174 struct T3FontCacheTag {
1175 Gushort code;
1176 Gushort mru; // valid bit (0x8000) and MRU index
1179 class T3FontCache {
1180 public:
1182 T3FontCache(Ref *fontID, double m11A, double m12A,
1183 double m21A, double m22A,
1184 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1185 GBool aa, GBool validBBoxA);
1186 ~T3FontCache();
1187 GBool matches(Ref *idA, double m11A, double m12A,
1188 double m21A, double m22A)
1189 { return fontID.num == idA->num && fontID.gen == idA->gen &&
1190 m11 == m11A && m12 == m12A && m21 == m21A && m22 == m22A; }
1192 Ref fontID; // PDF font ID
1193 double m11, m12, m21, m22; // transform matrix
1194 int glyphX, glyphY; // pixel offset of glyph bitmaps
1195 int glyphW, glyphH; // size of glyph bitmaps, in pixels
1196 GBool validBBox; // false if the bbox was [0 0 0 0]
1197 int glyphSize; // size of glyph bitmaps, in bytes
1198 int cacheSets; // number of sets in cache
1199 int cacheAssoc; // cache associativity (glyphs per set)
1200 Guchar *cacheData; // glyph pixmap cache
1201 T3FontCacheTag *cacheTags; // cache tags, i.e., char codes
1204 T3FontCache::T3FontCache(Ref *fontIDA, double m11A, double m12A,
1205 double m21A, double m22A,
1206 int glyphXA, int glyphYA, int glyphWA, int glyphHA,
1207 GBool validBBoxA, GBool aa) {
1208 int i;
1210 fontID = *fontIDA;
1211 m11 = m11A;
1212 m12 = m12A;
1213 m21 = m21A;
1214 m22 = m22A;
1215 glyphX = glyphXA;
1216 glyphY = glyphYA;
1217 glyphW = glyphWA;
1218 glyphH = glyphHA;
1219 validBBox = validBBoxA;
1220 // sanity check for excessively large glyphs (which most likely
1221 // indicate an incorrect BBox)
1222 i = glyphW * glyphH;
1223 if (i > 100000 || glyphW > INT_MAX / glyphH || glyphW <= 0 || glyphH <= 0) {
1224 glyphW = glyphH = 100;
1225 validBBox = gFalse;
1227 if (aa) {
1228 glyphSize = glyphW * glyphH;
1229 } else {
1230 glyphSize = ((glyphW + 7) >> 3) * glyphH;
1232 cacheAssoc = type3FontCacheAssoc;
1233 for (cacheSets = type3FontCacheMaxSets;
1234 cacheSets > 1 &&
1235 cacheSets * cacheAssoc * glyphSize > type3FontCacheSize;
1236 cacheSets >>= 1) ;
1237 if (glyphSize < 10485760 / cacheAssoc / cacheSets) {
1238 cacheData = (Guchar *)gmallocn_checkoverflow(cacheSets * cacheAssoc, glyphSize);
1239 } else {
1240 error(errSyntaxWarning, -1, "Not creating cacheData for T3FontCache, it asked for too much memory.\n"
1241 " This could teoretically result in wrong rendering,\n"
1242 " but most probably the document is bogus.\n"
1243 " Please report a bug if you think the rendering may be wrong because of this.");
1244 cacheData = NULL;
1246 if (cacheData != NULL)
1248 cacheTags = (T3FontCacheTag *)gmallocn(cacheSets * cacheAssoc,
1249 sizeof(T3FontCacheTag));
1250 for (i = 0; i < cacheSets * cacheAssoc; ++i) {
1251 cacheTags[i].mru = i & (cacheAssoc - 1);
1254 else
1256 cacheTags = NULL;
1260 T3FontCache::~T3FontCache() {
1261 gfree(cacheData);
1262 gfree(cacheTags);
1265 struct T3GlyphStack {
1266 Gushort code; // character code
1268 //----- cache info
1269 T3FontCache *cache; // font cache for the current font
1270 T3FontCacheTag *cacheTag; // pointer to cache tag for the glyph
1271 Guchar *cacheData; // pointer to cache data for the glyph
1273 //----- saved state
1274 SplashBitmap *origBitmap;
1275 Splash *origSplash;
1276 double origCTM4, origCTM5;
1278 T3GlyphStack *next; // next object on stack
1281 //------------------------------------------------------------------------
1282 // SplashTransparencyGroup
1283 //------------------------------------------------------------------------
1285 struct SplashTransparencyGroup {
1286 int tx, ty; // translation coordinates
1287 SplashBitmap *tBitmap; // bitmap for transparency group
1288 SplashBitmap *softmask; // bitmap for softmasks
1289 GfxColorSpace *blendingColorSpace;
1290 GBool isolated;
1292 //----- for knockout
1293 SplashBitmap *shape;
1294 GBool knockout;
1295 SplashCoord knockoutOpacity;
1296 GBool fontAA;
1298 //----- saved state
1299 SplashBitmap *origBitmap;
1300 Splash *origSplash;
1302 SplashTransparencyGroup *next;
1305 //------------------------------------------------------------------------
1306 // SplashOutputDev
1307 //------------------------------------------------------------------------
1309 SplashOutputDev::SplashOutputDev(SplashColorMode colorModeA,
1310 int bitmapRowPadA,
1311 GBool reverseVideoA,
1312 SplashColorPtr paperColorA,
1313 GBool bitmapTopDownA,
1314 SplashThinLineMode thinLineMode,
1315 GBool overprintPreviewA) {
1316 colorMode = colorModeA;
1317 bitmapRowPad = bitmapRowPadA;
1318 bitmapTopDown = bitmapTopDownA;
1319 bitmapUpsideDown = gFalse;
1320 fontAntialias = gTrue;
1321 vectorAntialias = gTrue;
1322 overprintPreview = overprintPreviewA;
1323 enableFreeTypeHinting = gFalse;
1324 enableSlightHinting = gFalse;
1325 setupScreenParams(72.0, 72.0);
1326 reverseVideo = reverseVideoA;
1327 if (paperColorA != NULL) {
1328 splashColorCopy(paperColor, paperColorA);
1329 } else {
1330 splashClearColor(paperColor);
1332 skipHorizText = gFalse;
1333 skipRotatedText = gFalse;
1334 keepAlphaChannel = paperColorA == NULL;
1336 doc = NULL;
1338 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
1339 colorMode != splashModeMono1, bitmapTopDown);
1340 splash = new Splash(bitmap, vectorAntialias, &screenParams);
1341 splash->setMinLineWidth(globalParams->getMinLineWidth());
1342 splash->setThinLineMode(thinLineMode);
1343 splash->clear(paperColor, 0);
1345 fontEngine = NULL;
1347 nT3Fonts = 0;
1348 t3GlyphStack = NULL;
1350 font = NULL;
1351 needFontUpdate = gFalse;
1352 textClipPath = NULL;
1353 transpGroupStack = NULL;
1354 nestCount = 0;
1355 xref = NULL;
1358 void SplashOutputDev::setupScreenParams(double hDPI, double vDPI) {
1359 screenParams.size = globalParams->getScreenSize();
1360 screenParams.dotRadius = globalParams->getScreenDotRadius();
1361 screenParams.gamma = (SplashCoord)globalParams->getScreenGamma();
1362 screenParams.blackThreshold =
1363 (SplashCoord)globalParams->getScreenBlackThreshold();
1364 screenParams.whiteThreshold =
1365 (SplashCoord)globalParams->getScreenWhiteThreshold();
1366 switch (globalParams->getScreenType()) {
1367 case screenDispersed:
1368 screenParams.type = splashScreenDispersed;
1369 if (screenParams.size < 0) {
1370 screenParams.size = 4;
1372 break;
1373 case screenClustered:
1374 screenParams.type = splashScreenClustered;
1375 if (screenParams.size < 0) {
1376 screenParams.size = 10;
1378 break;
1379 case screenStochasticClustered:
1380 screenParams.type = splashScreenStochasticClustered;
1381 if (screenParams.size < 0) {
1382 screenParams.size = 64;
1384 if (screenParams.dotRadius < 0) {
1385 screenParams.dotRadius = 2;
1387 break;
1388 case screenUnset:
1389 default:
1390 // use clustered dithering for resolution >= 300 dpi
1391 // (compare to 299.9 to avoid floating point issues)
1392 if (hDPI > 299.9 && vDPI > 299.9) {
1393 screenParams.type = splashScreenStochasticClustered;
1394 if (screenParams.size < 0) {
1395 screenParams.size = 64;
1397 if (screenParams.dotRadius < 0) {
1398 screenParams.dotRadius = 2;
1400 } else {
1401 screenParams.type = splashScreenDispersed;
1402 if (screenParams.size < 0) {
1403 screenParams.size = 4;
1409 SplashOutputDev::~SplashOutputDev() {
1410 int i;
1412 for (i = 0; i < nT3Fonts; ++i) {
1413 delete t3FontCache[i];
1415 if (fontEngine) {
1416 delete fontEngine;
1418 if (splash) {
1419 delete splash;
1421 if (bitmap) {
1422 delete bitmap;
1426 void SplashOutputDev::startDoc(PDFDoc *docA) {
1427 int i;
1429 doc = docA;
1430 if (fontEngine) {
1431 delete fontEngine;
1433 fontEngine = new SplashFontEngine(
1434 #if HAVE_T1LIB_H
1435 globalParams->getEnableT1lib(),
1436 #endif
1437 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
1438 globalParams->getEnableFreeType(),
1439 enableFreeTypeHinting,
1440 enableSlightHinting,
1441 #endif
1442 getFontAntialias() &&
1443 colorMode != splashModeMono1);
1444 for (i = 0; i < nT3Fonts; ++i) {
1445 delete t3FontCache[i];
1447 nT3Fonts = 0;
1450 void SplashOutputDev::startPage(int pageNum, GfxState *state, XRef *xrefA) {
1451 int w, h;
1452 double *ctm;
1453 SplashCoord mat[6];
1454 SplashColor color;
1456 xref = xrefA;
1457 if (state) {
1458 setupScreenParams(state->getHDPI(), state->getVDPI());
1459 w = (int)(state->getPageWidth() + 0.5);
1460 if (w <= 0) {
1461 w = 1;
1463 h = (int)(state->getPageHeight() + 0.5);
1464 if (h <= 0) {
1465 h = 1;
1467 } else {
1468 w = h = 1;
1470 SplashThinLineMode thinLineMode = splashThinLineDefault;
1471 if (splash) {
1472 thinLineMode = splash->getThinLineMode();
1473 delete splash;
1474 splash = NULL;
1476 if (!bitmap || w != bitmap->getWidth() || h != bitmap->getHeight()) {
1477 if (bitmap) {
1478 delete bitmap;
1479 bitmap = NULL;
1481 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1482 colorMode != splashModeMono1, bitmapTopDown);
1483 if (!bitmap->getDataPtr()) {
1484 delete bitmap;
1485 w = h = 1;
1486 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode,
1487 colorMode != splashModeMono1, bitmapTopDown);
1490 splash = new Splash(bitmap, vectorAntialias, &screenParams);
1491 splash->setThinLineMode(thinLineMode);
1492 splash->setMinLineWidth(globalParams->getMinLineWidth());
1493 if (state) {
1494 ctm = state->getCTM();
1495 mat[0] = (SplashCoord)ctm[0];
1496 mat[1] = (SplashCoord)ctm[1];
1497 mat[2] = (SplashCoord)ctm[2];
1498 mat[3] = (SplashCoord)ctm[3];
1499 mat[4] = (SplashCoord)ctm[4];
1500 mat[5] = (SplashCoord)ctm[5];
1501 splash->setMatrix(mat);
1503 switch (colorMode) {
1504 case splashModeMono1:
1505 case splashModeMono8:
1506 color[0] = 0;
1507 break;
1508 case splashModeXBGR8:
1509 color[3] = 255;
1510 case splashModeRGB8:
1511 case splashModeBGR8:
1512 color[0] = color[1] = color[2] = 0;
1513 break;
1514 #if SPLASH_CMYK
1515 case splashModeCMYK8:
1516 color[0] = color[1] = color[2] = color[3] = 0;
1517 break;
1518 case splashModeDeviceN8:
1519 for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
1520 color[i] = 0;
1521 break;
1522 #endif
1524 splash->setStrokePattern(new SplashSolidColor(color));
1525 splash->setFillPattern(new SplashSolidColor(color));
1526 splash->setLineCap(splashLineCapButt);
1527 splash->setLineJoin(splashLineJoinMiter);
1528 splash->setLineDash(NULL, 0, 0);
1529 splash->setMiterLimit(10);
1530 splash->setFlatness(1);
1531 // the SA parameter supposedly defaults to false, but Acrobat
1532 // apparently hardwires it to true
1533 splash->setStrokeAdjust(globalParams->getStrokeAdjust());
1534 splash->clear(paperColor, 0);
1537 void SplashOutputDev::endPage() {
1538 if (colorMode != splashModeMono1 && !keepAlphaChannel) {
1539 splash->compositeBackground(paperColor);
1543 void SplashOutputDev::saveState(GfxState *state) {
1544 splash->saveState();
1547 void SplashOutputDev::restoreState(GfxState *state) {
1548 splash->restoreState();
1549 needFontUpdate = gTrue;
1552 void SplashOutputDev::updateAll(GfxState *state) {
1553 updateLineDash(state);
1554 updateLineJoin(state);
1555 updateLineCap(state);
1556 updateLineWidth(state);
1557 updateFlatness(state);
1558 updateMiterLimit(state);
1559 updateStrokeAdjust(state);
1560 updateFillColorSpace(state);
1561 updateFillColor(state);
1562 updateStrokeColorSpace(state);
1563 updateStrokeColor(state);
1564 needFontUpdate = gTrue;
1567 void SplashOutputDev::updateCTM(GfxState *state, double m11, double m12,
1568 double m21, double m22,
1569 double m31, double m32) {
1570 double *ctm;
1571 SplashCoord mat[6];
1573 ctm = state->getCTM();
1574 mat[0] = (SplashCoord)ctm[0];
1575 mat[1] = (SplashCoord)ctm[1];
1576 mat[2] = (SplashCoord)ctm[2];
1577 mat[3] = (SplashCoord)ctm[3];
1578 mat[4] = (SplashCoord)ctm[4];
1579 mat[5] = (SplashCoord)ctm[5];
1580 splash->setMatrix(mat);
1583 void SplashOutputDev::updateLineDash(GfxState *state) {
1584 double *dashPattern;
1585 int dashLength;
1586 double dashStart;
1587 SplashCoord dash[20];
1588 int i;
1590 state->getLineDash(&dashPattern, &dashLength, &dashStart);
1591 if (dashLength > 20) {
1592 dashLength = 20;
1594 for (i = 0; i < dashLength; ++i) {
1595 dash[i] = (SplashCoord)dashPattern[i];
1596 if (dash[i] < 0) {
1597 dash[i] = 0;
1600 splash->setLineDash(dash, dashLength, (SplashCoord)dashStart);
1603 void SplashOutputDev::updateFlatness(GfxState *state) {
1604 #if 0 // Acrobat ignores the flatness setting, and always renders curves
1605 // with a fairly small flatness value
1606 splash->setFlatness(state->getFlatness());
1607 #endif
1610 void SplashOutputDev::updateLineJoin(GfxState *state) {
1611 splash->setLineJoin(state->getLineJoin());
1614 void SplashOutputDev::updateLineCap(GfxState *state) {
1615 splash->setLineCap(state->getLineCap());
1618 void SplashOutputDev::updateMiterLimit(GfxState *state) {
1619 splash->setMiterLimit(state->getMiterLimit());
1622 void SplashOutputDev::updateLineWidth(GfxState *state) {
1623 splash->setLineWidth(state->getLineWidth());
1626 void SplashOutputDev::updateStrokeAdjust(GfxState * /*state*/) {
1627 #if 0 // the SA parameter supposedly defaults to false, but Acrobat
1628 // apparently hardwires it to true
1629 splash->setStrokeAdjust(state->getStrokeAdjust());
1630 #endif
1633 void SplashOutputDev::updateFillColorSpace(GfxState *state) {
1634 #if SPLASH_CMYK
1635 if (colorMode == splashModeDeviceN8)
1636 state->getFillColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
1637 #endif
1640 void SplashOutputDev::updateStrokeColorSpace(GfxState *state) {
1641 #if SPLASH_CMYK
1642 if (colorMode == splashModeDeviceN8)
1643 state->getStrokeColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
1644 #endif
1647 void SplashOutputDev::updateFillColor(GfxState *state) {
1648 GfxGray gray;
1649 GfxRGB rgb;
1650 #if SPLASH_CMYK
1651 GfxCMYK cmyk;
1652 GfxColor deviceN;
1653 #endif
1655 switch (colorMode) {
1656 case splashModeMono1:
1657 case splashModeMono8:
1658 state->getFillGray(&gray);
1659 splash->setFillPattern(getColor(gray));
1660 break;
1661 case splashModeXBGR8:
1662 case splashModeRGB8:
1663 case splashModeBGR8:
1664 state->getFillRGB(&rgb);
1665 splash->setFillPattern(getColor(&rgb));
1666 break;
1667 #if SPLASH_CMYK
1668 case splashModeCMYK8:
1669 state->getFillCMYK(&cmyk);
1670 splash->setFillPattern(getColor(&cmyk));
1671 break;
1672 case splashModeDeviceN8:
1673 state->getFillDeviceN(&deviceN);
1674 splash->setFillPattern(getColor(&deviceN));
1675 break;
1676 #endif
1680 void SplashOutputDev::updateStrokeColor(GfxState *state) {
1681 GfxGray gray;
1682 GfxRGB rgb;
1683 #if SPLASH_CMYK
1684 GfxCMYK cmyk;
1685 GfxColor deviceN;
1686 #endif
1688 switch (colorMode) {
1689 case splashModeMono1:
1690 case splashModeMono8:
1691 state->getStrokeGray(&gray);
1692 splash->setStrokePattern(getColor(gray));
1693 break;
1694 case splashModeXBGR8:
1695 case splashModeRGB8:
1696 case splashModeBGR8:
1697 state->getStrokeRGB(&rgb);
1698 splash->setStrokePattern(getColor(&rgb));
1699 break;
1700 #if SPLASH_CMYK
1701 case splashModeCMYK8:
1702 state->getStrokeCMYK(&cmyk);
1703 splash->setStrokePattern(getColor(&cmyk));
1704 break;
1705 case splashModeDeviceN8:
1706 state->getStrokeDeviceN(&deviceN);
1707 splash->setStrokePattern(getColor(&deviceN));
1708 break;
1709 #endif
1713 SplashPattern *SplashOutputDev::getColor(GfxGray gray) {
1714 SplashColor color;
1716 if (reverseVideo) {
1717 gray = gfxColorComp1 - gray;
1719 color[0] = colToByte(gray);
1720 return new SplashSolidColor(color);
1723 SplashPattern *SplashOutputDev::getColor(GfxRGB *rgb) {
1724 GfxColorComp r, g, b;
1725 SplashColor color;
1727 if (reverseVideo) {
1728 r = gfxColorComp1 - rgb->r;
1729 g = gfxColorComp1 - rgb->g;
1730 b = gfxColorComp1 - rgb->b;
1731 } else {
1732 r = rgb->r;
1733 g = rgb->g;
1734 b = rgb->b;
1736 color[0] = colToByte(r);
1737 color[1] = colToByte(g);
1738 color[2] = colToByte(b);
1739 if (colorMode == splashModeXBGR8) color[3] = 255;
1740 return new SplashSolidColor(color);
1743 #if SPLASH_CMYK
1744 SplashPattern *SplashOutputDev::getColor(GfxCMYK *cmyk) {
1745 SplashColor color;
1747 color[0] = colToByte(cmyk->c);
1748 color[1] = colToByte(cmyk->m);
1749 color[2] = colToByte(cmyk->y);
1750 color[3] = colToByte(cmyk->k);
1751 return new SplashSolidColor(color);
1754 SplashPattern *SplashOutputDev::getColor(GfxColor *deviceN) {
1755 SplashColor color;
1757 for (int i = 0; i < 4 + SPOT_NCOMPS; i++)
1758 color[i] = colToByte(deviceN->c[i]);
1759 return new SplashSolidColor(color);
1761 #endif
1763 void SplashOutputDev::setOverprintMask(GfxColorSpace *colorSpace,
1764 GBool overprintFlag,
1765 int overprintMode,
1766 GfxColor *singleColor,
1767 GBool grayIndexed) {
1768 #if SPLASH_CMYK
1769 Guint mask;
1770 GfxCMYK cmyk;
1771 GBool additive = gFalse;
1772 int i;
1774 if (colorSpace->getMode() == csIndexed) {
1775 setOverprintMask(((GfxIndexedColorSpace *)colorSpace)->getBase(),
1776 overprintFlag,
1777 overprintMode,
1778 singleColor,
1779 grayIndexed);
1780 return;
1782 if (overprintFlag && overprintPreview) {
1783 mask = colorSpace->getOverprintMask();
1784 if (singleColor && overprintMode &&
1785 colorSpace->getMode() == csDeviceCMYK) {
1786 colorSpace->getCMYK(singleColor, &cmyk);
1787 if (cmyk.c == 0) {
1788 mask &= ~1;
1790 if (cmyk.m == 0) {
1791 mask &= ~2;
1793 if (cmyk.y == 0) {
1794 mask &= ~4;
1796 if (cmyk.k == 0) {
1797 mask &= ~8;
1800 if (grayIndexed) {
1801 mask &= ~7;
1802 } else if (colorSpace->getMode() == csSeparation) {
1803 GfxSeparationColorSpace *deviceSep = (GfxSeparationColorSpace *)colorSpace;
1804 additive = deviceSep->getName()->cmp("All") != 0 && mask == 0x0f && !deviceSep->isNonMarking();
1805 } else if (colorSpace->getMode() == csDeviceN) {
1806 GfxDeviceNColorSpace *deviceNCS = (GfxDeviceNColorSpace *)colorSpace;
1807 additive = mask == 0x0f && !deviceNCS->isNonMarking();
1808 for (i = 0; i < deviceNCS->getNComps() && additive; i++) {
1809 if (deviceNCS->getColorantName(i)->cmp("Cyan") == 0) {
1810 additive = gFalse;
1811 } else if (deviceNCS->getColorantName(i)->cmp("Magenta") == 0) {
1812 additive = gFalse;
1813 } else if (deviceNCS->getColorantName(i)->cmp("Yellow") == 0) {
1814 additive = gFalse;
1815 } else if (deviceNCS->getColorantName(i)->cmp("Black") == 0) {
1816 additive = gFalse;
1820 } else {
1821 mask = 0xffffffff;
1823 splash->setOverprintMask(mask, additive);
1824 #endif
1827 void SplashOutputDev::updateBlendMode(GfxState *state) {
1828 splash->setBlendFunc(splashOutBlendFuncs[state->getBlendMode()]);
1831 void SplashOutputDev::updateFillOpacity(GfxState *state) {
1832 splash->setFillAlpha((SplashCoord)state->getFillOpacity());
1833 if (transpGroupStack != NULL && (SplashCoord)state->getFillOpacity() < transpGroupStack->knockoutOpacity) {
1834 transpGroupStack->knockoutOpacity = (SplashCoord)state->getFillOpacity();
1838 void SplashOutputDev::updateStrokeOpacity(GfxState *state) {
1839 splash->setStrokeAlpha((SplashCoord)state->getStrokeOpacity());
1840 if (transpGroupStack != NULL && (SplashCoord)state->getStrokeOpacity() < transpGroupStack->knockoutOpacity) {
1841 transpGroupStack->knockoutOpacity = (SplashCoord)state->getStrokeOpacity();
1845 void SplashOutputDev::updatePatternOpacity(GfxState *state) {
1846 splash->setPatternAlpha((SplashCoord)state->getStrokeOpacity(), (SplashCoord)state->getFillOpacity());
1849 void SplashOutputDev::clearPatternOpacity(GfxState *state) {
1850 splash->clearPatternAlpha();
1853 void SplashOutputDev::updateFillOverprint(GfxState *state) {
1854 splash->setFillOverprint(state->getFillOverprint());
1857 void SplashOutputDev::updateStrokeOverprint(GfxState *state) {
1858 splash->setStrokeOverprint(state->getStrokeOverprint());
1861 void SplashOutputDev::updateOverprintMode(GfxState *state) {
1862 splash->setOverprintMode(state->getOverprintMode());
1865 void SplashOutputDev::updateTransfer(GfxState *state) {
1866 Function **transfer;
1867 Guchar red[256], green[256], blue[256], gray[256];
1868 double x, y;
1869 int i;
1871 transfer = state->getTransfer();
1872 if (transfer[0] &&
1873 transfer[0]->getInputSize() == 1 &&
1874 transfer[0]->getOutputSize() == 1) {
1875 if (transfer[1] &&
1876 transfer[1]->getInputSize() == 1 &&
1877 transfer[1]->getOutputSize() == 1 &&
1878 transfer[2] &&
1879 transfer[2]->getInputSize() == 1 &&
1880 transfer[2]->getOutputSize() == 1 &&
1881 transfer[3] &&
1882 transfer[3]->getInputSize() == 1 &&
1883 transfer[3]->getOutputSize() == 1) {
1884 for (i = 0; i < 256; ++i) {
1885 x = i / 255.0;
1886 transfer[0]->transform(&x, &y);
1887 red[i] = (Guchar)(y * 255.0 + 0.5);
1888 transfer[1]->transform(&x, &y);
1889 green[i] = (Guchar)(y * 255.0 + 0.5);
1890 transfer[2]->transform(&x, &y);
1891 blue[i] = (Guchar)(y * 255.0 + 0.5);
1892 transfer[3]->transform(&x, &y);
1893 gray[i] = (Guchar)(y * 255.0 + 0.5);
1895 } else {
1896 for (i = 0; i < 256; ++i) {
1897 x = i / 255.0;
1898 transfer[0]->transform(&x, &y);
1899 red[i] = green[i] = blue[i] = gray[i] = (Guchar)(y * 255.0 + 0.5);
1902 } else {
1903 for (i = 0; i < 256; ++i) {
1904 red[i] = green[i] = blue[i] = gray[i] = (Guchar)i;
1907 splash->setTransfer(red, green, blue, gray);
1910 void SplashOutputDev::updateFont(GfxState * /*state*/) {
1911 needFontUpdate = gTrue;
1914 void SplashOutputDev::doUpdateFont(GfxState *state) {
1915 GfxFont *gfxFont;
1916 GfxFontLoc *fontLoc;
1917 GfxFontType fontType;
1918 SplashOutFontFileID *id = NULL;
1919 SplashFontFile *fontFile;
1920 SplashFontSrc *fontsrc = NULL;
1921 FoFiTrueType *ff;
1922 Object refObj, strObj;
1923 GooString *fileName;
1924 char *tmpBuf;
1925 int tmpBufLen;
1926 int *codeToGID;
1927 double *textMat;
1928 double m11, m12, m21, m22, fontSize;
1929 int faceIndex = 0;
1930 SplashCoord mat[4];
1931 int n, i;
1932 GBool recreateFont = gFalse;
1933 GBool doAdjustFontMatrix = gFalse;
1935 needFontUpdate = gFalse;
1936 font = NULL;
1937 fileName = NULL;
1938 tmpBuf = NULL;
1939 fontLoc = NULL;
1941 if (!(gfxFont = state->getFont())) {
1942 goto err1;
1944 fontType = gfxFont->getType();
1945 if (fontType == fontType3) {
1946 goto err1;
1949 // sanity-check the font size - skip anything larger than 10 inches
1950 // (this avoids problems allocating memory for the font cache)
1951 if (state->getTransformedFontSize()
1952 > 10 * (state->getHDPI() + state->getVDPI())) {
1953 goto err1;
1956 // check the font file cache
1957 reload:
1958 delete id;
1959 delete fontLoc;
1960 if (fontsrc && !fontsrc->isFile)
1961 fontsrc->unref();
1963 id = new SplashOutFontFileID(gfxFont->getID());
1964 if ((fontFile = fontEngine->getFontFile(id))) {
1965 delete id;
1967 } else {
1969 if (!(fontLoc = gfxFont->locateFont((xref) ? xref : doc->getXRef(), NULL))) {
1970 error(errSyntaxError, -1, "Couldn't find a font for '{0:s}'",
1971 gfxFont->getName() ? gfxFont->getName()->getCString()
1972 : "(unnamed)");
1973 goto err2;
1976 // embedded font
1977 if (fontLoc->locType == gfxFontLocEmbedded) {
1978 // if there is an embedded font, read it to memory
1979 tmpBuf = gfxFont->readEmbFontFile((xref) ? xref : doc->getXRef(), &tmpBufLen);
1980 if (! tmpBuf)
1981 goto err2;
1983 // external font
1984 } else { // gfxFontLocExternal
1985 fileName = fontLoc->path;
1986 fontType = fontLoc->fontType;
1987 doAdjustFontMatrix = gTrue;
1990 fontsrc = new SplashFontSrc;
1991 if (fileName)
1992 fontsrc->setFile(fileName, gFalse);
1993 else
1994 fontsrc->setBuf(tmpBuf, tmpBufLen, gTrue);
1996 // load the font file
1997 switch (fontType) {
1998 case fontType1:
1999 if (!(fontFile = fontEngine->loadType1Font(
2001 fontsrc,
2002 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
2003 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2004 gfxFont->getName() ? gfxFont->getName()->getCString()
2005 : "(unnamed)");
2006 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2007 goto err2;
2009 break;
2010 case fontType1C:
2011 if (!(fontFile = fontEngine->loadType1CFont(
2013 fontsrc,
2014 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
2015 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2016 gfxFont->getName() ? gfxFont->getName()->getCString()
2017 : "(unnamed)");
2018 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2019 goto err2;
2021 break;
2022 case fontType1COT:
2023 if (!(fontFile = fontEngine->loadOpenTypeT1CFont(
2025 fontsrc,
2026 (const char **)((Gfx8BitFont *)gfxFont)->getEncoding()))) {
2027 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2028 gfxFont->getName() ? gfxFont->getName()->getCString()
2029 : "(unnamed)");
2030 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2031 goto err2;
2033 break;
2034 case fontTrueType:
2035 case fontTrueTypeOT:
2036 if (fileName)
2037 ff = FoFiTrueType::load(fileName->getCString());
2038 else
2039 ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
2040 if (ff) {
2041 codeToGID = ((Gfx8BitFont *)gfxFont)->getCodeToGIDMap(ff);
2042 n = 256;
2043 delete ff;
2044 // if we're substituting for a non-TrueType font, we need to mark
2045 // all notdef codes as "do not draw" (rather than drawing TrueType
2046 // notdef glyphs)
2047 if (gfxFont->getType() != fontTrueType &&
2048 gfxFont->getType() != fontTrueTypeOT) {
2049 for (i = 0; i < 256; ++i) {
2050 if (codeToGID[i] == 0) {
2051 codeToGID[i] = -1;
2055 } else {
2056 codeToGID = NULL;
2057 n = 0;
2059 if (!(fontFile = fontEngine->loadTrueTypeFont(
2061 fontsrc,
2062 codeToGID, n))) {
2063 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2064 gfxFont->getName() ? gfxFont->getName()->getCString()
2065 : "(unnamed)");
2066 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2067 goto err2;
2069 break;
2070 case fontCIDType0:
2071 case fontCIDType0C:
2072 if (!(fontFile = fontEngine->loadCIDFont(
2074 fontsrc))) {
2075 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2076 gfxFont->getName() ? gfxFont->getName()->getCString()
2077 : "(unnamed)");
2078 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2079 goto err2;
2081 break;
2082 case fontCIDType0COT:
2083 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
2084 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
2085 codeToGID = (int *)gmallocn(n, sizeof(int));
2086 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
2087 n * sizeof(int));
2088 } else {
2089 codeToGID = NULL;
2090 n = 0;
2092 if (!(fontFile = fontEngine->loadOpenTypeCFFFont(
2094 fontsrc,
2095 codeToGID, n))) {
2096 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2097 gfxFont->getName() ? gfxFont->getName()->getCString()
2098 : "(unnamed)");
2099 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2100 goto err2;
2102 break;
2103 case fontCIDType2:
2104 case fontCIDType2OT:
2105 codeToGID = NULL;
2106 n = 0;
2107 if (((GfxCIDFont *)gfxFont)->getCIDToGID()) {
2108 n = ((GfxCIDFont *)gfxFont)->getCIDToGIDLen();
2109 if (n) {
2110 codeToGID = (int *)gmallocn(n, sizeof(int));
2111 memcpy(codeToGID, ((GfxCIDFont *)gfxFont)->getCIDToGID(),
2112 n * sizeof(int));
2114 } else {
2115 if (fileName)
2116 ff = FoFiTrueType::load(fileName->getCString());
2117 else
2118 ff = FoFiTrueType::make(tmpBuf, tmpBufLen);
2119 if (! ff)
2121 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2122 gfxFont->getName() ? gfxFont->getName()->getCString()
2123 : "(unnamed)");
2124 goto err2;
2126 codeToGID = ((GfxCIDFont *)gfxFont)->getCodeToGIDMap(ff, &n);
2127 delete ff;
2129 if (!(fontFile = fontEngine->loadTrueTypeFont(
2131 fontsrc,
2132 codeToGID, n, faceIndex))) {
2133 error(errSyntaxError, -1, "Couldn't create a font for '{0:s}'",
2134 gfxFont->getName() ? gfxFont->getName()->getCString()
2135 : "(unnamed)");
2136 if (gfxFont->invalidateEmbeddedFont()) goto reload;
2137 goto err2;
2139 break;
2140 default:
2141 // this shouldn't happen
2142 goto err2;
2144 fontFile->doAdjustMatrix = doAdjustFontMatrix;
2147 // get the font matrix
2148 textMat = state->getTextMat();
2149 fontSize = state->getFontSize();
2150 m11 = textMat[0] * fontSize * state->getHorizScaling();
2151 m12 = textMat[1] * fontSize * state->getHorizScaling();
2152 m21 = textMat[2] * fontSize;
2153 m22 = textMat[3] * fontSize;
2155 // create the scaled font
2156 mat[0] = m11; mat[1] = m12;
2157 mat[2] = m21; mat[3] = m22;
2158 font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
2160 // for substituted fonts: adjust the font matrix -- compare the
2161 // width of 'm' in the original font and the substituted font
2162 if (fontFile->doAdjustMatrix && !gfxFont->isCIDFont()) {
2163 double w1, w2;
2164 CharCode code;
2165 char *name;
2166 for (code = 0; code < 256; ++code) {
2167 if ((name = ((Gfx8BitFont *)gfxFont)->getCharName(code)) &&
2168 name[0] == 'm' && name[1] == '\0') {
2169 break;
2172 if (code < 256) {
2173 w1 = ((Gfx8BitFont *)gfxFont)->getWidth(code);
2174 w2 = font->getGlyphAdvance(code);
2175 if (!gfxFont->isSymbolic() && w2 > 0) {
2176 // if real font is substantially narrower than substituted
2177 // font, reduce the font size accordingly
2178 if (w1 > 0.01 && w1 < 0.9 * w2) {
2179 w1 /= w2;
2180 m11 *= w1;
2181 m21 *= w1;
2182 recreateFont = gTrue;
2188 if (recreateFont)
2190 mat[0] = m11; mat[1] = m12;
2191 mat[2] = m21; mat[3] = m22;
2192 font = fontEngine->getFont(fontFile, mat, splash->getMatrix());
2195 delete fontLoc;
2196 if (fontsrc && !fontsrc->isFile)
2197 fontsrc->unref();
2198 return;
2200 err2:
2201 delete id;
2202 delete fontLoc;
2203 err1:
2204 if (fontsrc && !fontsrc->isFile)
2205 fontsrc->unref();
2206 return;
2209 void SplashOutputDev::stroke(GfxState *state) {
2210 SplashPath *path;
2212 if (state->getStrokeColorSpace()->isNonMarking()) {
2213 return;
2215 setOverprintMask(state->getStrokeColorSpace(), state->getStrokeOverprint(),
2216 state->getOverprintMode(), state->getStrokeColor());
2217 path = convertPath(state, state->getPath(), gFalse);
2218 splash->stroke(path);
2219 delete path;
2222 void SplashOutputDev::fill(GfxState *state) {
2223 SplashPath *path;
2225 if (state->getFillColorSpace()->isNonMarking()) {
2226 return;
2228 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2229 state->getOverprintMode(), state->getFillColor());
2230 path = convertPath(state, state->getPath(), gTrue);
2231 splash->fill(path, gFalse);
2232 delete path;
2235 void SplashOutputDev::eoFill(GfxState *state) {
2236 SplashPath *path;
2238 if (state->getFillColorSpace()->isNonMarking()) {
2239 return;
2241 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2242 state->getOverprintMode(), state->getFillColor());
2243 path = convertPath(state, state->getPath(), gTrue);
2244 splash->fill(path, gTrue);
2245 delete path;
2248 void SplashOutputDev::clip(GfxState *state) {
2249 SplashPath *path;
2251 path = convertPath(state, state->getPath(), gTrue);
2252 splash->clipToPath(path, gFalse);
2253 delete path;
2256 void SplashOutputDev::eoClip(GfxState *state) {
2257 SplashPath *path;
2259 path = convertPath(state, state->getPath(), gTrue);
2260 splash->clipToPath(path, gTrue);
2261 delete path;
2264 void SplashOutputDev::clipToStrokePath(GfxState *state) {
2265 SplashPath *path, *path2;
2267 path = convertPath(state, state->getPath(), gFalse);
2268 path2 = splash->makeStrokePath(path, state->getLineWidth());
2269 delete path;
2270 splash->clipToPath(path2, gFalse);
2271 delete path2;
2274 SplashPath *SplashOutputDev::convertPath(GfxState *state, GfxPath *path,
2275 GBool dropEmptySubpaths) {
2276 SplashPath *sPath;
2277 GfxSubpath *subpath;
2278 int n, i, j;
2280 n = dropEmptySubpaths ? 1 : 0;
2281 sPath = new SplashPath();
2282 for (i = 0; i < path->getNumSubpaths(); ++i) {
2283 subpath = path->getSubpath(i);
2284 if (subpath->getNumPoints() > n) {
2285 sPath->moveTo((SplashCoord)subpath->getX(0),
2286 (SplashCoord)subpath->getY(0));
2287 j = 1;
2288 while (j < subpath->getNumPoints()) {
2289 if (subpath->getCurve(j)) {
2290 sPath->curveTo((SplashCoord)subpath->getX(j),
2291 (SplashCoord)subpath->getY(j),
2292 (SplashCoord)subpath->getX(j+1),
2293 (SplashCoord)subpath->getY(j+1),
2294 (SplashCoord)subpath->getX(j+2),
2295 (SplashCoord)subpath->getY(j+2));
2296 j += 3;
2297 } else {
2298 sPath->lineTo((SplashCoord)subpath->getX(j),
2299 (SplashCoord)subpath->getY(j));
2300 ++j;
2303 if (subpath->isClosed()) {
2304 sPath->close();
2308 return sPath;
2311 void SplashOutputDev::drawChar(GfxState *state, double x, double y,
2312 double dx, double dy,
2313 double originX, double originY,
2314 CharCode code, int nBytes,
2315 Unicode *u, int uLen) {
2316 SplashPath *path;
2317 int render;
2318 GBool doFill, doStroke, doClip, strokeAdjust;
2319 double m[4];
2320 GBool horiz;
2322 if (skipHorizText || skipRotatedText) {
2323 state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2324 horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2325 fabs(m[2]) < 0.001 && m[3] < 0;
2326 if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2327 return;
2331 // check for invisible text -- this is used by Acrobat Capture
2332 render = state->getRender();
2333 if (render == 3) {
2334 return;
2337 if (needFontUpdate) {
2338 doUpdateFont(state);
2340 if (!font) {
2341 return;
2344 x -= originX;
2345 y -= originY;
2347 doFill = !(render & 1) && !state->getFillColorSpace()->isNonMarking();
2348 doStroke = ((render & 3) == 1 || (render & 3) == 2) &&
2349 !state->getStrokeColorSpace()->isNonMarking();
2350 doClip = render & 4;
2352 path = NULL;
2353 if (doStroke || doClip) {
2354 if ((path = font->getGlyphPath(code))) {
2355 path->offset((SplashCoord)x, (SplashCoord)y);
2359 // don't use stroke adjustment when stroking text -- the results
2360 // tend to be ugly (because characters with horizontal upper or
2361 // lower edges get misaligned relative to the other characters)
2362 strokeAdjust = gFalse; // make gcc happy
2363 if (doStroke) {
2364 strokeAdjust = splash->getStrokeAdjust();
2365 splash->setStrokeAdjust(gFalse);
2368 // fill and stroke
2369 if (doFill && doStroke) {
2370 if (path) {
2371 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2372 state->getOverprintMode(), state->getFillColor());
2373 splash->fill(path, gFalse);
2374 setOverprintMask(state->getStrokeColorSpace(),
2375 state->getStrokeOverprint(),
2376 state->getOverprintMode(),
2377 state->getStrokeColor());
2378 splash->stroke(path);
2381 // fill
2382 } else if (doFill) {
2383 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2384 state->getOverprintMode(), state->getFillColor());
2385 splash->fillChar((SplashCoord)x, (SplashCoord)y, code, font);
2387 // stroke
2388 } else if (doStroke) {
2389 if (path) {
2390 setOverprintMask(state->getStrokeColorSpace(),
2391 state->getStrokeOverprint(),
2392 state->getOverprintMode(),
2393 state->getStrokeColor());
2394 splash->stroke(path);
2398 // clip
2399 if (doClip) {
2400 if (path) {
2401 if (textClipPath) {
2402 textClipPath->append(path);
2403 } else {
2404 textClipPath = path;
2405 path = NULL;
2410 if (doStroke) {
2411 splash->setStrokeAdjust(strokeAdjust);
2414 if (path) {
2415 delete path;
2419 GBool SplashOutputDev::beginType3Char(GfxState *state, double x, double y,
2420 double dx, double dy,
2421 CharCode code, Unicode *u, int uLen) {
2422 GfxFont *gfxFont;
2423 Ref *fontID;
2424 double *ctm, *bbox;
2425 T3FontCache *t3Font;
2426 T3GlyphStack *t3gs;
2427 GBool validBBox;
2428 double m[4];
2429 GBool horiz;
2430 double x1, y1, xMin, yMin, xMax, yMax, xt, yt;
2431 int i, j;
2433 if (skipHorizText || skipRotatedText) {
2434 state->getFontTransMat(&m[0], &m[1], &m[2], &m[3]);
2435 horiz = m[0] > 0 && fabs(m[1]) < 0.001 &&
2436 fabs(m[2]) < 0.001 && m[3] < 0;
2437 if ((skipHorizText && horiz) || (skipRotatedText && !horiz)) {
2438 return gTrue;
2442 if (!(gfxFont = state->getFont())) {
2443 return gFalse;
2445 fontID = gfxFont->getID();
2446 ctm = state->getCTM();
2447 state->transform(0, 0, &xt, &yt);
2449 // is it the first (MRU) font in the cache?
2450 if (!(nT3Fonts > 0 &&
2451 t3FontCache[0]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3]))) {
2453 // is the font elsewhere in the cache?
2454 for (i = 1; i < nT3Fonts; ++i) {
2455 if (t3FontCache[i]->matches(fontID, ctm[0], ctm[1], ctm[2], ctm[3])) {
2456 t3Font = t3FontCache[i];
2457 for (j = i; j > 0; --j) {
2458 t3FontCache[j] = t3FontCache[j - 1];
2460 t3FontCache[0] = t3Font;
2461 break;
2464 if (i >= nT3Fonts) {
2466 // create new entry in the font cache
2467 if (nT3Fonts == splashOutT3FontCacheSize) {
2468 t3gs = t3GlyphStack;
2469 while (t3gs != NULL) {
2470 if (t3gs->cache == t3FontCache[nT3Fonts - 1]) {
2471 error(errSyntaxWarning, -1, "t3FontCache reaches limit but font still on stack in SplashOutputDev::beginType3Char");
2472 return gTrue;
2474 t3gs = t3gs->next;
2476 delete t3FontCache[nT3Fonts - 1];
2477 --nT3Fonts;
2479 for (j = nT3Fonts; j > 0; --j) {
2480 t3FontCache[j] = t3FontCache[j - 1];
2482 ++nT3Fonts;
2483 bbox = gfxFont->getFontBBox();
2484 if (bbox[0] == 0 && bbox[1] == 0 && bbox[2] == 0 && bbox[3] == 0) {
2485 // unspecified bounding box -- just take a guess
2486 xMin = xt - 5;
2487 xMax = xMin + 30;
2488 yMax = yt + 15;
2489 yMin = yMax - 45;
2490 validBBox = gFalse;
2491 } else {
2492 state->transform(bbox[0], bbox[1], &x1, &y1);
2493 xMin = xMax = x1;
2494 yMin = yMax = y1;
2495 state->transform(bbox[0], 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 state->transform(bbox[2], bbox[1], &x1, &y1);
2507 if (x1 < xMin) {
2508 xMin = x1;
2509 } else if (x1 > xMax) {
2510 xMax = x1;
2512 if (y1 < yMin) {
2513 yMin = y1;
2514 } else if (y1 > yMax) {
2515 yMax = y1;
2517 state->transform(bbox[2], bbox[3], &x1, &y1);
2518 if (x1 < xMin) {
2519 xMin = x1;
2520 } else if (x1 > xMax) {
2521 xMax = x1;
2523 if (y1 < yMin) {
2524 yMin = y1;
2525 } else if (y1 > yMax) {
2526 yMax = y1;
2528 validBBox = gTrue;
2530 t3FontCache[0] = new T3FontCache(fontID, ctm[0], ctm[1], ctm[2], ctm[3],
2531 (int)floor(xMin - xt) - 2,
2532 (int)floor(yMin - yt) - 2,
2533 (int)ceil(xMax) - (int)floor(xMin) + 4,
2534 (int)ceil(yMax) - (int)floor(yMin) + 4,
2535 validBBox,
2536 colorMode != splashModeMono1);
2539 t3Font = t3FontCache[0];
2541 // is the glyph in the cache?
2542 i = (code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2543 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2544 if (t3Font->cacheTags != NULL) {
2545 if ((t3Font->cacheTags[i+j].mru & 0x8000) &&
2546 t3Font->cacheTags[i+j].code == code) {
2547 drawType3Glyph(state, t3Font, &t3Font->cacheTags[i+j],
2548 t3Font->cacheData + (i+j) * t3Font->glyphSize);
2549 return gTrue;
2554 // push a new Type 3 glyph record
2555 t3gs = new T3GlyphStack();
2556 t3gs->next = t3GlyphStack;
2557 t3GlyphStack = t3gs;
2558 t3GlyphStack->code = code;
2559 t3GlyphStack->cache = t3Font;
2560 t3GlyphStack->cacheTag = NULL;
2561 t3GlyphStack->cacheData = NULL;
2563 haveT3Dx = gFalse;
2565 return gFalse;
2568 void SplashOutputDev::endType3Char(GfxState *state) {
2569 T3GlyphStack *t3gs;
2570 double *ctm;
2572 if (t3GlyphStack->cacheTag) {
2573 --nestCount;
2574 memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
2575 t3GlyphStack->cache->glyphSize);
2576 delete bitmap;
2577 delete splash;
2578 bitmap = t3GlyphStack->origBitmap;
2579 splash = t3GlyphStack->origSplash;
2580 ctm = state->getCTM();
2581 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2582 t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
2583 updateCTM(state, 0, 0, 0, 0, 0, 0);
2584 drawType3Glyph(state, t3GlyphStack->cache,
2585 t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
2587 t3gs = t3GlyphStack;
2588 t3GlyphStack = t3gs->next;
2589 delete t3gs;
2592 void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
2593 haveT3Dx = gTrue;
2596 void SplashOutputDev::type3D1(GfxState *state, double wx, double wy,
2597 double llx, double lly, double urx, double ury) {
2598 double *ctm;
2599 T3FontCache *t3Font;
2600 SplashColor color;
2601 double xt, yt, xMin, xMax, yMin, yMax, x1, y1;
2602 int i, j;
2604 // ignore multiple d0/d1 operators
2605 if (haveT3Dx) {
2606 return;
2608 haveT3Dx = gTrue;
2610 if (unlikely(t3GlyphStack == NULL)) {
2611 error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D1");
2612 return;
2615 if (unlikely(t3GlyphStack->origBitmap != NULL)) {
2616 error(errSyntaxWarning, -1, "t3GlyphStack origBitmap was not null in SplashOutputDev::type3D1");
2617 return;
2620 if (unlikely(t3GlyphStack->origSplash != NULL)) {
2621 error(errSyntaxWarning, -1, "t3GlyphStack origSplash was not null in SplashOutputDev::type3D1");
2622 return;
2625 t3Font = t3GlyphStack->cache;
2627 // check for a valid bbox
2628 state->transform(0, 0, &xt, &yt);
2629 state->transform(llx, lly, &x1, &y1);
2630 xMin = xMax = x1;
2631 yMin = yMax = y1;
2632 state->transform(llx, 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 state->transform(urx, lly, &x1, &y1);
2644 if (x1 < xMin) {
2645 xMin = x1;
2646 } else if (x1 > xMax) {
2647 xMax = x1;
2649 if (y1 < yMin) {
2650 yMin = y1;
2651 } else if (y1 > yMax) {
2652 yMax = y1;
2654 state->transform(urx, ury, &x1, &y1);
2655 if (x1 < xMin) {
2656 xMin = x1;
2657 } else if (x1 > xMax) {
2658 xMax = x1;
2660 if (y1 < yMin) {
2661 yMin = y1;
2662 } else if (y1 > yMax) {
2663 yMax = y1;
2665 if (xMin - xt < t3Font->glyphX ||
2666 yMin - yt < t3Font->glyphY ||
2667 xMax - xt > t3Font->glyphX + t3Font->glyphW ||
2668 yMax - yt > t3Font->glyphY + t3Font->glyphH) {
2669 if (t3Font->validBBox) {
2670 error(errSyntaxWarning, -1, "Bad bounding box in Type 3 glyph");
2672 return;
2675 if (t3Font->cacheTags == NULL)
2676 return;
2678 // allocate a cache entry
2679 i = (t3GlyphStack->code & (t3Font->cacheSets - 1)) * t3Font->cacheAssoc;
2680 for (j = 0; j < t3Font->cacheAssoc; ++j) {
2681 if ((t3Font->cacheTags[i+j].mru & 0x7fff) == t3Font->cacheAssoc - 1) {
2682 t3Font->cacheTags[i+j].mru = 0x8000;
2683 t3Font->cacheTags[i+j].code = t3GlyphStack->code;
2684 t3GlyphStack->cacheTag = &t3Font->cacheTags[i+j];
2685 t3GlyphStack->cacheData = t3Font->cacheData + (i+j) * t3Font->glyphSize;
2686 } else {
2687 ++t3Font->cacheTags[i+j].mru;
2691 // save state
2692 t3GlyphStack->origBitmap = bitmap;
2693 t3GlyphStack->origSplash = splash;
2694 ctm = state->getCTM();
2695 t3GlyphStack->origCTM4 = ctm[4];
2696 t3GlyphStack->origCTM5 = ctm[5];
2698 // create the temporary bitmap
2699 if (colorMode == splashModeMono1) {
2700 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2701 splashModeMono1, gFalse);
2702 splash = new Splash(bitmap, gFalse,
2703 t3GlyphStack->origSplash->getScreen());
2704 color[0] = 0;
2705 splash->clear(color);
2706 color[0] = 0xff;
2707 } else {
2708 bitmap = new SplashBitmap(t3Font->glyphW, t3Font->glyphH, 1,
2709 splashModeMono8, gFalse);
2710 splash = new Splash(bitmap, vectorAntialias,
2711 t3GlyphStack->origSplash->getScreen());
2712 color[0] = 0x00;
2713 splash->clear(color);
2714 color[0] = 0xff;
2716 splash->setMinLineWidth(globalParams->getMinLineWidth());
2717 splash->setThinLineMode(splashThinLineDefault);
2718 splash->setFillPattern(new SplashSolidColor(color));
2719 splash->setStrokePattern(new SplashSolidColor(color));
2720 //~ this should copy other state from t3GlyphStack->origSplash?
2721 state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
2722 -t3Font->glyphX, -t3Font->glyphY);
2723 updateCTM(state, 0, 0, 0, 0, 0, 0);
2724 ++nestCount;
2727 void SplashOutputDev::drawType3Glyph(GfxState *state, T3FontCache *t3Font,
2728 T3FontCacheTag * /*tag*/, Guchar *data) {
2729 SplashGlyphBitmap glyph;
2731 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2732 state->getOverprintMode(), state->getFillColor());
2733 glyph.x = -t3Font->glyphX;
2734 glyph.y = -t3Font->glyphY;
2735 glyph.w = t3Font->glyphW;
2736 glyph.h = t3Font->glyphH;
2737 glyph.aa = colorMode != splashModeMono1;
2738 glyph.data = data;
2739 glyph.freeData = gFalse;
2740 splash->fillGlyph(0, 0, &glyph);
2743 void SplashOutputDev::beginTextObject(GfxState *state) {
2746 void SplashOutputDev::endTextObject(GfxState *state) {
2747 if (textClipPath) {
2748 splash->clipToPath(textClipPath, gFalse);
2749 delete textClipPath;
2750 textClipPath = NULL;
2754 struct SplashOutImageMaskData {
2755 ImageStream *imgStr;
2756 GBool invert;
2757 int width, height, y;
2760 GBool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
2761 SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
2762 Guchar *p;
2763 SplashColorPtr q;
2764 int x;
2766 if (imgMaskData->y == imgMaskData->height) {
2767 return gFalse;
2769 if (!(p = imgMaskData->imgStr->getLine())) {
2770 return gFalse;
2772 for (x = 0, q = line; x < imgMaskData->width; ++x) {
2773 *q++ = *p++ ^ imgMaskData->invert;
2775 ++imgMaskData->y;
2776 return gTrue;
2779 void SplashOutputDev::drawImageMask(GfxState *state, Object *ref, Stream *str,
2780 int width, int height, GBool invert,
2781 GBool interpolate, GBool inlineImg) {
2782 double *ctm;
2783 SplashCoord mat[6];
2784 SplashOutImageMaskData imgMaskData;
2786 if (state->getFillColorSpace()->isNonMarking()) {
2787 return;
2789 setOverprintMask(state->getFillColorSpace(), state->getFillOverprint(),
2790 state->getOverprintMode(), state->getFillColor());
2792 ctm = state->getCTM();
2793 for (int i = 0; i < 6; ++i) {
2794 if (!isfinite(ctm[i])) return;
2796 mat[0] = ctm[0];
2797 mat[1] = ctm[1];
2798 mat[2] = -ctm[2];
2799 mat[3] = -ctm[3];
2800 mat[4] = ctm[2] + ctm[4];
2801 mat[5] = ctm[3] + ctm[5];
2803 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2804 imgMaskData.imgStr->reset();
2805 imgMaskData.invert = invert ? 0 : 1;
2806 imgMaskData.width = width;
2807 imgMaskData.height = height;
2808 imgMaskData.y = 0;
2810 splash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2811 if (inlineImg) {
2812 while (imgMaskData.y < height) {
2813 imgMaskData.imgStr->getLine();
2814 ++imgMaskData.y;
2818 delete imgMaskData.imgStr;
2819 str->close();
2822 void SplashOutputDev::setSoftMaskFromImageMask(GfxState *state,
2823 Object *ref, Stream *str,
2824 int width, int height,
2825 GBool invert,
2826 GBool inlineImg, double *baseMatrix) {
2827 double *ctm;
2828 SplashCoord mat[6];
2829 SplashOutImageMaskData imgMaskData;
2830 Splash *maskSplash;
2831 SplashColor maskColor;
2832 double bbox[4] = {0, 0, 1, 1}; // default;
2834 if (state->getFillColorSpace()->isNonMarking()) {
2835 return;
2838 ctm = state->getCTM();
2839 for (int i = 0; i < 6; ++i) {
2840 if (!isfinite(ctm[i])) return;
2843 beginTransparencyGroup(state, bbox, NULL, gFalse, gFalse, gFalse);
2844 baseMatrix[4] -= transpGroupStack->tx;
2845 baseMatrix[5] -= transpGroupStack->ty;
2847 ctm = state->getCTM();
2848 mat[0] = ctm[0];
2849 mat[1] = ctm[1];
2850 mat[2] = -ctm[2];
2851 mat[3] = -ctm[3];
2852 mat[4] = ctm[2] + ctm[4];
2853 mat[5] = ctm[3] + ctm[5];
2854 imgMaskData.imgStr = new ImageStream(str, width, 1, 1);
2855 imgMaskData.imgStr->reset();
2856 imgMaskData.invert = invert ? 0 : 1;
2857 imgMaskData.width = width;
2858 imgMaskData.height = height;
2859 imgMaskData.y = 0;
2861 transpGroupStack->softmask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), 1, splashModeMono8, gFalse);
2862 maskSplash = new Splash(transpGroupStack->softmask, vectorAntialias);
2863 maskColor[0] = 0;
2864 maskSplash->clear(maskColor);
2865 maskColor[0] = 0xff;
2866 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
2867 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData, width, height, mat, t3GlyphStack != NULL);
2868 delete maskSplash;
2869 delete imgMaskData.imgStr;
2870 str->close();
2873 void SplashOutputDev::unsetSoftMaskFromImageMask(GfxState *state, double *baseMatrix) {
2874 double bbox[4] = {0,0,1,1}; // dummy
2876 /* transfer mask to alpha channel! */
2877 // memcpy(maskBitmap->getAlphaPtr(), maskBitmap->getDataPtr(), bitmap->getRowSize() * bitmap->getHeight());
2878 // memset(maskBitmap->getDataPtr(), 0, bitmap->getRowSize() * bitmap->getHeight());
2879 if (transpGroupStack->softmask != NULL) {
2880 Guchar *dest = bitmap->getAlphaPtr();
2881 Guchar *src = transpGroupStack->softmask->getDataPtr();
2882 for (int c= 0; c < transpGroupStack->softmask->getRowSize() * transpGroupStack->softmask->getHeight(); c++) {
2883 dest[c] = src[c];
2885 delete transpGroupStack->softmask;
2886 transpGroupStack->softmask = NULL;
2888 endTransparencyGroup(state);
2889 baseMatrix[4] += transpGroupStack->tx;
2890 baseMatrix[5] += transpGroupStack->ty;
2891 paintTransparencyGroup(state, bbox);
2894 struct SplashOutImageData {
2895 ImageStream *imgStr;
2896 GfxImageColorMap *colorMap;
2897 SplashColorPtr lookup;
2898 int *maskColors;
2899 SplashColorMode colorMode;
2900 int width, height, y;
2903 #ifdef USE_CMS
2904 GBool SplashOutputDev::useIccImageSrc(void *data) {
2905 SplashOutImageData *imgData = (SplashOutImageData *)data;
2907 if (!imgData->lookup && imgData->colorMap->getColorSpace()->getMode() == csICCBased) {
2908 GfxICCBasedColorSpace *colorSpace = (GfxICCBasedColorSpace *) imgData->colorMap->getColorSpace();
2909 switch (imgData->colorMode) {
2910 case splashModeMono1:
2911 case splashModeMono8:
2912 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceGray)
2913 return gTrue;
2914 break;
2915 case splashModeXBGR8:
2916 case splashModeRGB8:
2917 case splashModeBGR8:
2918 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceRGB)
2919 return gTrue;
2920 break;
2921 #if SPLASH_CMYK
2922 case splashModeCMYK8:
2923 if (colorSpace->getAlt() != NULL && colorSpace->getAlt()->getMode() == csDeviceCMYK)
2924 return gTrue;
2925 break;
2926 #endif
2930 return gFalse;
2932 #endif
2934 GBool SplashOutputDev::imageSrc(void *data, SplashColorPtr colorLine,
2935 Guchar * /*alphaLine*/) {
2936 SplashOutImageData *imgData = (SplashOutImageData *)data;
2937 Guchar *p;
2938 SplashColorPtr q, col;
2939 GfxRGB rgb;
2940 GfxGray gray;
2941 #if SPLASH_CMYK
2942 GfxCMYK cmyk;
2943 GfxColor deviceN;
2944 #endif
2945 int nComps, x;
2947 if (imgData->y == imgData->height) {
2948 return gFalse;
2950 if (!(p = imgData->imgStr->getLine())) {
2951 int destComps = 1;
2952 if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8)
2953 destComps = 3;
2954 else if (imgData->colorMode == splashModeXBGR8)
2955 destComps = 4;
2956 #if SPLASH_CMYK
2957 else if (imgData->colorMode == splashModeCMYK8)
2958 destComps = 4;
2959 else if (imgData->colorMode == splashModeDeviceN8)
2960 destComps = SPOT_NCOMPS + 4;
2961 #endif
2962 memset(colorLine, 0, imgData->width * destComps);
2963 return gFalse;
2966 nComps = imgData->colorMap->getNumPixelComps();
2968 if (imgData->lookup) {
2969 switch (imgData->colorMode) {
2970 case splashModeMono1:
2971 case splashModeMono8:
2972 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2973 *q++ = imgData->lookup[*p];
2975 break;
2976 case splashModeRGB8:
2977 case splashModeBGR8:
2978 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2979 col = &imgData->lookup[3 * *p];
2980 *q++ = col[0];
2981 *q++ = col[1];
2982 *q++ = col[2];
2984 break;
2985 case splashModeXBGR8:
2986 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2987 col = &imgData->lookup[4 * *p];
2988 *q++ = col[0];
2989 *q++ = col[1];
2990 *q++ = col[2];
2991 *q++ = col[3];
2993 break;
2994 #if SPLASH_CMYK
2995 case splashModeCMYK8:
2996 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
2997 col = &imgData->lookup[4 * *p];
2998 *q++ = col[0];
2999 *q++ = col[1];
3000 *q++ = col[2];
3001 *q++ = col[3];
3003 break;
3004 case splashModeDeviceN8:
3005 for (x = 0, q = colorLine; x < imgData->width; ++x, ++p) {
3006 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
3007 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3008 *q++ = col[cp];
3010 break;
3011 #endif
3013 } else {
3014 switch (imgData->colorMode) {
3015 case splashModeMono1:
3016 case splashModeMono8:
3017 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3018 imgData->colorMap->getGray(p, &gray);
3019 *q++ = colToByte(gray);
3021 break;
3022 case splashModeRGB8:
3023 case splashModeBGR8:
3024 if (imgData->colorMap->useRGBLine()) {
3025 imgData->colorMap->getRGBLine(p, (Guchar *) colorLine, imgData->width);
3026 } else {
3027 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3028 imgData->colorMap->getRGB(p, &rgb);
3029 *q++ = colToByte(rgb.r);
3030 *q++ = colToByte(rgb.g);
3031 *q++ = colToByte(rgb.b);
3034 break;
3035 case splashModeXBGR8:
3036 if (imgData->colorMap->useRGBLine()) {
3037 imgData->colorMap->getRGBXLine(p, (Guchar *) colorLine, imgData->width);
3038 } else {
3039 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3040 imgData->colorMap->getRGB(p, &rgb);
3041 *q++ = colToByte(rgb.r);
3042 *q++ = colToByte(rgb.g);
3043 *q++ = colToByte(rgb.b);
3044 *q++ = 255;
3047 break;
3048 #if SPLASH_CMYK
3049 case splashModeCMYK8:
3050 if (imgData->colorMap->useCMYKLine()) {
3051 imgData->colorMap->getCMYKLine(p, (Guchar *) colorLine, imgData->width);
3052 } else {
3053 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3054 imgData->colorMap->getCMYK(p, &cmyk);
3055 *q++ = colToByte(cmyk.c);
3056 *q++ = colToByte(cmyk.m);
3057 *q++ = colToByte(cmyk.y);
3058 *q++ = colToByte(cmyk.k);
3061 break;
3062 case splashModeDeviceN8:
3063 if (imgData->colorMap->useDeviceNLine()) {
3064 imgData->colorMap->getDeviceNLine(p, (Guchar *) colorLine, imgData->width);
3065 } else {
3066 for (x = 0, q = colorLine; x < imgData->width; ++x, p += nComps) {
3067 imgData->colorMap->getDeviceN(p, &deviceN);
3068 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3069 *q++ = colToByte(deviceN.c[cp]);
3072 break;
3073 #endif
3077 ++imgData->y;
3078 return gTrue;
3081 #ifdef USE_CMS
3082 GBool SplashOutputDev::iccImageSrc(void *data, SplashColorPtr colorLine,
3083 Guchar * /*alphaLine*/) {
3084 SplashOutImageData *imgData = (SplashOutImageData *)data;
3085 Guchar *p;
3086 int nComps;
3088 if (imgData->y == imgData->height) {
3089 return gFalse;
3091 if (!(p = imgData->imgStr->getLine())) {
3092 int destComps = 1;
3093 if (imgData->colorMode == splashModeRGB8 || imgData->colorMode == splashModeBGR8)
3094 destComps = 3;
3095 else if (imgData->colorMode == splashModeXBGR8)
3096 destComps = 4;
3097 #if SPLASH_CMYK
3098 else if (imgData->colorMode == splashModeCMYK8)
3099 destComps = 4;
3100 else if (imgData->colorMode == splashModeDeviceN8)
3101 destComps = SPOT_NCOMPS + 4;
3102 #endif
3103 memset(colorLine, 0, imgData->width * destComps);
3104 return gFalse;
3107 if (imgData->colorMode == splashModeXBGR8) {
3108 SplashColorPtr q;
3109 int x;
3110 for (x = 0, q = colorLine; x < imgData->width; ++x) {
3111 *q++ = *p++;
3112 *q++ = *p++;
3113 *q++ = *p++;
3114 *q++ = 255;
3116 } else {
3117 nComps = imgData->colorMap->getNumPixelComps();
3118 memcpy(colorLine, p, imgData->width * nComps);
3121 ++imgData->y;
3122 return gTrue;
3125 void SplashOutputDev::iccTransform(void *data, SplashBitmap *bitmap) {
3126 SplashOutImageData *imgData = (SplashOutImageData *)data;
3127 int nComps = imgData->colorMap->getNumPixelComps();
3129 Guchar *colorLine = (Guchar *) gmalloc(nComps * bitmap->getWidth());
3130 Guchar *rgbxLine = (imgData->colorMode == splashModeXBGR8) ? (Guchar *) gmalloc(3 * bitmap->getWidth()) : NULL;
3131 for (int i = 0; i < bitmap->getHeight(); i++) {
3132 Guchar *p = bitmap->getDataPtr() + i * bitmap->getRowSize();
3133 switch (imgData->colorMode) {
3134 case splashModeMono1:
3135 case splashModeMono8:
3136 imgData->colorMap->getGrayLine(p, colorLine, bitmap->getWidth());
3137 memcpy(p, colorLine, nComps * bitmap->getWidth());
3138 break;
3139 case splashModeRGB8:
3140 case splashModeBGR8:
3141 imgData->colorMap->getRGBLine(p, colorLine, bitmap->getWidth());
3142 memcpy(p, colorLine, nComps * bitmap->getWidth());
3143 break;
3144 #if SPLASH_CMYK
3145 case splashModeCMYK8:
3146 imgData->colorMap->getCMYKLine(p, colorLine, bitmap->getWidth());
3147 memcpy(p, colorLine, nComps * bitmap->getWidth());
3148 break;
3149 #endif
3150 case splashModeXBGR8:
3151 Guchar *q;
3152 Guchar *b = p;
3153 int x;
3154 for (x = 0, q = rgbxLine; x < bitmap->getWidth(); ++x, ++b) {
3155 *q++ = *b++;
3156 *q++ = *b++;
3157 *q++ = *b++;
3159 imgData->colorMap->getRGBLine(rgbxLine, colorLine, bitmap->getWidth());
3160 b = p;
3161 for (x = 0, q = colorLine; x < bitmap->getWidth(); ++x, ++b) {
3162 *b++ = *q++;
3163 *b++ = *q++;
3164 *b++ = *q++;
3166 break;
3169 gfree(colorLine);
3170 if (rgbxLine != NULL)
3171 gfree(rgbxLine);
3173 #endif
3175 GBool SplashOutputDev::alphaImageSrc(void *data, SplashColorPtr colorLine,
3176 Guchar *alphaLine) {
3177 SplashOutImageData *imgData = (SplashOutImageData *)data;
3178 Guchar *p, *aq;
3179 SplashColorPtr q, col;
3180 GfxRGB rgb;
3181 GfxGray gray;
3182 #if SPLASH_CMYK
3183 GfxCMYK cmyk;
3184 GfxColor deviceN;
3185 #endif
3186 Guchar alpha;
3187 int nComps, x, i;
3189 if (imgData->y == imgData->height) {
3190 return gFalse;
3192 if (!(p = imgData->imgStr->getLine())) {
3193 return gFalse;
3196 nComps = imgData->colorMap->getNumPixelComps();
3198 for (x = 0, q = colorLine, aq = alphaLine;
3199 x < imgData->width;
3200 ++x, p += nComps) {
3201 alpha = 0;
3202 for (i = 0; i < nComps; ++i) {
3203 if (p[i] < imgData->maskColors[2*i] ||
3204 p[i] > imgData->maskColors[2*i+1]) {
3205 alpha = 0xff;
3206 break;
3209 if (imgData->lookup) {
3210 switch (imgData->colorMode) {
3211 case splashModeMono1:
3212 case splashModeMono8:
3213 *q++ = imgData->lookup[*p];
3214 break;
3215 case splashModeRGB8:
3216 case splashModeBGR8:
3217 col = &imgData->lookup[3 * *p];
3218 *q++ = col[0];
3219 *q++ = col[1];
3220 *q++ = col[2];
3221 break;
3222 case splashModeXBGR8:
3223 col = &imgData->lookup[4 * *p];
3224 *q++ = col[0];
3225 *q++ = col[1];
3226 *q++ = col[2];
3227 *q++ = 255;
3228 break;
3229 #if SPLASH_CMYK
3230 case splashModeCMYK8:
3231 col = &imgData->lookup[4 * *p];
3232 *q++ = col[0];
3233 *q++ = col[1];
3234 *q++ = col[2];
3235 *q++ = col[3];
3236 break;
3237 case splashModeDeviceN8:
3238 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
3239 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3240 *q++ = col[cp];
3241 break;
3242 #endif
3244 *aq++ = alpha;
3245 } else {
3246 switch (imgData->colorMode) {
3247 case splashModeMono1:
3248 case splashModeMono8:
3249 imgData->colorMap->getGray(p, &gray);
3250 *q++ = colToByte(gray);
3251 break;
3252 case splashModeXBGR8:
3253 case splashModeRGB8:
3254 case splashModeBGR8:
3255 imgData->colorMap->getRGB(p, &rgb);
3256 *q++ = colToByte(rgb.r);
3257 *q++ = colToByte(rgb.g);
3258 *q++ = colToByte(rgb.b);
3259 if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
3260 break;
3261 #if SPLASH_CMYK
3262 case splashModeCMYK8:
3263 imgData->colorMap->getCMYK(p, &cmyk);
3264 *q++ = colToByte(cmyk.c);
3265 *q++ = colToByte(cmyk.m);
3266 *q++ = colToByte(cmyk.y);
3267 *q++ = colToByte(cmyk.k);
3268 break;
3269 case splashModeDeviceN8:
3270 imgData->colorMap->getDeviceN(p, &deviceN);
3271 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3272 *q++ = colToByte(deviceN.c[cp]);
3273 break;
3274 #endif
3276 *aq++ = alpha;
3280 ++imgData->y;
3281 return gTrue;
3284 struct TilingSplashOutBitmap {
3285 SplashBitmap *bitmap;
3286 SplashPattern *pattern;
3287 SplashColorMode colorMode;
3288 int paintType;
3289 int repeatX;
3290 int repeatY;
3291 int y;
3294 GBool SplashOutputDev::tilingBitmapSrc(void *data, SplashColorPtr colorLine,
3295 Guchar *alphaLine) {
3296 TilingSplashOutBitmap *imgData = (TilingSplashOutBitmap *)data;
3298 if (imgData->y == imgData->bitmap->getHeight()) {
3299 imgData->repeatY--;
3300 if (imgData->repeatY == 0)
3301 return gFalse;
3302 imgData->y = 0;
3305 if (imgData->paintType == 1) {
3306 const SplashColorMode cMode = imgData->bitmap->getMode();
3307 SplashColorPtr q = colorLine;
3308 // For splashModeBGR8 and splashModeXBGR8 we need to use getPixel
3309 // for the others we can use raw access
3310 if (cMode == splashModeBGR8 || cMode == splashModeXBGR8) {
3311 for (int m = 0; m < imgData->repeatX; m++) {
3312 for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
3313 imgData->bitmap->getPixel(x, imgData->y, q);
3314 q += splashColorModeNComps[cMode];
3317 } else {
3318 const int n = imgData->bitmap->getRowSize();
3319 SplashColorPtr p;
3320 for (int m = 0; m < imgData->repeatX; m++) {
3321 p = imgData->bitmap->getDataPtr() + imgData->y * imgData->bitmap->getRowSize();
3322 for (int x = 0; x < n; ++x) {
3323 *q++ = *p++;
3327 if (alphaLine != NULL) {
3328 SplashColorPtr aq = alphaLine;
3329 SplashColorPtr p;
3330 const int n = imgData->bitmap->getWidth() - 1;
3331 for (int m = 0; m < imgData->repeatX; m++) {
3332 p = imgData->bitmap->getAlphaPtr() + imgData->y * imgData->bitmap->getWidth();
3333 for (int x = 0; x < n; ++x) {
3334 *aq++ = *p++;
3336 // This is a hack, because of how Splash antialias works if we overwrite the
3337 // last alpha pixel of the tile most/all of the files look much better
3338 *aq++ = (n == 0) ? *p : *(p - 1);
3341 } else {
3342 SplashColor col, pat;
3343 SplashColorPtr dest = colorLine;
3344 for (int m = 0; m < imgData->repeatX; m++) {
3345 for (int x = 0; x < imgData->bitmap->getWidth(); x++) {
3346 imgData->bitmap->getPixel(x, imgData->y, col);
3347 imgData->pattern->getColor(x, imgData->y, pat);
3348 for (int i = 0; i < splashColorModeNComps[imgData->colorMode]; ++i) {
3349 #if SPLASH_CMYK
3350 if (imgData->colorMode == splashModeCMYK8 || imgData->colorMode == splashModeDeviceN8)
3351 dest[i] = div255(pat[i] * (255 - col[0]));
3352 else
3353 #endif
3354 dest[i] = 255 - div255((255 - pat[i]) * (255 - col[0]));
3356 dest += splashColorModeNComps[imgData->colorMode];
3359 if (alphaLine != NULL) {
3360 const int y = (imgData->y == imgData->bitmap->getHeight() - 1 && imgData->y > 50) ? imgData->y - 1 : imgData->y;
3361 SplashColorPtr aq = alphaLine;
3362 SplashColorPtr p;
3363 const int n = imgData->bitmap->getWidth();
3364 for (int m = 0; m < imgData->repeatX; m++) {
3365 p = imgData->bitmap->getAlphaPtr() + y * imgData->bitmap->getWidth();
3366 for (int x = 0; x < n; ++x) {
3367 *aq++ = *p++;
3372 ++imgData->y;
3373 return gTrue;
3376 void SplashOutputDev::drawImage(GfxState *state, Object *ref, Stream *str,
3377 int width, int height,
3378 GfxImageColorMap *colorMap,
3379 GBool interpolate,
3380 int *maskColors, GBool inlineImg) {
3381 double *ctm;
3382 SplashCoord mat[6];
3383 SplashOutImageData imgData;
3384 SplashColorMode srcMode;
3385 SplashImageSource src;
3386 SplashICCTransform tf;
3387 GfxGray gray;
3388 GfxRGB rgb;
3389 #if SPLASH_CMYK
3390 GfxCMYK cmyk;
3391 GBool grayIndexed = gFalse;
3392 GfxColor deviceN;
3393 #endif
3394 Guchar pix;
3395 int n, i;
3397 ctm = state->getCTM();
3398 for (i = 0; i < 6; ++i) {
3399 if (!isfinite(ctm[i])) return;
3401 mat[0] = ctm[0];
3402 mat[1] = ctm[1];
3403 mat[2] = -ctm[2];
3404 mat[3] = -ctm[3];
3405 mat[4] = ctm[2] + ctm[4];
3406 mat[5] = ctm[3] + ctm[5];
3408 imgData.imgStr = new ImageStream(str, width,
3409 colorMap->getNumPixelComps(),
3410 colorMap->getBits());
3411 imgData.imgStr->reset();
3412 imgData.colorMap = colorMap;
3413 imgData.maskColors = maskColors;
3414 imgData.colorMode = colorMode;
3415 imgData.width = width;
3416 imgData.height = height;
3417 imgData.y = 0;
3419 // special case for one-channel (monochrome/gray/separation) images:
3420 // build a lookup table here
3421 imgData.lookup = NULL;
3422 if (colorMap->getNumPixelComps() == 1) {
3423 n = 1 << colorMap->getBits();
3424 switch (colorMode) {
3425 case splashModeMono1:
3426 case splashModeMono8:
3427 imgData.lookup = (SplashColorPtr)gmalloc(n);
3428 for (i = 0; i < n; ++i) {
3429 pix = (Guchar)i;
3430 colorMap->getGray(&pix, &gray);
3431 imgData.lookup[i] = colToByte(gray);
3433 break;
3434 case splashModeRGB8:
3435 case splashModeBGR8:
3436 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3437 for (i = 0; i < n; ++i) {
3438 pix = (Guchar)i;
3439 colorMap->getRGB(&pix, &rgb);
3440 imgData.lookup[3*i] = colToByte(rgb.r);
3441 imgData.lookup[3*i+1] = colToByte(rgb.g);
3442 imgData.lookup[3*i+2] = colToByte(rgb.b);
3444 break;
3445 case splashModeXBGR8:
3446 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3447 for (i = 0; i < n; ++i) {
3448 pix = (Guchar)i;
3449 colorMap->getRGB(&pix, &rgb);
3450 imgData.lookup[4*i] = colToByte(rgb.r);
3451 imgData.lookup[4*i+1] = colToByte(rgb.g);
3452 imgData.lookup[4*i+2] = colToByte(rgb.b);
3453 imgData.lookup[4*i+3] = 255;
3455 break;
3456 #if SPLASH_CMYK
3457 case splashModeCMYK8:
3458 grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
3459 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3460 for (i = 0; i < n; ++i) {
3461 pix = (Guchar)i;
3462 colorMap->getCMYK(&pix, &cmyk);
3463 if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
3464 grayIndexed = gFalse;
3466 imgData.lookup[4*i] = colToByte(cmyk.c);
3467 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3468 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3469 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3471 break;
3472 case splashModeDeviceN8:
3473 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3474 grayIndexed = colorMap->getColorSpace()->getMode() != csDeviceGray;
3475 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3476 for (i = 0; i < n; ++i) {
3477 pix = (Guchar)i;
3478 colorMap->getCMYK(&pix, &cmyk);
3479 if (cmyk.c != 0 || cmyk.m != 0 || cmyk.y != 0) {
3480 grayIndexed = gFalse;
3482 colorMap->getDeviceN(&pix, &deviceN);
3483 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3484 imgData.lookup[(SPOT_NCOMPS+4)*i +cp] = colToByte(deviceN.c[cp]);
3486 break;
3487 #endif
3491 #if SPLASH_CMYK
3492 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3493 state->getOverprintMode(), NULL, grayIndexed);
3494 #else
3495 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3496 state->getOverprintMode(), NULL);
3497 #endif
3499 if (colorMode == splashModeMono1) {
3500 srcMode = splashModeMono8;
3501 } else {
3502 srcMode = colorMode;
3504 #ifdef USE_CMS
3505 src = maskColors ? &alphaImageSrc : useIccImageSrc(&imgData) ? &iccImageSrc : &imageSrc;
3506 tf = maskColors == NULL && useIccImageSrc(&imgData) ? &iccTransform : NULL;
3507 #else
3508 src = maskColors ? &alphaImageSrc : &imageSrc;
3509 tf = NULL;
3510 #endif
3511 splash->drawImage(src, tf, &imgData, srcMode, maskColors ? gTrue : gFalse,
3512 width, height, mat, interpolate);
3513 if (inlineImg) {
3514 while (imgData.y < height) {
3515 imgData.imgStr->getLine();
3516 ++imgData.y;
3520 gfree(imgData.lookup);
3521 delete imgData.imgStr;
3522 str->close();
3525 struct SplashOutMaskedImageData {
3526 ImageStream *imgStr;
3527 GfxImageColorMap *colorMap;
3528 SplashBitmap *mask;
3529 SplashColorPtr lookup;
3530 SplashColorMode colorMode;
3531 int width, height, y;
3534 GBool SplashOutputDev::maskedImageSrc(void *data, SplashColorPtr colorLine,
3535 Guchar *alphaLine) {
3536 SplashOutMaskedImageData *imgData = (SplashOutMaskedImageData *)data;
3537 Guchar *p, *aq;
3538 SplashColorPtr q, col;
3539 GfxRGB rgb;
3540 GfxGray gray;
3541 #if SPLASH_CMYK
3542 GfxCMYK cmyk;
3543 GfxColor deviceN;
3544 #endif
3545 Guchar alpha;
3546 Guchar *maskPtr;
3547 int maskBit;
3548 int nComps, x;
3550 if (imgData->y == imgData->height) {
3551 return gFalse;
3553 if (!(p = imgData->imgStr->getLine())) {
3554 return gFalse;
3557 nComps = imgData->colorMap->getNumPixelComps();
3559 maskPtr = imgData->mask->getDataPtr() +
3560 imgData->y * imgData->mask->getRowSize();
3561 maskBit = 0x80;
3562 for (x = 0, q = colorLine, aq = alphaLine;
3563 x < imgData->width;
3564 ++x, p += nComps) {
3565 alpha = (*maskPtr & maskBit) ? 0xff : 0x00;
3566 if (!(maskBit >>= 1)) {
3567 ++maskPtr;
3568 maskBit = 0x80;
3570 if (imgData->lookup) {
3571 switch (imgData->colorMode) {
3572 case splashModeMono1:
3573 case splashModeMono8:
3574 *q++ = imgData->lookup[*p];
3575 break;
3576 case splashModeRGB8:
3577 case splashModeBGR8:
3578 col = &imgData->lookup[3 * *p];
3579 *q++ = col[0];
3580 *q++ = col[1];
3581 *q++ = col[2];
3582 break;
3583 case splashModeXBGR8:
3584 col = &imgData->lookup[4 * *p];
3585 *q++ = col[0];
3586 *q++ = col[1];
3587 *q++ = col[2];
3588 *q++ = 255;
3589 break;
3590 #if SPLASH_CMYK
3591 case splashModeCMYK8:
3592 col = &imgData->lookup[4 * *p];
3593 *q++ = col[0];
3594 *q++ = col[1];
3595 *q++ = col[2];
3596 *q++ = col[3];
3597 break;
3598 case splashModeDeviceN8:
3599 col = &imgData->lookup[(SPOT_NCOMPS+4) * *p];
3600 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3601 *q++ = col[cp];
3602 break;
3603 #endif
3605 *aq++ = alpha;
3606 } else {
3607 switch (imgData->colorMode) {
3608 case splashModeMono1:
3609 case splashModeMono8:
3610 imgData->colorMap->getGray(p, &gray);
3611 *q++ = colToByte(gray);
3612 break;
3613 case splashModeXBGR8:
3614 case splashModeRGB8:
3615 case splashModeBGR8:
3616 imgData->colorMap->getRGB(p, &rgb);
3617 *q++ = colToByte(rgb.r);
3618 *q++ = colToByte(rgb.g);
3619 *q++ = colToByte(rgb.b);
3620 if (imgData->colorMode == splashModeXBGR8) *q++ = 255;
3621 break;
3622 #if SPLASH_CMYK
3623 case splashModeCMYK8:
3624 imgData->colorMap->getCMYK(p, &cmyk);
3625 *q++ = colToByte(cmyk.c);
3626 *q++ = colToByte(cmyk.m);
3627 *q++ = colToByte(cmyk.y);
3628 *q++ = colToByte(cmyk.k);
3629 break;
3630 case splashModeDeviceN8:
3631 imgData->colorMap->getDeviceN(p, &deviceN);
3632 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3633 *q++ = colToByte(deviceN.c[cp]);
3634 break;
3635 #endif
3637 *aq++ = alpha;
3641 ++imgData->y;
3642 return gTrue;
3645 void SplashOutputDev::drawMaskedImage(GfxState *state, Object *ref,
3646 Stream *str, int width, int height,
3647 GfxImageColorMap *colorMap,
3648 GBool interpolate,
3649 Stream *maskStr, int maskWidth,
3650 int maskHeight, GBool maskInvert,
3651 GBool maskInterpolate) {
3652 GfxImageColorMap *maskColorMap;
3653 Object maskDecode, decodeLow, decodeHigh;
3654 double *ctm;
3655 SplashCoord mat[6];
3656 SplashOutMaskedImageData imgData;
3657 SplashOutImageMaskData imgMaskData;
3658 SplashColorMode srcMode;
3659 SplashBitmap *maskBitmap;
3660 Splash *maskSplash;
3661 SplashColor maskColor;
3662 GfxGray gray;
3663 GfxRGB rgb;
3664 #if SPLASH_CMYK
3665 GfxCMYK cmyk;
3666 GfxColor deviceN;
3667 #endif
3668 Guchar pix;
3669 int n, i;
3671 #if SPLASH_CMYK
3672 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3673 #endif
3674 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3675 state->getOverprintMode(), NULL);
3677 // If the mask is higher resolution than the image, use
3678 // drawSoftMaskedImage() instead.
3679 if (maskWidth > width || maskHeight > height) {
3680 decodeLow.initInt(maskInvert ? 0 : 1);
3681 decodeHigh.initInt(maskInvert ? 1 : 0);
3682 maskDecode.initArray((xref) ? xref : doc->getXRef());
3683 maskDecode.arrayAdd(&decodeLow);
3684 maskDecode.arrayAdd(&decodeHigh);
3685 maskColorMap = new GfxImageColorMap(1, &maskDecode,
3686 new GfxDeviceGrayColorSpace());
3687 maskDecode.free();
3688 drawSoftMaskedImage(state, ref, str, width, height, colorMap, interpolate,
3689 maskStr, maskWidth, maskHeight, maskColorMap, maskInterpolate);
3690 delete maskColorMap;
3692 } else {
3693 //----- scale the mask image to the same size as the source image
3695 mat[0] = (SplashCoord)width;
3696 mat[1] = 0;
3697 mat[2] = 0;
3698 mat[3] = (SplashCoord)height;
3699 mat[4] = 0;
3700 mat[5] = 0;
3701 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth, 1, 1);
3702 imgMaskData.imgStr->reset();
3703 imgMaskData.invert = maskInvert ? 0 : 1;
3704 imgMaskData.width = maskWidth;
3705 imgMaskData.height = maskHeight;
3706 imgMaskData.y = 0;
3707 maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
3708 if (!maskBitmap->getDataPtr()) {
3709 delete maskBitmap;
3710 width = height = 1;
3711 maskBitmap = new SplashBitmap(width, height, 1, splashModeMono1, gFalse);
3713 maskSplash = new Splash(maskBitmap, gFalse);
3714 maskColor[0] = 0;
3715 maskSplash->clear(maskColor);
3716 maskColor[0] = 0xff;
3717 maskSplash->setFillPattern(new SplashSolidColor(maskColor));
3718 maskSplash->fillImageMask(&imageMaskSrc, &imgMaskData,
3719 maskWidth, maskHeight, mat, gFalse);
3720 delete imgMaskData.imgStr;
3721 maskStr->close();
3722 delete maskSplash;
3724 //----- draw the source image
3726 ctm = state->getCTM();
3727 for (i = 0; i < 6; ++i) {
3728 if (!isfinite(ctm[i])) {
3729 delete maskBitmap;
3730 return;
3733 mat[0] = ctm[0];
3734 mat[1] = ctm[1];
3735 mat[2] = -ctm[2];
3736 mat[3] = -ctm[3];
3737 mat[4] = ctm[2] + ctm[4];
3738 mat[5] = ctm[3] + ctm[5];
3740 imgData.imgStr = new ImageStream(str, width,
3741 colorMap->getNumPixelComps(),
3742 colorMap->getBits());
3743 imgData.imgStr->reset();
3744 imgData.colorMap = colorMap;
3745 imgData.mask = maskBitmap;
3746 imgData.colorMode = colorMode;
3747 imgData.width = width;
3748 imgData.height = height;
3749 imgData.y = 0;
3751 // special case for one-channel (monochrome/gray/separation) images:
3752 // build a lookup table here
3753 imgData.lookup = NULL;
3754 if (colorMap->getNumPixelComps() == 1) {
3755 n = 1 << colorMap->getBits();
3756 switch (colorMode) {
3757 case splashModeMono1:
3758 case splashModeMono8:
3759 imgData.lookup = (SplashColorPtr)gmalloc(n);
3760 for (i = 0; i < n; ++i) {
3761 pix = (Guchar)i;
3762 colorMap->getGray(&pix, &gray);
3763 imgData.lookup[i] = colToByte(gray);
3765 break;
3766 case splashModeRGB8:
3767 case splashModeBGR8:
3768 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3769 for (i = 0; i < n; ++i) {
3770 pix = (Guchar)i;
3771 colorMap->getRGB(&pix, &rgb);
3772 imgData.lookup[3*i] = colToByte(rgb.r);
3773 imgData.lookup[3*i+1] = colToByte(rgb.g);
3774 imgData.lookup[3*i+2] = colToByte(rgb.b);
3776 break;
3777 case splashModeXBGR8:
3778 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3779 for (i = 0; i < n; ++i) {
3780 pix = (Guchar)i;
3781 colorMap->getRGB(&pix, &rgb);
3782 imgData.lookup[4*i] = colToByte(rgb.r);
3783 imgData.lookup[4*i+1] = colToByte(rgb.g);
3784 imgData.lookup[4*i+2] = colToByte(rgb.b);
3785 imgData.lookup[4*i+3] = 255;
3787 break;
3788 #if SPLASH_CMYK
3789 case splashModeCMYK8:
3790 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3791 for (i = 0; i < n; ++i) {
3792 pix = (Guchar)i;
3793 colorMap->getCMYK(&pix, &cmyk);
3794 imgData.lookup[4*i] = colToByte(cmyk.c);
3795 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3796 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3797 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3799 break;
3800 case splashModeDeviceN8:
3801 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3802 for (i = 0; i < n; ++i) {
3803 pix = (Guchar)i;
3804 colorMap->getDeviceN(&pix, &deviceN);
3805 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3806 imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
3808 break;
3809 #endif
3813 if (colorMode == splashModeMono1) {
3814 srcMode = splashModeMono8;
3815 } else {
3816 srcMode = colorMode;
3818 splash->drawImage(&maskedImageSrc, NULL, &imgData, srcMode, gTrue,
3819 width, height, mat, interpolate);
3820 delete maskBitmap;
3821 gfree(imgData.lookup);
3822 delete imgData.imgStr;
3823 str->close();
3827 void SplashOutputDev::drawSoftMaskedImage(GfxState *state, Object *ref,
3828 Stream *str, int width, int height,
3829 GfxImageColorMap *colorMap,
3830 GBool interpolate,
3831 Stream *maskStr,
3832 int maskWidth, int maskHeight,
3833 GfxImageColorMap *maskColorMap,
3834 GBool maskInterpolate) {
3835 double *ctm;
3836 SplashCoord mat[6];
3837 SplashOutImageData imgData;
3838 SplashOutImageData imgMaskData;
3839 SplashColorMode srcMode;
3840 SplashBitmap *maskBitmap;
3841 Splash *maskSplash;
3842 SplashColor maskColor;
3843 GfxGray gray;
3844 GfxRGB rgb;
3845 #if SPLASH_CMYK
3846 GfxCMYK cmyk;
3847 GfxColor deviceN;
3848 #endif
3849 Guchar pix;
3850 int n, i;
3852 #if SPLASH_CMYK
3853 colorMap->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
3854 #endif
3855 setOverprintMask(colorMap->getColorSpace(), state->getFillOverprint(),
3856 state->getOverprintMode(), NULL);
3858 ctm = state->getCTM();
3859 for (i = 0; i < 6; ++i) {
3860 if (!isfinite(ctm[i])) return;
3862 mat[0] = ctm[0];
3863 mat[1] = ctm[1];
3864 mat[2] = -ctm[2];
3865 mat[3] = -ctm[3];
3866 mat[4] = ctm[2] + ctm[4];
3867 mat[5] = ctm[3] + ctm[5];
3869 //----- set up the soft mask
3871 imgMaskData.imgStr = new ImageStream(maskStr, maskWidth,
3872 maskColorMap->getNumPixelComps(),
3873 maskColorMap->getBits());
3874 imgMaskData.imgStr->reset();
3875 imgMaskData.colorMap = maskColorMap;
3876 imgMaskData.maskColors = NULL;
3877 imgMaskData.colorMode = splashModeMono8;
3878 imgMaskData.width = maskWidth;
3879 imgMaskData.height = maskHeight;
3880 imgMaskData.y = 0;
3881 n = 1 << maskColorMap->getBits();
3882 imgMaskData.lookup = (SplashColorPtr)gmalloc(n);
3883 for (i = 0; i < n; ++i) {
3884 pix = (Guchar)i;
3885 maskColorMap->getGray(&pix, &gray);
3886 imgMaskData.lookup[i] = colToByte(gray);
3888 maskBitmap = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
3889 1, splashModeMono8, gFalse);
3890 maskSplash = new Splash(maskBitmap, vectorAntialias);
3891 maskColor[0] = 0;
3892 maskSplash->clear(maskColor);
3893 maskSplash->drawImage(&imageSrc, NULL, &imgMaskData, splashModeMono8, gFalse,
3894 maskWidth, maskHeight, mat, maskInterpolate);
3895 delete imgMaskData.imgStr;
3896 maskStr->close();
3897 gfree(imgMaskData.lookup);
3898 delete maskSplash;
3899 splash->setSoftMask(maskBitmap);
3901 //----- draw the source image
3903 imgData.imgStr = new ImageStream(str, width,
3904 colorMap->getNumPixelComps(),
3905 colorMap->getBits());
3906 imgData.imgStr->reset();
3907 imgData.colorMap = colorMap;
3908 imgData.maskColors = NULL;
3909 imgData.colorMode = colorMode;
3910 imgData.width = width;
3911 imgData.height = height;
3912 imgData.y = 0;
3914 // special case for one-channel (monochrome/gray/separation) images:
3915 // build a lookup table here
3916 imgData.lookup = NULL;
3917 if (colorMap->getNumPixelComps() == 1) {
3918 n = 1 << colorMap->getBits();
3919 switch (colorMode) {
3920 case splashModeMono1:
3921 case splashModeMono8:
3922 imgData.lookup = (SplashColorPtr)gmalloc(n);
3923 for (i = 0; i < n; ++i) {
3924 pix = (Guchar)i;
3925 colorMap->getGray(&pix, &gray);
3926 imgData.lookup[i] = colToByte(gray);
3928 break;
3929 case splashModeRGB8:
3930 case splashModeBGR8:
3931 imgData.lookup = (SplashColorPtr)gmallocn(n, 3);
3932 for (i = 0; i < n; ++i) {
3933 pix = (Guchar)i;
3934 colorMap->getRGB(&pix, &rgb);
3935 imgData.lookup[3*i] = colToByte(rgb.r);
3936 imgData.lookup[3*i+1] = colToByte(rgb.g);
3937 imgData.lookup[3*i+2] = colToByte(rgb.b);
3939 break;
3940 case splashModeXBGR8:
3941 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3942 for (i = 0; i < n; ++i) {
3943 pix = (Guchar)i;
3944 colorMap->getRGB(&pix, &rgb);
3945 imgData.lookup[4*i] = colToByte(rgb.r);
3946 imgData.lookup[4*i+1] = colToByte(rgb.g);
3947 imgData.lookup[4*i+2] = colToByte(rgb.b);
3948 imgData.lookup[4*i+3] = 255;
3950 break;
3951 #if SPLASH_CMYK
3952 case splashModeCMYK8:
3953 imgData.lookup = (SplashColorPtr)gmallocn(n, 4);
3954 for (i = 0; i < n; ++i) {
3955 pix = (Guchar)i;
3956 colorMap->getCMYK(&pix, &cmyk);
3957 imgData.lookup[4*i] = colToByte(cmyk.c);
3958 imgData.lookup[4*i+1] = colToByte(cmyk.m);
3959 imgData.lookup[4*i+2] = colToByte(cmyk.y);
3960 imgData.lookup[4*i+3] = colToByte(cmyk.k);
3962 break;
3963 case splashModeDeviceN8:
3964 imgData.lookup = (SplashColorPtr)gmallocn(n, SPOT_NCOMPS+4);
3965 for (i = 0; i < n; ++i) {
3966 pix = (Guchar)i;
3967 colorMap->getDeviceN(&pix, &deviceN);
3968 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
3969 imgData.lookup[(SPOT_NCOMPS+4)*i + cp] = colToByte(deviceN.c[cp]);
3971 break;
3972 #endif
3976 if (colorMode == splashModeMono1) {
3977 srcMode = splashModeMono8;
3978 } else {
3979 srcMode = colorMode;
3981 splash->drawImage(&imageSrc, NULL, &imgData, srcMode, gFalse, width, height, mat, interpolate);
3982 splash->setSoftMask(NULL);
3983 gfree(imgData.lookup);
3984 delete imgData.imgStr;
3985 str->close();
3988 GBool SplashOutputDev::checkTransparencyGroup(GfxState *state, GBool knockout) {
3989 if (state->getFillOpacity() != 1 ||
3990 state->getStrokeOpacity() != 1 ||
3991 state->getAlphaIsShape() ||
3992 state->getBlendMode() != gfxBlendNormal ||
3993 splash->getSoftMask() != NULL ||
3994 knockout)
3995 return gTrue;
3996 return transpGroupStack != NULL && transpGroupStack->shape != NULL;
3999 void SplashOutputDev::beginTransparencyGroup(GfxState *state, double *bbox,
4000 GfxColorSpace *blendingColorSpace,
4001 GBool isolated, GBool knockout,
4002 GBool forSoftMask) {
4003 SplashTransparencyGroup *transpGroup;
4004 SplashColor color;
4005 double xMin, yMin, xMax, yMax, x, y;
4006 int tx, ty, w, h, i;
4008 // transform the bbox
4009 state->transform(bbox[0], bbox[1], &x, &y);
4010 xMin = xMax = x;
4011 yMin = yMax = y;
4012 state->transform(bbox[0], bbox[3], &x, &y);
4013 if (x < xMin) {
4014 xMin = x;
4015 } else if (x > xMax) {
4016 xMax = x;
4018 if (y < yMin) {
4019 yMin = y;
4020 } else if (y > yMax) {
4021 yMax = y;
4023 state->transform(bbox[2], bbox[1], &x, &y);
4024 if (x < xMin) {
4025 xMin = x;
4026 } else if (x > xMax) {
4027 xMax = x;
4029 if (y < yMin) {
4030 yMin = y;
4031 } else if (y > yMax) {
4032 yMax = y;
4034 state->transform(bbox[2], bbox[3], &x, &y);
4035 if (x < xMin) {
4036 xMin = x;
4037 } else if (x > xMax) {
4038 xMax = x;
4040 if (y < yMin) {
4041 yMin = y;
4042 } else if (y > yMax) {
4043 yMax = y;
4045 tx = (int)floor(xMin);
4046 if (tx < 0) {
4047 tx = 0;
4048 } else if (tx >= bitmap->getWidth()) {
4049 tx = bitmap->getWidth() - 1;
4051 ty = (int)floor(yMin);
4052 if (ty < 0) {
4053 ty = 0;
4054 } else if (ty >= bitmap->getHeight()) {
4055 ty = bitmap->getHeight() - 1;
4057 w = (int)ceil(xMax) - tx + 1;
4058 if (tx + w > bitmap->getWidth()) {
4059 w = bitmap->getWidth() - tx;
4061 if (w < 1) {
4062 w = 1;
4064 h = (int)ceil(yMax) - ty + 1;
4065 if (ty + h > bitmap->getHeight()) {
4066 h = bitmap->getHeight() - ty;
4068 if (h < 1) {
4069 h = 1;
4072 // push a new stack entry
4073 transpGroup = new SplashTransparencyGroup();
4074 transpGroup->softmask = NULL;
4075 transpGroup->tx = tx;
4076 transpGroup->ty = ty;
4077 transpGroup->blendingColorSpace = blendingColorSpace;
4078 transpGroup->isolated = isolated;
4079 transpGroup->shape = (knockout && !isolated) ? SplashBitmap::copy(bitmap) : NULL;
4080 transpGroup->knockout = (knockout && isolated);
4081 transpGroup->knockoutOpacity = 1.0;
4082 transpGroup->next = transpGroupStack;
4083 transpGroupStack = transpGroup;
4085 // save state
4086 transpGroup->origBitmap = bitmap;
4087 transpGroup->origSplash = splash;
4088 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
4089 transpGroup->fontAA = fontEngine->getAA();
4090 #endif
4092 //~ this handles the blendingColorSpace arg for soft masks, but
4093 //~ not yet for transparency groups
4095 // switch to the blending color space
4096 if (forSoftMask && isolated && blendingColorSpace) {
4097 if (blendingColorSpace->getMode() == csDeviceGray ||
4098 blendingColorSpace->getMode() == csCalGray ||
4099 (blendingColorSpace->getMode() == csICCBased &&
4100 blendingColorSpace->getNComps() == 1)) {
4101 colorMode = splashModeMono8;
4102 } else if (blendingColorSpace->getMode() == csDeviceRGB ||
4103 blendingColorSpace->getMode() == csCalRGB ||
4104 (blendingColorSpace->getMode() == csICCBased &&
4105 blendingColorSpace->getNComps() == 3)) {
4106 //~ does this need to use BGR8?
4107 colorMode = splashModeRGB8;
4108 #if SPLASH_CMYK
4109 } else if (blendingColorSpace->getMode() == csDeviceCMYK ||
4110 (blendingColorSpace->getMode() == csICCBased &&
4111 blendingColorSpace->getNComps() == 4)) {
4112 colorMode = splashModeCMYK8;
4113 #endif
4117 // create the temporary bitmap
4118 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue,
4119 bitmapTopDown, bitmap->getSeparationList());
4120 if (!bitmap->getDataPtr()) {
4121 delete bitmap;
4122 w = h = 1;
4123 bitmap = new SplashBitmap(w, h, bitmapRowPad, colorMode, gTrue, bitmapTopDown);
4125 splash = new Splash(bitmap, vectorAntialias,
4126 transpGroup->origSplash->getScreen());
4127 if (transpGroup->next != NULL && transpGroup->next->knockout) {
4128 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
4129 fontEngine->setAA(gFalse);
4130 #endif
4132 splash->setThinLineMode(transpGroup->origSplash->getThinLineMode());
4133 splash->setMinLineWidth(globalParams->getMinLineWidth());
4134 //~ Acrobat apparently copies at least the fill and stroke colors, and
4135 //~ maybe other state(?) -- but not the clipping path (and not sure
4136 //~ what else)
4137 //~ [this is likely the same situation as in type3D1()]
4138 splash->setFillPattern(transpGroup->origSplash->getFillPattern()->copy());
4139 splash->setStrokePattern(
4140 transpGroup->origSplash->getStrokePattern()->copy());
4141 if (isolated) {
4142 for (i = 0; i < splashMaxColorComps; ++i) {
4143 color[i] = 0;
4145 if (colorMode == splashModeXBGR8) color[3] = 255;
4146 splash->clear(color, 0);
4147 } else {
4148 SplashBitmap *shape = (knockout) ? transpGroup->shape :
4149 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->shape : transpGroup->origBitmap;
4150 int shapeTx = (knockout) ? tx :
4151 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->tx + tx : tx;
4152 int shapeTy = (knockout) ? ty :
4153 (transpGroup->next != NULL && transpGroup->next->shape != NULL) ? transpGroup->next->ty + ty : ty;
4154 splash->blitTransparent(transpGroup->origBitmap, tx, ty, 0, 0, w, h);
4155 splash->setInNonIsolatedGroup(shape, shapeTx, shapeTy);
4157 transpGroup->tBitmap = bitmap;
4158 state->shiftCTMAndClip(-tx, -ty);
4159 updateCTM(state, 0, 0, 0, 0, 0, 0);
4160 ++nestCount;
4163 void SplashOutputDev::endTransparencyGroup(GfxState *state) {
4164 // restore state
4165 --nestCount;
4166 delete splash;
4167 bitmap = transpGroupStack->origBitmap;
4168 colorMode = bitmap->getMode();
4169 splash = transpGroupStack->origSplash;
4170 state->shiftCTMAndClip(transpGroupStack->tx, transpGroupStack->ty);
4171 updateCTM(state, 0, 0, 0, 0, 0, 0);
4174 void SplashOutputDev::paintTransparencyGroup(GfxState *state, double *bbox) {
4175 SplashBitmap *tBitmap;
4176 SplashTransparencyGroup *transpGroup;
4177 GBool isolated;
4178 int tx, ty;
4180 tx = transpGroupStack->tx;
4181 ty = transpGroupStack->ty;
4182 tBitmap = transpGroupStack->tBitmap;
4183 isolated = transpGroupStack->isolated;
4185 // paint the transparency group onto the parent bitmap
4186 // - the clip path was set in the parent's state)
4187 if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
4188 SplashCoord knockoutOpacity = (transpGroupStack->next != NULL) ? transpGroupStack->next->knockoutOpacity
4189 : transpGroupStack->knockoutOpacity;
4190 splash->setOverprintMask(0xffffffff, gFalse);
4191 splash->composite(tBitmap, 0, 0, tx, ty,
4192 tBitmap->getWidth(), tBitmap->getHeight(),
4193 gFalse, !isolated, transpGroupStack->next != NULL && transpGroupStack->next->knockout, knockoutOpacity);
4194 #if HAVE_FREETYPE_FREETYPE_H || HAVE_FREETYPE_H
4195 fontEngine->setAA(transpGroupStack->fontAA);
4196 #endif
4197 if (transpGroupStack->next != NULL && transpGroupStack->next->shape != NULL) {
4198 transpGroupStack->next->knockout = gTrue;
4202 // pop the stack
4203 transpGroup = transpGroupStack;
4204 transpGroupStack = transpGroup->next;
4205 if (transpGroupStack != NULL && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) {
4206 transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity;
4208 delete transpGroup->shape;
4209 delete transpGroup;
4211 delete tBitmap;
4214 void SplashOutputDev::setSoftMask(GfxState *state, double *bbox,
4215 GBool alpha, Function *transferFunc,
4216 GfxColor *backdropColor) {
4217 SplashBitmap *softMask, *tBitmap;
4218 Splash *tSplash;
4219 SplashTransparencyGroup *transpGroup;
4220 SplashColor color;
4221 SplashColorPtr p;
4222 GfxGray gray;
4223 GfxRGB rgb;
4224 #if SPLASH_CMYK
4225 GfxCMYK cmyk;
4226 GfxColor deviceN;
4227 #endif
4228 double lum, lum2;
4229 int tx, ty, x, y;
4231 tx = transpGroupStack->tx;
4232 ty = transpGroupStack->ty;
4233 tBitmap = transpGroupStack->tBitmap;
4235 // composite with backdrop color
4236 if (!alpha && tBitmap->getMode() != splashModeMono1) {
4237 //~ need to correctly handle the case where no blending color
4238 //~ space is given
4239 if (transpGroupStack->blendingColorSpace) {
4240 tSplash = new Splash(tBitmap, vectorAntialias,
4241 transpGroupStack->origSplash->getScreen());
4242 switch (tBitmap->getMode()) {
4243 case splashModeMono1:
4244 // transparency is not supported in mono1 mode
4245 break;
4246 case splashModeMono8:
4247 transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
4248 color[0] = colToByte(gray);
4249 tSplash->compositeBackground(color);
4250 break;
4251 case splashModeXBGR8:
4252 color[3] = 255;
4253 case splashModeRGB8:
4254 case splashModeBGR8:
4255 transpGroupStack->blendingColorSpace->getRGB(backdropColor, &rgb);
4256 color[0] = colToByte(rgb.r);
4257 color[1] = colToByte(rgb.g);
4258 color[2] = colToByte(rgb.b);
4259 tSplash->compositeBackground(color);
4260 break;
4261 #if SPLASH_CMYK
4262 case splashModeCMYK8:
4263 transpGroupStack->blendingColorSpace->getCMYK(backdropColor, &cmyk);
4264 color[0] = colToByte(cmyk.c);
4265 color[1] = colToByte(cmyk.m);
4266 color[2] = colToByte(cmyk.y);
4267 color[3] = colToByte(cmyk.k);
4268 tSplash->compositeBackground(color);
4269 break;
4270 case splashModeDeviceN8:
4271 transpGroupStack->blendingColorSpace->getDeviceN(backdropColor, &deviceN);
4272 for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
4273 color[cp] = colToByte(deviceN.c[cp]);
4274 tSplash->compositeBackground(color);
4275 break;
4276 #endif
4278 delete tSplash;
4282 softMask = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(),
4283 1, splashModeMono8, gFalse);
4284 unsigned char fill = 0;
4285 if (transpGroupStack->blendingColorSpace) {
4286 transpGroupStack->blendingColorSpace->getGray(backdropColor, &gray);
4287 fill = colToByte(gray);
4289 memset(softMask->getDataPtr(), fill,
4290 softMask->getRowSize() * softMask->getHeight());
4291 p = softMask->getDataPtr() + ty * softMask->getRowSize() + tx;
4292 int xMax = tBitmap->getWidth();
4293 int yMax = tBitmap->getHeight();
4294 if (xMax > bitmap->getWidth() - tx) xMax = bitmap->getWidth() - tx;
4295 if (yMax > bitmap->getHeight() - ty) yMax = bitmap->getHeight() - ty;
4296 for (y = 0; y < yMax; ++y) {
4297 for (x = 0; x < xMax; ++x) {
4298 if (alpha) {
4299 if (transferFunc) {
4300 lum = tBitmap->getAlpha(x, y) / 255.0;
4301 transferFunc->transform(&lum, &lum2);
4302 p[x] = (int)(lum2 * 255.0 + 0.5);
4303 } else
4304 p[x] = tBitmap->getAlpha(x, y);
4305 } else {
4306 tBitmap->getPixel(x, y, color);
4307 // convert to luminosity
4308 switch (tBitmap->getMode()) {
4309 case splashModeMono1:
4310 case splashModeMono8:
4311 lum = color[0] / 255.0;
4312 break;
4313 case splashModeXBGR8:
4314 case splashModeRGB8:
4315 case splashModeBGR8:
4316 lum = (0.3 / 255.0) * color[0] +
4317 (0.59 / 255.0) * color[1] +
4318 (0.11 / 255.0) * color[2];
4319 break;
4320 #if SPLASH_CMYK
4321 case splashModeCMYK8:
4322 case splashModeDeviceN8:
4323 lum = (1 - color[3] / 255.0)
4324 - (0.3 / 255.0) * color[0]
4325 - (0.59 / 255.0) * color[1]
4326 - (0.11 / 255.0) * color[2];
4327 if (lum < 0) {
4328 lum = 0;
4330 break;
4331 #endif
4333 if (transferFunc) {
4334 transferFunc->transform(&lum, &lum2);
4335 } else {
4336 lum2 = lum;
4338 p[x] = (int)(lum2 * 255.0 + 0.5);
4341 p += softMask->getRowSize();
4343 splash->setSoftMask(softMask);
4345 // pop the stack
4346 transpGroup = transpGroupStack;
4347 transpGroupStack = transpGroup->next;
4348 delete transpGroup;
4350 delete tBitmap;
4353 void SplashOutputDev::clearSoftMask(GfxState *state) {
4354 splash->setSoftMask(NULL);
4357 void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
4358 splashColorCopy(paperColor, paperColorA);
4361 int SplashOutputDev::getBitmapWidth() {
4362 return bitmap->getWidth();
4365 int SplashOutputDev::getBitmapHeight() {
4366 return bitmap->getHeight();
4369 SplashBitmap *SplashOutputDev::takeBitmap() {
4370 SplashBitmap *ret;
4372 ret = bitmap;
4373 bitmap = new SplashBitmap(1, 1, bitmapRowPad, colorMode,
4374 colorMode != splashModeMono1, bitmapTopDown);
4375 return ret;
4378 void SplashOutputDev::getModRegion(int *xMin, int *yMin,
4379 int *xMax, int *yMax) {
4380 splash->getModRegion(xMin, yMin, xMax, yMax);
4383 void SplashOutputDev::clearModRegion() {
4384 splash->clearModRegion();
4387 #if 1 //~tmp: turn off anti-aliasing temporarily
4388 GBool SplashOutputDev::getVectorAntialias() {
4389 return splash->getVectorAntialias();
4392 void SplashOutputDev::setVectorAntialias(GBool vaa) {
4393 vaa = vaa && colorMode != splashModeMono1;
4394 vectorAntialias = vaa;
4395 splash->setVectorAntialias(vaa);
4397 #endif
4399 void SplashOutputDev::setFreeTypeHinting(GBool enable, GBool enableSlightHintingA)
4401 enableFreeTypeHinting = enable;
4402 enableSlightHinting = enableSlightHintingA;
4405 GBool SplashOutputDev::tilingPatternFill(GfxState *state, Gfx *gfxA, Catalog *catalog, Object *str,
4406 double *ptm, int paintType, int /*tilingType*/, Dict *resDict,
4407 double *mat, double *bbox,
4408 int x0, int y0, int x1, int y1,
4409 double xStep, double yStep)
4411 PDFRectangle box;
4412 Gfx *gfx;
4413 Splash *formerSplash = splash;
4414 SplashBitmap *formerBitmap = bitmap;
4415 double width, height;
4416 int surface_width, surface_height, result_width, result_height, i;
4417 int repeatX, repeatY;
4418 SplashCoord matc[6];
4419 Matrix m1;
4420 double *ctm, savedCTM[6];
4421 double kx, ky, sx, sy;
4422 GBool retValue = gFalse;
4424 width = bbox[2] - bbox[0];
4425 height = bbox[3] - bbox[1];
4427 if (xStep != width || yStep != height)
4428 return gFalse;
4430 // calculate offsets
4431 ctm = state->getCTM();
4432 for (i = 0; i < 6; ++i) {
4433 savedCTM[i] = ctm[i];
4435 state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4436 state->concatCTM(1, 0, 0, 1, bbox[0], bbox[1]);
4437 ctm = state->getCTM();
4438 for (i = 0; i < 6; ++i) {
4439 if (!isfinite(ctm[i])) {
4440 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4441 return gFalse;
4444 matc[4] = x0 * xStep * ctm[0] + y0 * yStep * ctm[2] + ctm[4];
4445 matc[5] = x0 * xStep * ctm[1] + y0 * yStep * ctm[3] + ctm[5];
4446 if (splashAbs(ctm[1]) > splashAbs(ctm[0])) {
4447 kx = -ctm[1];
4448 ky = ctm[2] - (ctm[0] * ctm[3]) / ctm[1];
4449 } else {
4450 kx = ctm[0];
4451 ky = ctm[3] - (ctm[1] * ctm[2]) / ctm[0];
4453 result_width = (int) ceil(fabs(kx * width * (x1 - x0)));
4454 result_height = (int) ceil(fabs(ky * height * (y1 - y0)));
4455 kx = state->getHDPI() / 72.0;
4456 ky = state->getVDPI() / 72.0;
4457 m1.m[0] = (ptm[0] == 0) ? fabs(ptm[2]) * kx : fabs(ptm[0]) * kx;
4458 m1.m[1] = 0;
4459 m1.m[2] = 0;
4460 m1.m[3] = (ptm[3] == 0) ? fabs(ptm[1]) * ky : fabs(ptm[3]) * ky;
4461 m1.m[4] = 0;
4462 m1.m[5] = 0;
4463 m1.transform(width, height, &kx, &ky);
4464 surface_width = (int) ceil (fabs(kx));
4465 surface_height = (int) ceil (fabs(ky));
4467 sx = (double) result_width / (surface_width * (x1 - x0));
4468 sy = (double) result_height / (surface_height * (y1 - y0));
4469 m1.m[0] *= sx;
4470 m1.m[3] *= sy;
4471 m1.transform(width, height, &kx, &ky);
4473 if(fabs(kx) < 1 && fabs(ky) < 1) {
4474 kx = std::min<double>(kx, ky);
4475 ky = 2 / kx;
4476 m1.m[0] *= ky;
4477 m1.m[3] *= ky;
4478 m1.transform(width, height, &kx, &ky);
4479 surface_width = (int) ceil (fabs(kx));
4480 surface_height = (int) ceil (fabs(ky));
4481 repeatX = x1 - x0;
4482 repeatY = y1 - y0;
4483 } else {
4484 if ((unsigned long) surface_width * surface_height > 0x800000L) {
4485 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4486 return gFalse;
4488 while(fabs(kx) > 16384 || fabs(ky) > 16384) {
4489 // limit pattern bitmap size
4490 m1.m[0] /= 2;
4491 m1.m[3] /= 2;
4492 m1.transform(width, height, &kx, &ky);
4494 surface_width = (int) ceil (fabs(kx));
4495 surface_height = (int) ceil (fabs(ky));
4496 // adjust repeat values to completely fill region
4497 repeatX = result_width / surface_width;
4498 repeatY = result_height / surface_height;
4499 if (surface_width * repeatX < result_width)
4500 repeatX++;
4501 if (surface_height * repeatY < result_height)
4502 repeatY++;
4503 if (x1 - x0 > repeatX)
4504 repeatX = x1 - x0;
4505 if (y1 - y0 > repeatY)
4506 repeatY = y1 - y0;
4508 // restore CTM and calculate rotate and scale with rounded matric
4509 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4510 state->concatCTM(mat[0], mat[1], mat[2], mat[3], mat[4], mat[5]);
4511 state->concatCTM(width * repeatX, 0, 0, height * repeatY, bbox[0], bbox[1]);
4512 ctm = state->getCTM();
4513 matc[0] = ctm[0];
4514 matc[1] = ctm[1];
4515 matc[2] = ctm[2];
4516 matc[3] = ctm[3];
4518 if (surface_width == 0 || surface_height == 0) {
4519 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4520 return gFalse;
4522 m1.transform(bbox[0], bbox[1], &kx, &ky);
4523 m1.m[4] = -kx;
4524 m1.m[5] = -ky;
4526 bitmap = new SplashBitmap(surface_width, surface_height, 1,
4527 (paintType == 1) ? colorMode : splashModeMono8, gTrue);
4528 if (bitmap->getDataPtr() == NULL) {
4529 SplashBitmap *tBitmap = bitmap;
4530 bitmap = formerBitmap;
4531 delete tBitmap;
4532 state->setCTM(savedCTM[0], savedCTM[1], savedCTM[2], savedCTM[3], savedCTM[4], savedCTM[5]);
4533 return gFalse;
4535 splash = new Splash(bitmap, gTrue);
4536 if (paintType == 2) {
4537 SplashColor clearColor;
4538 #if SPLASH_CMYK
4539 clearColor[0] = (colorMode == splashModeCMYK8 || colorMode == splashModeDeviceN8) ? 0x00 : 0xFF;
4540 #else
4541 clearColor[0] = 0xFF;
4542 #endif
4543 splash->clear(clearColor, 0);
4544 } else {
4545 splash->clear(paperColor, 0);
4547 splash->setThinLineMode(formerSplash->getThinLineMode());
4548 splash->setMinLineWidth(globalParams->getMinLineWidth());
4550 box.x1 = bbox[0]; box.y1 = bbox[1];
4551 box.x2 = bbox[2]; box.y2 = bbox[3];
4552 gfx = new Gfx(doc, this, resDict, &box, NULL, NULL, NULL, gfxA->getXRef());
4553 // set pattern transformation matrix
4554 gfx->getState()->setCTM(m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4555 updateCTM(gfx->getState(), m1.m[0], m1.m[1], m1.m[2], m1.m[3], m1.m[4], m1.m[5]);
4556 gfx->display(str);
4557 delete splash;
4558 splash = formerSplash;
4559 TilingSplashOutBitmap imgData;
4560 imgData.bitmap = bitmap;
4561 imgData.paintType = paintType;
4562 imgData.pattern = splash->getFillPattern();
4563 imgData.colorMode = colorMode;
4564 imgData.y = 0;
4565 imgData.repeatX = repeatX;
4566 imgData.repeatY = repeatY;
4567 SplashBitmap *tBitmap = bitmap;
4568 bitmap = formerBitmap;
4569 result_width = tBitmap->getWidth() * imgData.repeatX;
4570 result_height = tBitmap->getHeight() * imgData.repeatY;
4572 if (splashAbs(matc[1]) > splashAbs(matc[0])) {
4573 kx = -matc[1];
4574 ky = matc[2] - (matc[0] * matc[3]) / matc[1];
4575 } else {
4576 kx = matc[0];
4577 ky = matc[3] - (matc[1] * matc[2]) / matc[0];
4579 kx = result_width / (fabs(kx) + 1);
4580 ky = result_height / (fabs(ky) + 1);
4581 state->concatCTM(kx, 0, 0, ky, 0, 0);
4582 ctm = state->getCTM();
4583 matc[0] = ctm[0];
4584 matc[1] = ctm[1];
4585 matc[2] = ctm[2];
4586 matc[3] = ctm[3];
4587 GBool minorAxisZero = matc[1] == 0 && matc[2] == 0;
4588 if (matc[0] > 0 && minorAxisZero && matc[3] > 0) {
4589 // draw the tiles
4590 for (int y = 0; y < imgData.repeatY; ++y) {
4591 for (int x = 0; x < imgData.repeatX; ++x) {
4592 x0 = splashFloor(matc[4]) + x * tBitmap->getWidth();
4593 y0 = splashFloor(matc[5]) + y * tBitmap->getHeight();
4594 splash->blitImage(tBitmap, gTrue, x0, y0);
4597 retValue = gTrue;
4598 } else {
4599 retValue = splash->drawImage(&tilingBitmapSrc, NULL, &imgData, colorMode, gTrue, result_width, result_height, matc, gFalse, gTrue) == splashOk;
4601 delete tBitmap;
4602 delete gfx;
4603 return retValue;
4606 GBool SplashOutputDev::gouraudTriangleShadedFill(GfxState *state, GfxGouraudTriangleShading *shading)
4608 GfxColorSpaceMode shadingMode = shading->getColorSpace()->getMode();
4609 GBool bDirectColorTranslation = gFalse; // triggers an optimization.
4610 switch (colorMode) {
4611 case splashModeRGB8:
4612 bDirectColorTranslation = (shadingMode == csDeviceRGB);
4613 break;
4614 #if SPLASH_CMYK
4615 case splashModeCMYK8:
4616 case splashModeDeviceN8:
4617 bDirectColorTranslation = (shadingMode == csDeviceCMYK);
4618 break;
4619 #endif
4620 default:
4621 break;
4623 SplashGouraudColor *splashShading = new SplashGouraudPattern(bDirectColorTranslation, state, shading, colorMode);
4624 // restore vector antialias because we support it here
4625 if (shading->isParameterized()) {
4626 GBool vaa = getVectorAntialias();
4627 GBool retVal = gFalse;
4628 setVectorAntialias(gTrue);
4629 retVal = splash->gouraudTriangleShadedFill(splashShading);
4630 setVectorAntialias(vaa);
4631 return retVal;
4633 delete splashShading;
4634 return gFalse;
4637 GBool SplashOutputDev::univariateShadedFill(GfxState *state, SplashUnivariatePattern *pattern, double tMin, double tMax) {
4638 double xMin, yMin, xMax, yMax;
4639 SplashPath *path;
4640 GBool vaa = getVectorAntialias();
4641 // restore vector antialias because we support it here
4642 setVectorAntialias(gTrue);
4644 GBool retVal = gFalse;
4645 // get the clip region bbox
4646 if (pattern->getShading()->getHasBBox()) {
4647 pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax);
4648 } else {
4649 state->getClipBBox(&xMin, &yMin, &xMax, &yMax);
4651 xMin = floor (xMin);
4652 yMin = floor (yMin);
4653 xMax = ceil (xMax);
4654 yMax = ceil (yMax);
4657 Matrix ctm, ictm;
4658 double x[4], y[4];
4659 int i;
4661 state->getCTM(&ctm);
4662 ctm.invertTo(&ictm);
4664 ictm.transform(xMin, yMin, &x[0], &y[0]);
4665 ictm.transform(xMax, yMin, &x[1], &y[1]);
4666 ictm.transform(xMin, yMax, &x[2], &y[2]);
4667 ictm.transform(xMax, yMax, &x[3], &y[3]);
4669 xMin = xMax = x[0];
4670 yMin = yMax = y[0];
4671 for (i = 1; i < 4; i++) {
4672 xMin = std::min<double>(xMin, x[i]);
4673 yMin = std::min<double>(yMin, y[i]);
4674 xMax = std::max<double>(xMax, x[i]);
4675 yMax = std::max<double>(yMax, y[i]);
4680 // fill the region
4681 state->moveTo(xMin, yMin);
4682 state->lineTo(xMax, yMin);
4683 state->lineTo(xMax, yMax);
4684 state->lineTo(xMin, yMax);
4685 state->closePath();
4686 path = convertPath(state, state->getPath(), gTrue);
4688 #if SPLASH_CMYK
4689 pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
4690 #endif
4691 setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(),
4692 state->getOverprintMode(), NULL);
4693 retVal = (splash->shadedFill(path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
4694 state->clearPath();
4695 setVectorAntialias(vaa);
4696 delete path;
4698 return retVal;
4701 GBool SplashOutputDev::axialShadedFill(GfxState *state, GfxAxialShading *shading, double tMin, double tMax) {
4702 SplashAxialPattern *pattern = new SplashAxialPattern(colorMode, state, shading);
4703 GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4705 delete pattern;
4707 return retVal;
4710 GBool SplashOutputDev::radialShadedFill(GfxState *state, GfxRadialShading *shading, double tMin, double tMax) {
4711 SplashRadialPattern *pattern = new SplashRadialPattern(colorMode, state, shading);
4712 GBool retVal = univariateShadedFill(state, pattern, tMin, tMax);
4714 delete pattern;
4716 return retVal;