a patch for ttc fonts with bad index
[luatex.git] / source / libs / poppler / poppler-0.36.0 / splash / Splash.cc
blob7453822d291c8cffc5d3c2a32573820300c95b5e
1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
7 //========================================================================
8 //
9 // Modified under the Poppler project - http://poppler.freedesktop.org
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
14 // Copyright (C) 2005-2015 Albert Astals Cid <aacid@kde.org>
15 // Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
16 // Copyright (C) 2010-2015 Thomas Freitag <Thomas.Freitag@alfa.de>
17 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
18 // Copyright (C) 2011-2013, 2015 William Bader <williambader@hotmail.com>
19 // Copyright (C) 2012 Markus Trippelsdorf <markus@trippelsdorf.de>
20 // Copyright (C) 2012 Adrian Johnson <ajohnson@redneon.com>
21 // Copyright (C) 2012 Matthias Kramm <kramm@quiss.org>
23 // To see a description of the changes please see the Changelog file that
24 // came with your tarball or type make ChangeLog if you are building from git
26 //========================================================================
28 #include <config.h>
30 #ifdef USE_GCC_PRAGMAS
31 #pragma implementation
32 #endif
34 #include <stdlib.h>
35 #include <string.h>
36 #include <limits.h>
37 #include <assert.h>
38 #include <math.h>
39 #include "goo/gmem.h"
40 #include "goo/GooLikely.h"
41 #include "goo/GooList.h"
42 #include "poppler/Error.h"
43 #include "SplashErrorCodes.h"
44 #include "SplashMath.h"
45 #include "SplashBitmap.h"
46 #include "SplashState.h"
47 #include "SplashPath.h"
48 #include "SplashXPath.h"
49 #include "SplashXPathScanner.h"
50 #include "SplashPattern.h"
51 #include "SplashScreen.h"
52 #include "SplashFont.h"
53 #include "SplashGlyphBitmap.h"
54 #include "Splash.h"
55 #include <algorithm>
57 //------------------------------------------------------------------------
59 #define splashAAGamma 1.5
61 // distance of Bezier control point from center for circle approximation
62 // = (4 * (sqrt(2) - 1) / 3) * r
63 #define bezierCircle ((SplashCoord)0.55228475)
64 #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
66 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
67 static inline Guchar div255(int x) {
68 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
71 // Clip x to lie in [0, 255].
72 static inline Guchar clip255(int x) {
73 return x < 0 ? 0 : x > 255 ? 255 : x;
76 template<typename T>
77 inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; }
79 // The PDF spec says that all pixels whose *centers* lie within the
80 // image target region get painted, so we want to round n+0.5 down to
81 // n. But this causes problems, e.g., with PDF files that fill a
82 // rectangle with black and then draw an image to the exact same
83 // rectangle, so we instead use the fill scan conversion rule.
84 // However, the correct rule works better for glyphs, so we also
85 // provide that option in fillImageMask.
86 #if 0
87 static inline int imgCoordMungeLower(SplashCoord x) {
88 return splashCeil(x + 0.5) - 1;
90 static inline int imgCoordMungeUpper(SplashCoord x) {
91 return splashCeil(x + 0.5) - 1;
93 #else
94 static inline int imgCoordMungeLower(SplashCoord x) {
95 return splashFloor(x);
97 static inline int imgCoordMungeUpper(SplashCoord x) {
98 return splashFloor(x) + 1;
100 static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) {
101 return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
103 static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) {
104 return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
106 #endif
108 // Used by drawImage and fillImageMask to divide the target
109 // quadrilateral into sections.
110 struct ImageSection {
111 int y0, y1; // actual y range
112 int ia0, ia1; // vertex indices for edge A
113 int ib0, ib1; // vertex indices for edge A
114 SplashCoord xa0, ya0, xa1, ya1; // edge A
115 SplashCoord dxdya; // slope of edge A
116 SplashCoord xb0, yb0, xb1, yb1; // edge B
117 SplashCoord dxdyb; // slope of edge B
120 //------------------------------------------------------------------------
121 // SplashPipe
122 //------------------------------------------------------------------------
124 #define splashPipeMaxStages 9
126 struct SplashPipe {
127 // pixel coordinates
128 int x, y;
130 // source pattern
131 SplashPattern *pattern;
133 // source alpha and color
134 Guchar aInput;
135 GBool usesShape;
136 SplashColorPtr cSrc;
137 SplashColor cSrcVal;
139 // non-isolated group alpha0
140 Guchar *alpha0Ptr;
142 // knockout groups
143 GBool knockout;
144 Guchar knockoutOpacity;
146 // soft mask
147 SplashColorPtr softMaskPtr;
149 // destination alpha and color
150 SplashColorPtr destColorPtr;
151 int destColorMask;
152 Guchar *destAlphaPtr;
154 // shape
155 Guchar shape;
157 // result alpha and color
158 GBool noTransparency;
159 SplashPipeResultColorCtrl resultColorCtrl;
161 // non-isolated group correction
162 GBool nonIsolatedGroup;
164 // the "run" function
165 void (Splash::*run)(SplashPipe *pipe);
168 SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
169 splashPipeResultColorNoAlphaBlendMono,
170 splashPipeResultColorNoAlphaBlendMono,
171 splashPipeResultColorNoAlphaBlendRGB,
172 splashPipeResultColorNoAlphaBlendRGB,
173 splashPipeResultColorNoAlphaBlendRGB
174 #if SPLASH_CMYK
176 splashPipeResultColorNoAlphaBlendCMYK,
177 splashPipeResultColorNoAlphaBlendDeviceN
178 #endif
181 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
182 splashPipeResultColorAlphaNoBlendMono,
183 splashPipeResultColorAlphaNoBlendMono,
184 splashPipeResultColorAlphaNoBlendRGB,
185 splashPipeResultColorAlphaNoBlendRGB,
186 splashPipeResultColorAlphaNoBlendRGB
187 #if SPLASH_CMYK
189 splashPipeResultColorAlphaNoBlendCMYK,
190 splashPipeResultColorAlphaNoBlendDeviceN
191 #endif
194 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
195 splashPipeResultColorAlphaBlendMono,
196 splashPipeResultColorAlphaBlendMono,
197 splashPipeResultColorAlphaBlendRGB,
198 splashPipeResultColorAlphaBlendRGB,
199 splashPipeResultColorAlphaBlendRGB
200 #if SPLASH_CMYK
202 splashPipeResultColorAlphaBlendCMYK,
203 splashPipeResultColorAlphaBlendDeviceN
204 #endif
207 //------------------------------------------------------------------------
209 static void blendXor(SplashColorPtr src, SplashColorPtr dest,
210 SplashColorPtr blend, SplashColorMode cm) {
211 int i;
213 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
214 blend[i] = src[i] ^ dest[i];
218 //------------------------------------------------------------------------
219 // modified region
220 //------------------------------------------------------------------------
222 void Splash::clearModRegion() {
223 modXMin = bitmap->getWidth();
224 modYMin = bitmap->getHeight();
225 modXMax = -1;
226 modYMax = -1;
229 inline void Splash::updateModX(int x) {
230 if (x < modXMin) {
231 modXMin = x;
233 if (x > modXMax) {
234 modXMax = x;
238 inline void Splash::updateModY(int y) {
239 if (y < modYMin) {
240 modYMin = y;
242 if (y > modYMax) {
243 modYMax = y;
247 //------------------------------------------------------------------------
248 // pipeline
249 //------------------------------------------------------------------------
251 inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
252 SplashPattern *pattern, SplashColorPtr cSrc,
253 Guchar aInput, GBool usesShape,
254 GBool nonIsolatedGroup,
255 GBool knockout, Guchar knockoutOpacity) {
256 pipeSetXY(pipe, x, y);
257 pipe->pattern = NULL;
259 // source color
260 if (pattern) {
261 if (pattern->isStatic()) {
262 pattern->getColor(x, y, pipe->cSrcVal);
263 } else {
264 pipe->pattern = pattern;
266 pipe->cSrc = pipe->cSrcVal;
267 } else {
268 pipe->cSrc = cSrc;
271 // source alpha
272 pipe->aInput = aInput;
273 pipe->usesShape = usesShape;
274 pipe->shape = 0;
276 // knockout
277 pipe->knockout = knockout;
278 pipe->knockoutOpacity = knockoutOpacity;
280 // result alpha
281 if (aInput == 255 && !state->softMask && !usesShape &&
282 !state->inNonIsolatedGroup && !nonIsolatedGroup) {
283 pipe->noTransparency = gTrue;
284 } else {
285 pipe->noTransparency = gFalse;
288 // result color
289 if (pipe->noTransparency) {
290 // the !state->blendFunc case is handled separately in pipeRun
291 pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
292 } else if (!state->blendFunc) {
293 pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
294 } else {
295 pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
298 // non-isolated group correction
299 pipe->nonIsolatedGroup = nonIsolatedGroup;
301 // select the 'run' function
302 pipe->run = &Splash::pipeRun;
303 if (!pipe->pattern && pipe->noTransparency && !state->blendFunc) {
304 if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
305 pipe->run = &Splash::pipeRunSimpleMono1;
306 } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
307 pipe->run = &Splash::pipeRunSimpleMono8;
308 } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
309 pipe->run = &Splash::pipeRunSimpleRGB8;
310 } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
311 pipe->run = &Splash::pipeRunSimpleXBGR8;
312 } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
313 pipe->run = &Splash::pipeRunSimpleBGR8;
314 #if SPLASH_CMYK
315 } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
316 pipe->run = &Splash::pipeRunSimpleCMYK8;
317 } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
318 pipe->run = &Splash::pipeRunSimpleDeviceN8;
319 #endif
321 } else if (!pipe->pattern && !pipe->noTransparency && !state->softMask &&
322 pipe->usesShape &&
323 !(state->inNonIsolatedGroup && alpha0Bitmap->alpha) &&
324 !state->blendFunc && !pipe->nonIsolatedGroup) {
325 if (bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
326 pipe->run = &Splash::pipeRunAAMono1;
327 } else if (bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
328 pipe->run = &Splash::pipeRunAAMono8;
329 } else if (bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
330 pipe->run = &Splash::pipeRunAARGB8;
331 } else if (bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
332 pipe->run = &Splash::pipeRunAAXBGR8;
333 } else if (bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
334 pipe->run = &Splash::pipeRunAABGR8;
335 #if SPLASH_CMYK
336 } else if (bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
337 pipe->run = &Splash::pipeRunAACMYK8;
338 } else if (bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
339 pipe->run = &Splash::pipeRunAADeviceN8;
340 #endif
345 // general case
346 void Splash::pipeRun(SplashPipe *pipe) {
347 Guchar aSrc, aDest, alphaI, alphaIm1, alpha0, aResult;
348 SplashColor cSrcNonIso, cDest, cBlend;
349 SplashColorPtr cSrc;
350 Guchar cResult0, cResult1, cResult2, cResult3;
351 int t;
352 #if SPLASH_CMYK
353 int cp, mask;
354 Guchar cResult[SPOT_NCOMPS+4];
355 #endif
357 //----- source color
359 // static pattern: handled in pipeInit
360 // fixed color: handled in pipeInit
362 // dynamic pattern
363 if (pipe->pattern) {
364 if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) {
365 pipeIncX(pipe);
366 return;
368 #if SPLASH_CMYK
369 if (bitmap->mode == splashModeCMYK8 || bitmap->mode == splashModeDeviceN8) {
370 if (state->fillOverprint && state->overprintMode && pipe->pattern->isCMYK()) {
371 Guint mask = 15;
372 if (pipe->cSrcVal[0] == 0) {
373 mask &= ~1;
375 if (pipe->cSrcVal[1] == 0) {
376 mask &= ~2;
378 if (pipe->cSrcVal[2] == 0) {
379 mask &= ~4;
381 if (pipe->cSrcVal[3] == 0) {
382 mask &= ~8;
384 state->overprintMask = mask;
387 #endif
390 if (pipe->noTransparency && !state->blendFunc) {
392 //----- write destination pixel
394 switch (bitmap->mode) {
395 case splashModeMono1:
396 cResult0 = state->grayTransfer[pipe->cSrc[0]];
397 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
398 *pipe->destColorPtr |= pipe->destColorMask;
399 } else {
400 *pipe->destColorPtr &= ~pipe->destColorMask;
402 if (!(pipe->destColorMask >>= 1)) {
403 pipe->destColorMask = 0x80;
404 ++pipe->destColorPtr;
406 break;
407 case splashModeMono8:
408 *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
409 break;
410 case splashModeRGB8:
411 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
412 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
413 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
414 break;
415 case splashModeXBGR8:
416 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
417 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
418 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
419 *pipe->destColorPtr++ = 255;
420 break;
421 case splashModeBGR8:
422 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
423 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
424 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
425 break;
426 #if SPLASH_CMYK
427 case splashModeCMYK8:
428 if (state->overprintMask & 1) {
429 pipe->destColorPtr[0] = (state->overprintAdditive) ?
430 std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) :
431 state->cmykTransferC[pipe->cSrc[0]];
433 if (state->overprintMask & 2) {
434 pipe->destColorPtr[1] = (state->overprintAdditive) ?
435 std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) :
436 state->cmykTransferM[pipe->cSrc[1]];
438 if (state->overprintMask & 4) {
439 pipe->destColorPtr[2] = (state->overprintAdditive) ?
440 std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) :
441 state->cmykTransferY[pipe->cSrc[2]];
443 if (state->overprintMask & 8) {
444 pipe->destColorPtr[3] = (state->overprintAdditive) ?
445 std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) :
446 state->cmykTransferK[pipe->cSrc[3]];
448 pipe->destColorPtr += 4;
449 break;
450 case splashModeDeviceN8:
451 mask = 1;
452 for (cp = 0; cp < SPOT_NCOMPS + 4; cp ++) {
453 if (state->overprintMask & mask) {
454 pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
456 mask <<= 1;
458 pipe->destColorPtr += (SPOT_NCOMPS+4);
459 break;
460 #endif
462 if (pipe->destAlphaPtr) {
463 *pipe->destAlphaPtr++ = 255;
466 } else {
468 //----- read destination pixel
470 Guchar *destColorPtr;
471 if (pipe->shape && state->blendFunc && pipe->knockout && alpha0Bitmap != NULL) {
472 destColorPtr = alpha0Bitmap->data + (alpha0Y+pipe->y)*alpha0Bitmap->rowSize;
473 switch (bitmap->mode) {
474 case splashModeMono1:
475 destColorPtr += (alpha0X+pipe->x) / 8;
476 break;
477 case splashModeMono8:
478 destColorPtr += (alpha0X+pipe->x);
479 break;
480 case splashModeRGB8:
481 case splashModeBGR8:
482 destColorPtr += (alpha0X+pipe->x) * 3;
483 break;
484 case splashModeXBGR8:
485 #if SPLASH_CMYK
486 case splashModeCMYK8:
487 #endif
488 destColorPtr += (alpha0X+pipe->x) * 4;
489 break;
490 #if SPLASH_CMYK
491 case splashModeDeviceN8:
492 destColorPtr += (alpha0X+pipe->x) * (SPOT_NCOMPS + 4);
493 break;
494 #endif
496 } else {
497 destColorPtr = pipe->destColorPtr;
499 switch (bitmap->mode) {
500 case splashModeMono1:
501 cDest[0] = (*destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
502 break;
503 case splashModeMono8:
504 cDest[0] = *destColorPtr;
505 break;
506 case splashModeRGB8:
507 cDest[0] = destColorPtr[0];
508 cDest[1] = destColorPtr[1];
509 cDest[2] = destColorPtr[2];
510 break;
511 case splashModeXBGR8:
512 cDest[0] = destColorPtr[2];
513 cDest[1] = destColorPtr[1];
514 cDest[2] = destColorPtr[0];
515 cDest[3] = 255;
516 break;
517 case splashModeBGR8:
518 cDest[0] = destColorPtr[2];
519 cDest[1] = destColorPtr[1];
520 cDest[2] = destColorPtr[0];
521 break;
522 #if SPLASH_CMYK
523 case splashModeCMYK8:
524 cDest[0] = destColorPtr[0];
525 cDest[1] = destColorPtr[1];
526 cDest[2] = destColorPtr[2];
527 cDest[3] = destColorPtr[3];
528 break;
529 case splashModeDeviceN8:
530 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
531 cDest[cp] = destColorPtr[cp];
532 break;
533 #endif
535 if (pipe->destAlphaPtr) {
536 aDest = *pipe->destAlphaPtr;
537 } else {
538 aDest = 0xff;
541 //----- source alpha
543 if (state->softMask) {
544 if (pipe->usesShape) {
545 aSrc = div255(div255(pipe->aInput * *pipe->softMaskPtr++) *
546 pipe->shape);
547 } else {
548 aSrc = div255(pipe->aInput * *pipe->softMaskPtr++);
550 } else if (pipe->usesShape) {
551 aSrc = div255(pipe->aInput * pipe->shape);
552 } else {
553 aSrc = pipe->aInput;
556 //----- non-isolated group correction
558 if (pipe->nonIsolatedGroup) {
559 // This path is only used when Splash::composite() is called to
560 // composite a non-isolated group onto the backdrop. In this
561 // case, pipe->shape is the source (group) alpha.
562 if (pipe->shape == 0) {
563 // this value will be multiplied by zero later, so it doesn't
564 // matter what we use
565 cSrc = pipe->cSrc;
566 } else {
567 t = (aDest * 255) / pipe->shape - aDest;
568 switch (bitmap->mode) {
569 #if SPLASH_CMYK
570 case splashModeDeviceN8:
571 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++)
572 cSrcNonIso[cp] = clip255(pipe->cSrc[cp] +
573 ((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
574 break;
575 case splashModeCMYK8:
576 for (cp = 0; cp < 4; cp++)
577 cSrcNonIso[cp] = clip255(pipe->cSrc[cp] +
578 ((pipe->cSrc[cp] - cDest[cp]) * t) / 255);
579 break;
580 #endif
581 case splashModeXBGR8:
582 cSrcNonIso[3] = 255;
583 case splashModeRGB8:
584 case splashModeBGR8:
585 cSrcNonIso[2] = clip255(pipe->cSrc[2] +
586 ((pipe->cSrc[2] - cDest[2]) * t) / 255);
587 cSrcNonIso[1] = clip255(pipe->cSrc[1] +
588 ((pipe->cSrc[1] - cDest[1]) * t) / 255);
589 case splashModeMono1:
590 case splashModeMono8:
591 cSrcNonIso[0] = clip255(pipe->cSrc[0] +
592 ((pipe->cSrc[0] - cDest[0]) * t) / 255);
593 break;
595 cSrc = cSrcNonIso;
596 // knockout: remove backdrop color
597 if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) {
598 aDest = 0;
601 } else {
602 cSrc = pipe->cSrc;
605 //----- blend function
607 if (state->blendFunc) {
608 #if SPLASH_CMYK
609 if (bitmap->mode == splashModeDeviceN8) {
610 for (int k = 4; k < 4 + SPOT_NCOMPS; k++) {
611 cBlend[k] = 0;
614 #endif
615 (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
618 //----- result alpha and non-isolated group element correction
620 if (pipe->noTransparency) {
621 alphaI = alphaIm1 = aResult = 255;
622 } else {
623 aResult = aSrc + aDest - div255(aSrc * aDest);
625 // alphaI = alpha_i
626 // alphaIm1 = alpha_(i-1)
627 if (pipe->alpha0Ptr) {
628 alpha0 = *pipe->alpha0Ptr++;
629 alphaI = aResult + alpha0 - div255(aResult * alpha0);
630 alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
631 } else {
632 alphaI = aResult;
633 alphaIm1 = aDest;
637 //----- result color
639 cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
641 switch (pipe->resultColorCtrl) {
643 case splashPipeResultColorNoAlphaBlendMono:
644 cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] +
645 aDest * cBlend[0])];
646 break;
647 case splashPipeResultColorNoAlphaBlendRGB:
648 cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] +
649 aDest * cBlend[0])];
650 cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] +
651 aDest * cBlend[1])];
652 cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] +
653 aDest * cBlend[2])];
654 break;
655 #if SPLASH_CMYK
656 case splashPipeResultColorNoAlphaBlendCMYK:
657 cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] +
658 aDest * cBlend[0])];
659 cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] +
660 aDest * cBlend[1])];
661 cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] +
662 aDest * cBlend[2])];
663 cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
664 aDest * cBlend[3])];
665 break;
666 case splashPipeResultColorNoAlphaBlendDeviceN:
667 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
668 cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] +
669 aDest * cBlend[cp])];
670 break;
671 #endif
673 case splashPipeResultColorAlphaNoBlendMono:
674 if (alphaI == 0) {
675 cResult0 = 0;
676 } else {
677 cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
678 aSrc * cSrc[0]) / alphaI];
680 break;
681 case splashPipeResultColorAlphaNoBlendRGB:
682 if (alphaI == 0) {
683 cResult0 = 0;
684 cResult1 = 0;
685 cResult2 = 0;
686 } else {
687 cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
688 aSrc * cSrc[0]) / alphaI];
689 cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
690 aSrc * cSrc[1]) / alphaI];
691 cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
692 aSrc * cSrc[2]) / alphaI];
694 break;
695 #if SPLASH_CMYK
696 case splashPipeResultColorAlphaNoBlendCMYK:
697 if (alphaI == 0) {
698 cResult0 = 0;
699 cResult1 = 0;
700 cResult2 = 0;
701 cResult3 = 0;
702 } else {
703 cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
704 aSrc * cSrc[0]) / alphaI];
705 cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
706 aSrc * cSrc[1]) / alphaI];
707 cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
708 aSrc * cSrc[2]) / alphaI];
709 cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
710 aSrc * cSrc[3]) / alphaI];
712 break;
713 case splashPipeResultColorAlphaNoBlendDeviceN:
714 if (alphaI == 0) {
715 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
716 cResult[cp] = 0;
717 } else {
718 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
719 cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
720 aSrc * cSrc[cp]) / alphaI];
722 break;
723 #endif
725 case splashPipeResultColorAlphaBlendMono:
726 if (alphaI == 0) {
727 cResult0 = 0;
728 } else {
729 cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
730 aSrc * ((255 - alphaIm1) * cSrc[0] +
731 alphaIm1 * cBlend[0]) / 255) /
732 alphaI];
734 break;
735 case splashPipeResultColorAlphaBlendRGB:
736 if (alphaI == 0) {
737 cResult0 = 0;
738 cResult1 = 0;
739 cResult2 = 0;
740 } else {
741 cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
742 aSrc * ((255 - alphaIm1) * cSrc[0] +
743 alphaIm1 * cBlend[0]) / 255) /
744 alphaI];
745 cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
746 aSrc * ((255 - alphaIm1) * cSrc[1] +
747 alphaIm1 * cBlend[1]) / 255) /
748 alphaI];
749 cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
750 aSrc * ((255 - alphaIm1) * cSrc[2] +
751 alphaIm1 * cBlend[2]) / 255) /
752 alphaI];
754 break;
755 #if SPLASH_CMYK
756 case splashPipeResultColorAlphaBlendCMYK:
757 if (alphaI == 0) {
758 cResult0 = 0;
759 cResult1 = 0;
760 cResult2 = 0;
761 cResult3 = 0;
762 } else {
763 cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
764 aSrc * ((255 - alphaIm1) * cSrc[0] +
765 alphaIm1 * cBlend[0]) / 255) /
766 alphaI];
767 cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
768 aSrc * ((255 - alphaIm1) * cSrc[1] +
769 alphaIm1 * cBlend[1]) / 255) /
770 alphaI];
771 cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
772 aSrc * ((255 - alphaIm1) * cSrc[2] +
773 alphaIm1 * cBlend[2]) / 255) /
774 alphaI];
775 cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
776 aSrc * ((255 - alphaIm1) * cSrc[3] +
777 alphaIm1 * cBlend[3]) / 255) /
778 alphaI];
780 break;
781 case splashPipeResultColorAlphaBlendDeviceN:
782 if (alphaI == 0) {
783 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
784 cResult[cp] = 0;
785 } else {
786 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
787 cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
788 aSrc * ((255 - alphaIm1) * cSrc[cp] +
789 alphaIm1 * cBlend[cp]) / 255) /
790 alphaI];
792 break;
793 #endif
796 //----- write destination pixel
798 switch (bitmap->mode) {
799 case splashModeMono1:
800 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
801 *pipe->destColorPtr |= pipe->destColorMask;
802 } else {
803 *pipe->destColorPtr &= ~pipe->destColorMask;
805 if (!(pipe->destColorMask >>= 1)) {
806 pipe->destColorMask = 0x80;
807 ++pipe->destColorPtr;
809 break;
810 case splashModeMono8:
811 *pipe->destColorPtr++ = cResult0;
812 break;
813 case splashModeRGB8:
814 *pipe->destColorPtr++ = cResult0;
815 *pipe->destColorPtr++ = cResult1;
816 *pipe->destColorPtr++ = cResult2;
817 break;
818 case splashModeXBGR8:
819 *pipe->destColorPtr++ = cResult2;
820 *pipe->destColorPtr++ = cResult1;
821 *pipe->destColorPtr++ = cResult0;
822 *pipe->destColorPtr++ = 255;
823 break;
824 case splashModeBGR8:
825 *pipe->destColorPtr++ = cResult2;
826 *pipe->destColorPtr++ = cResult1;
827 *pipe->destColorPtr++ = cResult0;
828 break;
829 #if SPLASH_CMYK
830 case splashModeCMYK8:
831 if (state->overprintMask & 1) {
832 pipe->destColorPtr[0] = (state->overprintAdditive) ?
833 std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
834 cResult0;
836 if (state->overprintMask & 2) {
837 pipe->destColorPtr[1] = (state->overprintAdditive) ?
838 std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
839 cResult1;
841 if (state->overprintMask & 4) {
842 pipe->destColorPtr[2] = (state->overprintAdditive) ?
843 std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
844 cResult2;
846 if (state->overprintMask & 8) {
847 pipe->destColorPtr[3] = (state->overprintAdditive) ?
848 std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
849 cResult3;
851 pipe->destColorPtr += 4;
852 break;
853 case splashModeDeviceN8:
854 mask = 1;
855 for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
856 if (state->overprintMask & mask) {
857 pipe->destColorPtr[cp] = cResult[cp];
859 mask <<=1;
861 pipe->destColorPtr += (SPOT_NCOMPS+4);
862 break;
863 #endif
865 if (pipe->destAlphaPtr) {
866 *pipe->destAlphaPtr++ = aResult;
871 ++pipe->x;
874 // special case:
875 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
876 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
877 void Splash::pipeRunSimpleMono1(SplashPipe *pipe) {
878 Guchar cResult0;
880 //----- write destination pixel
881 cResult0 = state->grayTransfer[pipe->cSrc[0]];
882 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
883 *pipe->destColorPtr |= pipe->destColorMask;
884 } else {
885 *pipe->destColorPtr &= ~pipe->destColorMask;
887 if (!(pipe->destColorMask >>= 1)) {
888 pipe->destColorMask = 0x80;
889 ++pipe->destColorPtr;
892 ++pipe->x;
895 // special case:
896 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
897 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
898 void Splash::pipeRunSimpleMono8(SplashPipe *pipe) {
899 //----- write destination pixel
900 *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
901 *pipe->destAlphaPtr++ = 255;
903 ++pipe->x;
906 // special case:
907 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
908 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
909 void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) {
910 //----- write destination pixel
911 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
912 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
913 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
914 *pipe->destAlphaPtr++ = 255;
916 ++pipe->x;
919 // special case:
920 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
921 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
922 void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) {
923 //----- write destination pixel
924 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
925 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
926 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
927 *pipe->destColorPtr++ = 255;
928 *pipe->destAlphaPtr++ = 255;
930 ++pipe->x;
933 // special case:
934 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
935 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
936 void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) {
937 //----- write destination pixel
938 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
939 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
940 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
941 *pipe->destAlphaPtr++ = 255;
943 ++pipe->x;
946 #if SPLASH_CMYK
947 // special case:
948 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
949 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
950 void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) {
951 //----- write destination pixel
952 if (state->overprintMask & 1) {
953 pipe->destColorPtr[0] = (state->overprintAdditive) ?
954 std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) :
955 state->cmykTransferC[pipe->cSrc[0]];
957 if (state->overprintMask & 2) {
958 pipe->destColorPtr[1] = (state->overprintAdditive) ?
959 std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) :
960 state->cmykTransferM[pipe->cSrc[1]];
962 if (state->overprintMask & 4) {
963 pipe->destColorPtr[2] = (state->overprintAdditive) ?
964 std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) :
965 state->cmykTransferY[pipe->cSrc[2]];
967 if (state->overprintMask & 8) {
968 pipe->destColorPtr[3] = (state->overprintAdditive) ?
969 std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) :
970 state->cmykTransferK[pipe->cSrc[3]];
972 pipe->destColorPtr += 4;
973 *pipe->destAlphaPtr++ = 255;
975 ++pipe->x;
978 // special case:
979 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
980 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
981 void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) {
982 //----- write destination pixel
983 int mask = 1;
984 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) {
985 if (state->overprintMask & mask) {
986 pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
988 mask <<=1;
990 pipe->destColorPtr += (SPOT_NCOMPS+4);
991 *pipe->destAlphaPtr++ = 255;
993 ++pipe->x;
995 #endif
997 // special case:
998 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
999 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1000 // !pipe->nonIsolatedGroup &&
1001 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr
1002 void Splash::pipeRunAAMono1(SplashPipe *pipe) {
1003 Guchar aSrc;
1004 SplashColor cDest;
1005 Guchar cResult0;
1007 //----- read destination pixel
1008 cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
1010 //----- source alpha
1011 aSrc = div255(pipe->aInput * pipe->shape);
1013 //----- result color
1014 // note: aDest = alpha2 = aResult = 0xff
1015 cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] +
1016 aSrc * pipe->cSrc[0])];
1018 //----- write destination pixel
1019 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
1020 *pipe->destColorPtr |= pipe->destColorMask;
1021 } else {
1022 *pipe->destColorPtr &= ~pipe->destColorMask;
1024 if (!(pipe->destColorMask >>= 1)) {
1025 pipe->destColorMask = 0x80;
1026 ++pipe->destColorPtr;
1029 ++pipe->x;
1032 // special case:
1033 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1034 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1035 // !pipe->nonIsolatedGroup &&
1036 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr
1037 void Splash::pipeRunAAMono8(SplashPipe *pipe) {
1038 Guchar aSrc, aDest, alpha2, aResult;
1039 SplashColor cDest;
1040 Guchar cResult0;
1042 //----- read destination pixel
1043 cDest[0] = *pipe->destColorPtr;
1044 aDest = *pipe->destAlphaPtr;
1046 //----- source alpha
1047 aSrc = div255(pipe->aInput * pipe->shape);
1049 //----- result alpha and non-isolated group element correction
1050 aResult = aSrc + aDest - div255(aSrc * aDest);
1051 alpha2 = aResult;
1053 //----- result color
1054 if (alpha2 == 0) {
1055 cResult0 = 0;
1056 } else {
1057 cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1058 aSrc * pipe->cSrc[0]) / alpha2)];
1061 //----- write destination pixel
1062 *pipe->destColorPtr++ = cResult0;
1063 *pipe->destAlphaPtr++ = aResult;
1065 ++pipe->x;
1068 // special case:
1069 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1070 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1071 // !pipe->nonIsolatedGroup &&
1072 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr
1073 void Splash::pipeRunAARGB8(SplashPipe *pipe) {
1074 Guchar aSrc, aDest, alpha2, aResult;
1075 SplashColor cDest;
1076 Guchar cResult0, cResult1, cResult2;
1078 //----- read destination pixel
1079 cDest[0] = pipe->destColorPtr[0];
1080 cDest[1] = pipe->destColorPtr[1];
1081 cDest[2] = pipe->destColorPtr[2];
1082 aDest = *pipe->destAlphaPtr;
1084 //----- source alpha
1085 aSrc = div255(pipe->aInput * pipe->shape);
1087 //----- result alpha and non-isolated group element correction
1088 aResult = aSrc + aDest - div255(aSrc * aDest);
1089 alpha2 = aResult;
1091 //----- result color
1092 if (alpha2 == 0) {
1093 cResult0 = 0;
1094 cResult1 = 0;
1095 cResult2 = 0;
1096 } else {
1097 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1098 aSrc * pipe->cSrc[0]) / alpha2)];
1099 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1100 aSrc * pipe->cSrc[1]) / alpha2)];
1101 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1102 aSrc * pipe->cSrc[2]) / alpha2)];
1105 //----- write destination pixel
1106 *pipe->destColorPtr++ = cResult0;
1107 *pipe->destColorPtr++ = cResult1;
1108 *pipe->destColorPtr++ = cResult2;
1109 *pipe->destAlphaPtr++ = aResult;
1111 ++pipe->x;
1114 // special case:
1115 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1116 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1117 // !pipe->nonIsolatedGroup &&
1118 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr
1119 void Splash::pipeRunAAXBGR8(SplashPipe *pipe) {
1120 Guchar aSrc, aDest, alpha2, aResult;
1121 SplashColor cDest;
1122 Guchar cResult0, cResult1, cResult2;
1124 //----- read destination pixel
1125 cDest[0] = pipe->destColorPtr[2];
1126 cDest[1] = pipe->destColorPtr[1];
1127 cDest[2] = pipe->destColorPtr[0];
1128 aDest = *pipe->destAlphaPtr;
1130 //----- source alpha
1131 aSrc = div255(pipe->aInput * pipe->shape);
1133 //----- result alpha and non-isolated group element correction
1134 aResult = aSrc + aDest - div255(aSrc * aDest);
1135 alpha2 = aResult;
1137 //----- result color
1138 if (alpha2 == 0) {
1139 cResult0 = 0;
1140 cResult1 = 0;
1141 cResult2 = 0;
1142 } else {
1143 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1144 aSrc * pipe->cSrc[0]) / alpha2)];
1145 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1146 aSrc * pipe->cSrc[1]) / alpha2)];
1147 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1148 aSrc * pipe->cSrc[2]) / alpha2)];
1151 //----- write destination pixel
1152 *pipe->destColorPtr++ = cResult2;
1153 *pipe->destColorPtr++ = cResult1;
1154 *pipe->destColorPtr++ = cResult0;
1155 *pipe->destColorPtr++ = 255;
1156 *pipe->destAlphaPtr++ = aResult;
1158 ++pipe->x;
1161 // special case:
1162 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1163 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1164 // !pipe->nonIsolatedGroup &&
1165 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr
1166 void Splash::pipeRunAABGR8(SplashPipe *pipe) {
1167 Guchar aSrc, aDest, alpha2, aResult;
1168 SplashColor cDest;
1169 Guchar cResult0, cResult1, cResult2;
1171 //----- read destination pixel
1172 cDest[0] = pipe->destColorPtr[2];
1173 cDest[1] = pipe->destColorPtr[1];
1174 cDest[2] = pipe->destColorPtr[0];
1175 aDest = *pipe->destAlphaPtr;
1177 //----- source alpha
1178 aSrc = div255(pipe->aInput * pipe->shape);
1180 //----- result alpha and non-isolated group element correction
1181 aResult = aSrc + aDest - div255(aSrc * aDest);
1182 alpha2 = aResult;
1184 //----- result color
1185 if (alpha2 == 0) {
1186 cResult0 = 0;
1187 cResult1 = 0;
1188 cResult2 = 0;
1189 } else {
1190 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1191 aSrc * pipe->cSrc[0]) / alpha2)];
1192 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1193 aSrc * pipe->cSrc[1]) / alpha2)];
1194 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1195 aSrc * pipe->cSrc[2]) / alpha2)];
1198 //----- write destination pixel
1199 *pipe->destColorPtr++ = cResult2;
1200 *pipe->destColorPtr++ = cResult1;
1201 *pipe->destColorPtr++ = cResult0;
1202 *pipe->destAlphaPtr++ = aResult;
1204 ++pipe->x;
1207 #if SPLASH_CMYK
1208 // special case:
1209 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1210 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1211 // !pipe->nonIsolatedGroup &&
1212 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr
1213 void Splash::pipeRunAACMYK8(SplashPipe *pipe) {
1214 Guchar aSrc, aDest, alpha2, aResult;
1215 SplashColor cDest;
1216 Guchar cResult0, cResult1, cResult2, cResult3;
1218 //----- read destination pixel
1219 cDest[0] = pipe->destColorPtr[0];
1220 cDest[1] = pipe->destColorPtr[1];
1221 cDest[2] = pipe->destColorPtr[2];
1222 cDest[3] = pipe->destColorPtr[3];
1223 aDest = *pipe->destAlphaPtr;
1225 //----- source alpha
1226 aSrc = div255(pipe->aInput * pipe->shape);
1228 //----- result alpha and non-isolated group element correction
1229 aResult = aSrc + aDest - div255(aSrc * aDest);
1230 alpha2 = aResult;
1232 //----- result color
1233 if (alpha2 == 0) {
1234 cResult0 = 0;
1235 cResult1 = 0;
1236 cResult2 = 0;
1237 cResult3 = 0;
1238 } else {
1239 cResult0 = state->cmykTransferC[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1240 aSrc * pipe->cSrc[0]) / alpha2)];
1241 cResult1 = state->cmykTransferM[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1242 aSrc * pipe->cSrc[1]) / alpha2)];
1243 cResult2 = state->cmykTransferY[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1244 aSrc * pipe->cSrc[2]) / alpha2)];
1245 cResult3 = state->cmykTransferK[(Guchar)(((alpha2 - aSrc) * cDest[3] +
1246 aSrc * pipe->cSrc[3]) / alpha2)];
1249 //----- write destination pixel
1250 if (state->overprintMask & 1) {
1251 pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ?
1252 std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
1253 cResult0;
1255 if (state->overprintMask & 2) {
1256 pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ?
1257 std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
1258 cResult1;
1260 if (state->overprintMask & 4) {
1261 pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ?
1262 std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
1263 cResult2;
1265 if (state->overprintMask & 8) {
1266 pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ?
1267 std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
1268 cResult3;
1270 pipe->destColorPtr += 4;
1271 *pipe->destAlphaPtr++ = aResult;
1273 ++pipe->x;
1276 // special case:
1277 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1278 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1279 // !pipe->nonIsolatedGroup &&
1280 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr
1281 void Splash::pipeRunAADeviceN8(SplashPipe *pipe) {
1282 Guchar aSrc, aDest, alpha2, aResult;
1283 SplashColor cDest;
1284 Guchar cResult[SPOT_NCOMPS+4];
1285 int cp, mask;
1287 //----- read destination pixel
1288 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1289 cDest[cp] = pipe->destColorPtr[cp];
1290 aDest = *pipe->destAlphaPtr;
1292 //----- source alpha
1293 aSrc = div255(pipe->aInput * pipe->shape);
1295 //----- result alpha and non-isolated group element correction
1296 aResult = aSrc + aDest - div255(aSrc * aDest);
1297 alpha2 = aResult;
1299 //----- result color
1300 if (alpha2 == 0) {
1301 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1302 cResult[cp] = 0;
1303 } else {
1304 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1305 cResult[cp] = state->deviceNTransfer[cp][(Guchar)(((alpha2 - aSrc) * cDest[cp] +
1306 aSrc * pipe->cSrc[cp]) / alpha2)];
1309 //----- write destination pixel
1310 mask = 1;
1311 for (cp=0; cp < SPOT_NCOMPS+4; cp++) {
1312 if (state->overprintMask & mask) {
1313 pipe->destColorPtr[cp] = cResult[cp];
1315 mask <<= 1;
1317 pipe->destColorPtr += (SPOT_NCOMPS+4);
1318 *pipe->destAlphaPtr++ = aResult;
1320 ++pipe->x;
1322 #endif
1324 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
1325 pipe->x = x;
1326 pipe->y = y;
1327 if (state->softMask) {
1328 pipe->softMaskPtr =
1329 &state->softMask->data[y * state->softMask->rowSize + x];
1331 switch (bitmap->mode) {
1332 case splashModeMono1:
1333 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
1334 pipe->destColorMask = 0x80 >> (x & 7);
1335 break;
1336 case splashModeMono8:
1337 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
1338 break;
1339 case splashModeRGB8:
1340 case splashModeBGR8:
1341 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
1342 break;
1343 case splashModeXBGR8:
1344 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1345 break;
1346 #if SPLASH_CMYK
1347 case splashModeCMYK8:
1348 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1349 break;
1350 case splashModeDeviceN8:
1351 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x];
1352 break;
1353 #endif
1355 if (bitmap->alpha) {
1356 pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
1357 } else {
1358 pipe->destAlphaPtr = NULL;
1360 if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
1361 pipe->alpha0Ptr =
1362 &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
1363 (alpha0X + x)];
1364 } else {
1365 pipe->alpha0Ptr = NULL;
1369 inline void Splash::pipeIncX(SplashPipe *pipe) {
1370 ++pipe->x;
1371 if (state->softMask) {
1372 ++pipe->softMaskPtr;
1374 switch (bitmap->mode) {
1375 case splashModeMono1:
1376 if (!(pipe->destColorMask >>= 1)) {
1377 pipe->destColorMask = 0x80;
1378 ++pipe->destColorPtr;
1380 break;
1381 case splashModeMono8:
1382 ++pipe->destColorPtr;
1383 break;
1384 case splashModeRGB8:
1385 case splashModeBGR8:
1386 pipe->destColorPtr += 3;
1387 break;
1388 case splashModeXBGR8:
1389 pipe->destColorPtr += 4;
1390 break;
1391 #if SPLASH_CMYK
1392 case splashModeCMYK8:
1393 pipe->destColorPtr += 4;
1394 break;
1395 case splashModeDeviceN8:
1396 pipe->destColorPtr += (SPOT_NCOMPS+4);
1397 break;
1398 #endif
1400 if (pipe->destAlphaPtr) {
1401 ++pipe->destAlphaPtr;
1403 if (pipe->alpha0Ptr) {
1404 ++pipe->alpha0Ptr;
1408 inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
1409 if (unlikely(y < 0))
1410 return;
1412 if (noClip || state->clip->test(x, y)) {
1413 pipeSetXY(pipe, x, y);
1414 (this->*pipe->run)(pipe);
1415 updateModX(x);
1416 updateModY(y);
1420 inline void Splash::drawAAPixelInit() {
1421 aaBufY = -1;
1424 inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
1425 #if splashAASize == 4
1426 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1427 1, 2, 2, 3, 2, 3, 3, 4 };
1428 int w;
1429 #else
1430 int xx, yy;
1431 #endif
1432 SplashColorPtr p;
1433 int x0, x1, t;
1435 if (x < 0 || x >= bitmap->width ||
1436 y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
1437 return;
1440 // update aaBuf
1441 if (y != aaBufY) {
1442 memset(aaBuf->getDataPtr(), 0xff,
1443 aaBuf->getRowSize() * aaBuf->getHeight());
1444 x0 = 0;
1445 x1 = bitmap->width - 1;
1446 state->clip->clipAALine(aaBuf, &x0, &x1, y);
1447 aaBufY = y;
1450 // compute the shape value
1451 #if splashAASize == 4
1452 p = aaBuf->getDataPtr() + (x >> 1);
1453 w = aaBuf->getRowSize();
1454 if (x & 1) {
1455 t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
1456 bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
1457 } else {
1458 t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
1459 bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
1461 #else
1462 t = 0;
1463 for (yy = 0; yy < splashAASize; ++yy) {
1464 for (xx = 0; xx < splashAASize; ++xx) {
1465 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
1466 ((x * splashAASize + xx) >> 3);
1467 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1470 #endif
1472 // draw the pixel
1473 if (t != 0) {
1474 pipeSetXY(pipe, x, y);
1475 pipe->shape = div255(aaGamma[t] * pipe->shape);
1476 (this->*pipe->run)(pipe);
1477 updateModX(x);
1478 updateModY(y);
1482 inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
1483 GBool noClip) {
1484 int x;
1486 if (noClip) {
1487 pipeSetXY(pipe, x0, y);
1488 for (x = x0; x <= x1; ++x) {
1489 (this->*pipe->run)(pipe);
1491 updateModX(x0);
1492 updateModX(x1);
1493 updateModY(y);
1494 } else {
1495 if (x0 < state->clip->getXMinI()) {
1496 x0 = state->clip->getXMinI();
1498 if (x1 > state->clip->getXMaxI()) {
1499 x1 = state->clip->getXMaxI();
1501 pipeSetXY(pipe, x0, y);
1502 for (x = x0; x <= x1; ++x) {
1503 if (state->clip->test(x, y)) {
1504 (this->*pipe->run)(pipe);
1505 updateModX(x);
1506 updateModY(y);
1507 } else {
1508 pipeIncX(pipe);
1514 inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine, Guchar lineOpacity) {
1515 #if splashAASize == 4
1516 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1517 1, 2, 2, 3, 2, 3, 3, 4 };
1518 SplashColorPtr p0, p1, p2, p3;
1519 int t;
1520 #else
1521 SplashColorPtr p;
1522 int xx, yy, t;
1523 #endif
1524 int x;
1526 #if splashAASize == 4
1527 p0 = aaBuf->getDataPtr() + (x0 >> 1);
1528 p1 = p0 + aaBuf->getRowSize();
1529 p2 = p1 + aaBuf->getRowSize();
1530 p3 = p2 + aaBuf->getRowSize();
1531 #endif
1532 pipeSetXY(pipe, x0, y);
1533 for (x = x0; x <= x1; ++x) {
1535 // compute the shape value
1536 #if splashAASize == 4
1537 if (x & 1) {
1538 t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
1539 bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
1540 ++p0; ++p1; ++p2; ++p3;
1541 } else {
1542 t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
1543 bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
1545 #else
1546 t = 0;
1547 for (yy = 0; yy < splashAASize; ++yy) {
1548 for (xx = 0; xx < splashAASize; ++xx) {
1549 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
1550 ((x * splashAASize + xx) >> 3);
1551 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1554 #endif
1556 if (t != 0) {
1557 pipe->shape = (adjustLine) ? div255((int) lineOpacity * (double)aaGamma[t]) : (double)aaGamma[t];
1558 (this->*pipe->run)(pipe);
1559 updateModX(x);
1560 updateModY(y);
1561 } else {
1562 pipeIncX(pipe);
1567 //------------------------------------------------------------------------
1569 // Transform a point from user space to device space.
1570 inline void Splash::transform(SplashCoord *matrix,
1571 SplashCoord xi, SplashCoord yi,
1572 SplashCoord *xo, SplashCoord *yo) {
1573 // [ m[0] m[1] 0 ]
1574 // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
1575 // [ m[4] m[5] 1 ]
1576 *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
1577 *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
1580 //------------------------------------------------------------------------
1581 // Splash
1582 //------------------------------------------------------------------------
1584 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
1585 SplashScreenParams *screenParams) {
1586 int i;
1588 bitmap = bitmapA;
1589 vectorAntialias = vectorAntialiasA;
1590 inShading = gFalse;
1591 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
1592 screenParams);
1593 if (vectorAntialias) {
1594 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1595 1, splashModeMono1, gFalse);
1596 for (i = 0; i <= splashAASize * splashAASize; ++i) {
1597 aaGamma[i] = (Guchar)splashRound(
1598 splashPow((SplashCoord)i /
1599 (SplashCoord)(splashAASize * splashAASize),
1600 splashAAGamma) * 255);
1602 } else {
1603 aaBuf = NULL;
1605 minLineWidth = 0;
1606 thinLineMode = splashThinLineDefault;
1607 clearModRegion();
1608 debugMode = gFalse;
1609 alpha0Bitmap = NULL;
1612 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
1613 SplashScreen *screenA) {
1614 int i;
1616 bitmap = bitmapA;
1617 inShading = gFalse;
1618 vectorAntialias = vectorAntialiasA;
1619 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
1620 screenA);
1621 if (vectorAntialias) {
1622 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1623 1, splashModeMono1, gFalse);
1624 for (i = 0; i <= splashAASize * splashAASize; ++i) {
1625 aaGamma[i] = (Guchar)splashRound(
1626 splashPow((SplashCoord)i /
1627 (SplashCoord)(splashAASize * splashAASize),
1628 splashAAGamma) * 255);
1630 } else {
1631 aaBuf = NULL;
1633 minLineWidth = 0;
1634 thinLineMode = splashThinLineDefault;
1635 clearModRegion();
1636 debugMode = gFalse;
1637 alpha0Bitmap = NULL;
1640 Splash::~Splash() {
1641 while (state->next) {
1642 restoreState();
1644 delete state;
1645 if (vectorAntialias) {
1646 delete aaBuf;
1650 //------------------------------------------------------------------------
1651 // state read
1652 //------------------------------------------------------------------------
1654 SplashCoord *Splash::getMatrix() {
1655 return state->matrix;
1658 SplashPattern *Splash::getStrokePattern() {
1659 return state->strokePattern;
1662 SplashPattern *Splash::getFillPattern() {
1663 return state->fillPattern;
1666 SplashScreen *Splash::getScreen() {
1667 return state->screen;
1670 SplashBlendFunc Splash::getBlendFunc() {
1671 return state->blendFunc;
1674 SplashCoord Splash::getStrokeAlpha() {
1675 return state->strokeAlpha;
1678 SplashCoord Splash::getFillAlpha() {
1679 return state->fillAlpha;
1682 SplashCoord Splash::getLineWidth() {
1683 return state->lineWidth;
1686 int Splash::getLineCap() {
1687 return state->lineCap;
1690 int Splash::getLineJoin() {
1691 return state->lineJoin;
1694 SplashCoord Splash::getMiterLimit() {
1695 return state->miterLimit;
1698 SplashCoord Splash::getFlatness() {
1699 return state->flatness;
1702 SplashCoord *Splash::getLineDash() {
1703 return state->lineDash;
1706 int Splash::getLineDashLength() {
1707 return state->lineDashLength;
1710 SplashCoord Splash::getLineDashPhase() {
1711 return state->lineDashPhase;
1714 GBool Splash::getStrokeAdjust() {
1715 return state->strokeAdjust;
1718 SplashClip *Splash::getClip() {
1719 return state->clip;
1722 SplashBitmap *Splash::getSoftMask() {
1723 return state->softMask;
1726 GBool Splash::getInNonIsolatedGroup() {
1727 return state->inNonIsolatedGroup;
1730 //------------------------------------------------------------------------
1731 // state write
1732 //------------------------------------------------------------------------
1734 void Splash::setMatrix(SplashCoord *matrix) {
1735 memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
1738 void Splash::setStrokePattern(SplashPattern *strokePattern) {
1739 state->setStrokePattern(strokePattern);
1742 void Splash::setFillPattern(SplashPattern *fillPattern) {
1743 state->setFillPattern(fillPattern);
1746 void Splash::setScreen(SplashScreen *screen) {
1747 state->setScreen(screen);
1750 void Splash::setBlendFunc(SplashBlendFunc func) {
1751 state->blendFunc = func;
1754 void Splash::setStrokeAlpha(SplashCoord alpha) {
1755 state->strokeAlpha = alpha;
1758 void Splash::setFillAlpha(SplashCoord alpha) {
1759 state->fillAlpha = alpha;
1762 void Splash::setFillOverprint(GBool fop) {
1763 state->fillOverprint = fop;
1766 void Splash::setStrokeOverprint(GBool gop) {
1767 state->strokeOverprint = gop;
1770 void Splash::setOverprintMode(int opm) {
1771 state->overprintMode = opm;
1774 void Splash::setLineWidth(SplashCoord lineWidth) {
1775 state->lineWidth = lineWidth;
1778 void Splash::setLineCap(int lineCap) {
1779 state->lineCap = lineCap;
1782 void Splash::setLineJoin(int lineJoin) {
1783 state->lineJoin = lineJoin;
1786 void Splash::setMiterLimit(SplashCoord miterLimit) {
1787 state->miterLimit = miterLimit;
1790 void Splash::setFlatness(SplashCoord flatness) {
1791 if (flatness < 1) {
1792 state->flatness = 1;
1793 } else {
1794 state->flatness = flatness;
1798 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
1799 SplashCoord lineDashPhase) {
1800 state->setLineDash(lineDash, lineDashLength, lineDashPhase);
1803 void Splash::setStrokeAdjust(GBool strokeAdjust) {
1804 state->strokeAdjust = strokeAdjust;
1807 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
1808 SplashCoord x1, SplashCoord y1) {
1809 state->clip->resetToRect(x0, y0, x1, y1);
1812 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
1813 SplashCoord x1, SplashCoord y1) {
1814 return state->clip->clipToRect(x0, y0, x1, y1);
1817 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
1818 return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
1821 void Splash::setSoftMask(SplashBitmap *softMask) {
1822 state->setSoftMask(softMask);
1825 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
1826 int alpha0XA, int alpha0YA) {
1827 alpha0Bitmap = alpha0BitmapA;
1828 alpha0X = alpha0XA;
1829 alpha0Y = alpha0YA;
1830 state->inNonIsolatedGroup = gTrue;
1833 void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
1834 Guchar *gray) {
1835 state->setTransfer(red, green, blue, gray);
1838 void Splash::setOverprintMask(Guint overprintMask, GBool additive) {
1839 state->overprintMask = overprintMask;
1840 state->overprintAdditive = additive;
1843 //------------------------------------------------------------------------
1844 // state save/restore
1845 //------------------------------------------------------------------------
1847 void Splash::saveState() {
1848 SplashState *newState;
1850 newState = state->copy();
1851 newState->next = state;
1852 state = newState;
1855 SplashError Splash::restoreState() {
1856 SplashState *oldState;
1858 if (!state->next) {
1859 return splashErrNoSave;
1861 oldState = state;
1862 state = state->next;
1863 delete oldState;
1864 return splashOk;
1867 //------------------------------------------------------------------------
1868 // drawing operations
1869 //------------------------------------------------------------------------
1871 void Splash::clear(SplashColorPtr color, Guchar alpha) {
1872 SplashColorPtr row, p;
1873 Guchar mono;
1874 int x, y;
1876 switch (bitmap->mode) {
1877 case splashModeMono1:
1878 mono = (color[0] & 0x80) ? 0xff : 0x00;
1879 if (bitmap->rowSize < 0) {
1880 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1881 mono, -bitmap->rowSize * bitmap->height);
1882 } else {
1883 memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1885 break;
1886 case splashModeMono8:
1887 if (bitmap->rowSize < 0) {
1888 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1889 color[0], -bitmap->rowSize * bitmap->height);
1890 } else {
1891 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1893 break;
1894 case splashModeRGB8:
1895 if (color[0] == color[1] && color[1] == color[2]) {
1896 if (bitmap->rowSize < 0) {
1897 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1898 color[0], -bitmap->rowSize * bitmap->height);
1899 } else {
1900 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1902 } else {
1903 row = bitmap->data;
1904 for (y = 0; y < bitmap->height; ++y) {
1905 p = row;
1906 for (x = 0; x < bitmap->width; ++x) {
1907 *p++ = color[2];
1908 *p++ = color[1];
1909 *p++ = color[0];
1911 row += bitmap->rowSize;
1914 break;
1915 case splashModeXBGR8:
1916 if (color[0] == color[1] && color[1] == color[2]) {
1917 if (bitmap->rowSize < 0) {
1918 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1919 color[0], -bitmap->rowSize * bitmap->height);
1920 } else {
1921 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1923 } else {
1924 row = bitmap->data;
1925 for (y = 0; y < bitmap->height; ++y) {
1926 p = row;
1927 for (x = 0; x < bitmap->width; ++x) {
1928 *p++ = color[0];
1929 *p++ = color[1];
1930 *p++ = color[2];
1931 *p++ = 255;
1933 row += bitmap->rowSize;
1936 break;
1937 case splashModeBGR8:
1938 if (color[0] == color[1] && color[1] == color[2]) {
1939 if (bitmap->rowSize < 0) {
1940 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1941 color[0], -bitmap->rowSize * bitmap->height);
1942 } else {
1943 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1945 } else {
1946 row = bitmap->data;
1947 for (y = 0; y < bitmap->height; ++y) {
1948 p = row;
1949 for (x = 0; x < bitmap->width; ++x) {
1950 *p++ = color[0];
1951 *p++ = color[1];
1952 *p++ = color[2];
1954 row += bitmap->rowSize;
1957 break;
1958 #if SPLASH_CMYK
1959 case splashModeCMYK8:
1960 if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1961 if (bitmap->rowSize < 0) {
1962 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1963 color[0], -bitmap->rowSize * bitmap->height);
1964 } else {
1965 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1967 } else {
1968 row = bitmap->data;
1969 for (y = 0; y < bitmap->height; ++y) {
1970 p = row;
1971 for (x = 0; x < bitmap->width; ++x) {
1972 *p++ = color[0];
1973 *p++ = color[1];
1974 *p++ = color[2];
1975 *p++ = color[3];
1977 row += bitmap->rowSize;
1980 break;
1981 case splashModeDeviceN8:
1982 row = bitmap->data;
1983 for (y = 0; y < bitmap->height; ++y) {
1984 p = row;
1985 for (x = 0; x < bitmap->width; ++x) {
1986 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
1987 *p++ = color[cp];
1989 row += bitmap->rowSize;
1991 break;
1992 #endif
1995 if (bitmap->alpha) {
1996 memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
1999 updateModX(0);
2000 updateModY(0);
2001 updateModX(bitmap->width - 1);
2002 updateModY(bitmap->height - 1);
2005 SplashError Splash::stroke(SplashPath *path) {
2006 SplashPath *path2, *dPath;
2007 SplashCoord d1, d2, t1, t2, w;
2009 if (debugMode) {
2010 printf("stroke [dash:%d] [width:%.2f]:\n",
2011 state->lineDashLength, (double)state->lineWidth);
2012 dumpPath(path);
2014 opClipRes = splashClipAllOutside;
2015 if (path->length == 0) {
2016 return splashErrEmptyPath;
2018 path2 = flattenPath(path, state->matrix, state->flatness);
2019 if (state->lineDashLength > 0) {
2020 dPath = makeDashedPath(path2);
2021 delete path2;
2022 path2 = dPath;
2023 if (path2->length == 0) {
2024 delete path2;
2025 return splashErrEmptyPath;
2029 // transform a unit square, and take the half the max of the two
2030 // diagonals; the product of this number and the line width is the
2031 // (approximate) transformed line width
2032 t1 = state->matrix[0] + state->matrix[2];
2033 t2 = state->matrix[1] + state->matrix[3];
2034 d1 = t1 * t1 + t2 * t2;
2035 t1 = state->matrix[0] - state->matrix[2];
2036 t2 = state->matrix[1] - state->matrix[3];
2037 d2 = t1 * t1 + t2 * t2;
2038 if (d2 > d1) {
2039 d1 = d2;
2041 d1 *= 0.5;
2042 if (d1 > 0 &&
2043 d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
2044 w = minLineWidth / splashSqrt(d1);
2045 strokeWide(path2, w);
2046 } else if (bitmap->mode == splashModeMono1) {
2047 // this gets close to Adobe's behavior in mono mode
2048 if (d1 * state->lineWidth <= 2) {
2049 strokeNarrow(path2);
2050 } else {
2051 strokeWide(path2, state->lineWidth);
2053 } else {
2054 if (state->lineWidth == 0) {
2055 strokeNarrow(path2);
2056 } else {
2057 strokeWide(path2, state->lineWidth);
2061 delete path2;
2062 return splashOk;
2065 void Splash::strokeNarrow(SplashPath *path) {
2066 SplashPipe pipe;
2067 SplashXPath *xPath;
2068 SplashXPathSeg *seg;
2069 int x0, x1, y0, y1, xa, xb, y;
2070 SplashCoord dxdy;
2071 SplashClipResult clipRes;
2072 int nClipRes[3];
2073 int i;
2075 nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
2077 xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
2079 pipeInit(&pipe, 0, 0, state->strokePattern, NULL,
2080 (Guchar)splashRound(state->strokeAlpha * 255),
2081 gFalse, gFalse);
2083 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
2084 if (seg->y0 <= seg->y1) {
2085 y0 = splashFloor(seg->y0);
2086 y1 = splashFloor(seg->y1);
2087 x0 = splashFloor(seg->x0);
2088 x1 = splashFloor(seg->x1);
2089 } else {
2090 y0 = splashFloor(seg->y1);
2091 y1 = splashFloor(seg->y0);
2092 x0 = splashFloor(seg->x1);
2093 x1 = splashFloor(seg->x0);
2095 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
2096 x0 <= x1 ? x1 : x0, y1))
2097 != splashClipAllOutside) {
2098 if (y0 == y1) {
2099 if (x0 <= x1) {
2100 drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
2101 } else {
2102 drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
2104 } else {
2105 dxdy = seg->dxdy;
2106 if (y0 < state->clip->getYMinI()) {
2107 y0 = state->clip->getYMinI();
2108 x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
2110 if (y1 > state->clip->getYMaxI()) {
2111 y1 = state->clip->getYMaxI();
2112 x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
2114 if (x0 <= x1) {
2115 xa = x0;
2116 for (y = y0; y <= y1; ++y) {
2117 if (y < y1) {
2118 xb = splashFloor(seg->x0 +
2119 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2120 } else {
2121 xb = x1 + 1;
2123 if (xa == xb) {
2124 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2125 } else {
2126 drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
2128 xa = xb;
2130 } else {
2131 xa = x0;
2132 for (y = y0; y <= y1; ++y) {
2133 if (y < y1) {
2134 xb = splashFloor(seg->x0 +
2135 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2136 } else {
2137 xb = x1 - 1;
2139 if (xa == xb) {
2140 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2141 } else {
2142 drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
2144 xa = xb;
2149 ++nClipRes[clipRes];
2151 if (nClipRes[splashClipPartial] ||
2152 (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
2153 opClipRes = splashClipPartial;
2154 } else if (nClipRes[splashClipAllInside]) {
2155 opClipRes = splashClipAllInside;
2156 } else {
2157 opClipRes = splashClipAllOutside;
2160 delete xPath;
2163 void Splash::strokeWide(SplashPath *path, SplashCoord w) {
2164 SplashPath *path2;
2166 path2 = makeStrokePath(path, w, gFalse);
2167 fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
2168 delete path2;
2171 SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
2172 SplashCoord flatness) {
2173 SplashPath *fPath;
2174 SplashCoord flatness2;
2175 Guchar flag;
2176 int i;
2178 fPath = new SplashPath();
2179 #if USE_FIXEDPOINT
2180 flatness2 = flatness;
2181 #else
2182 flatness2 = flatness * flatness;
2183 #endif
2184 i = 0;
2185 while (i < path->length) {
2186 flag = path->flags[i];
2187 if (flag & splashPathFirst) {
2188 fPath->moveTo(path->pts[i].x, path->pts[i].y);
2189 ++i;
2190 } else {
2191 if (flag & splashPathCurve) {
2192 flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
2193 path->pts[i ].x, path->pts[i ].y,
2194 path->pts[i+1].x, path->pts[i+1].y,
2195 path->pts[i+2].x, path->pts[i+2].y,
2196 matrix, flatness2, fPath);
2197 i += 3;
2198 } else {
2199 fPath->lineTo(path->pts[i].x, path->pts[i].y);
2200 ++i;
2202 if (path->flags[i-1] & splashPathClosed) {
2203 fPath->close();
2207 return fPath;
2210 void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
2211 SplashCoord x1, SplashCoord y1,
2212 SplashCoord x2, SplashCoord y2,
2213 SplashCoord x3, SplashCoord y3,
2214 SplashCoord *matrix, SplashCoord flatness2,
2215 SplashPath *fPath) {
2216 SplashCoord cx[splashMaxCurveSplits + 1][3];
2217 SplashCoord cy[splashMaxCurveSplits + 1][3];
2218 int cNext[splashMaxCurveSplits + 1];
2219 SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
2220 SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
2221 SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
2222 int p1, p2, p3;
2224 // initial segment
2225 p1 = 0;
2226 p2 = splashMaxCurveSplits;
2227 cx[p1][0] = x0; cy[p1][0] = y0;
2228 cx[p1][1] = x1; cy[p1][1] = y1;
2229 cx[p1][2] = x2; cy[p1][2] = y2;
2230 cx[p2][0] = x3; cy[p2][0] = y3;
2231 cNext[p1] = p2;
2233 while (p1 < splashMaxCurveSplits) {
2235 // get the next segment
2236 xl0 = cx[p1][0]; yl0 = cy[p1][0];
2237 xx1 = cx[p1][1]; yy1 = cy[p1][1];
2238 xx2 = cx[p1][2]; yy2 = cy[p1][2];
2239 p2 = cNext[p1];
2240 xr3 = cx[p2][0]; yr3 = cy[p2][0];
2242 // compute the distances (in device space) from the control points
2243 // to the midpoint of the straight line (this is a bit of a hack,
2244 // but it's much faster than computing the actual distances to the
2245 // line)
2246 transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
2247 transform(matrix, xx1, yy1, &tx, &ty);
2248 #if USE_FIXEDPOINT
2249 d1 = splashDist(tx, ty, mx, my);
2250 #else
2251 dx = tx - mx;
2252 dy = ty - my;
2253 d1 = dx*dx + dy*dy;
2254 #endif
2255 transform(matrix, xx2, yy2, &tx, &ty);
2256 #if USE_FIXEDPOINT
2257 d2 = splashDist(tx, ty, mx, my);
2258 #else
2259 dx = tx - mx;
2260 dy = ty - my;
2261 d2 = dx*dx + dy*dy;
2262 #endif
2264 // if the curve is flat enough, or no more subdivisions are
2265 // allowed, add the straight line segment
2266 if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
2267 fPath->lineTo(xr3, yr3);
2268 p1 = p2;
2270 // otherwise, subdivide the curve
2271 } else {
2272 xl1 = splashAvg(xl0, xx1);
2273 yl1 = splashAvg(yl0, yy1);
2274 xh = splashAvg(xx1, xx2);
2275 yh = splashAvg(yy1, yy2);
2276 xl2 = splashAvg(xl1, xh);
2277 yl2 = splashAvg(yl1, yh);
2278 xr2 = splashAvg(xx2, xr3);
2279 yr2 = splashAvg(yy2, yr3);
2280 xr1 = splashAvg(xh, xr2);
2281 yr1 = splashAvg(yh, yr2);
2282 xr0 = splashAvg(xl2, xr1);
2283 yr0 = splashAvg(yl2, yr1);
2284 // add the new subdivision points
2285 p3 = (p1 + p2) / 2;
2286 cx[p1][1] = xl1; cy[p1][1] = yl1;
2287 cx[p1][2] = xl2; cy[p1][2] = yl2;
2288 cNext[p1] = p3;
2289 cx[p3][0] = xr0; cy[p3][0] = yr0;
2290 cx[p3][1] = xr1; cy[p3][1] = yr1;
2291 cx[p3][2] = xr2; cy[p3][2] = yr2;
2292 cNext[p3] = p2;
2297 SplashPath *Splash::makeDashedPath(SplashPath *path) {
2298 SplashPath *dPath;
2299 SplashCoord lineDashTotal;
2300 SplashCoord lineDashStartPhase, lineDashDist, segLen;
2301 SplashCoord x0, y0, x1, y1, xa, ya;
2302 GBool lineDashStartOn, lineDashOn, newPath;
2303 int lineDashStartIdx, lineDashIdx;
2304 int i, j, k;
2306 lineDashTotal = 0;
2307 for (i = 0; i < state->lineDashLength; ++i) {
2308 lineDashTotal += state->lineDash[i];
2310 // Acrobat simply draws nothing if the dash array is [0]
2311 if (lineDashTotal == 0) {
2312 return new SplashPath();
2314 lineDashStartPhase = state->lineDashPhase;
2315 i = splashFloor(lineDashStartPhase / lineDashTotal);
2316 lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
2317 lineDashStartOn = gTrue;
2318 lineDashStartIdx = 0;
2319 if (lineDashStartPhase > 0) {
2320 while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
2321 lineDashStartOn = !lineDashStartOn;
2322 lineDashStartPhase -= state->lineDash[lineDashStartIdx];
2323 ++lineDashStartIdx;
2325 if (unlikely(lineDashStartIdx == state->lineDashLength)) {
2326 return new SplashPath();
2330 dPath = new SplashPath();
2332 // process each subpath
2333 i = 0;
2334 while (i < path->length) {
2336 // find the end of the subpath
2337 for (j = i;
2338 j < path->length - 1 && !(path->flags[j] & splashPathLast);
2339 ++j) ;
2341 // initialize the dash parameters
2342 lineDashOn = lineDashStartOn;
2343 lineDashIdx = lineDashStartIdx;
2344 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
2346 // process each segment of the subpath
2347 newPath = gTrue;
2348 for (k = i; k < j; ++k) {
2350 // grab the segment
2351 x0 = path->pts[k].x;
2352 y0 = path->pts[k].y;
2353 x1 = path->pts[k+1].x;
2354 y1 = path->pts[k+1].y;
2355 segLen = splashDist(x0, y0, x1, y1);
2357 // process the segment
2358 while (segLen > 0) {
2360 if (lineDashDist >= segLen) {
2361 if (lineDashOn) {
2362 if (newPath) {
2363 dPath->moveTo(x0, y0);
2364 newPath = gFalse;
2366 dPath->lineTo(x1, y1);
2368 lineDashDist -= segLen;
2369 segLen = 0;
2371 } else {
2372 xa = x0 + (lineDashDist / segLen) * (x1 - x0);
2373 ya = y0 + (lineDashDist / segLen) * (y1 - y0);
2374 if (lineDashOn) {
2375 if (newPath) {
2376 dPath->moveTo(x0, y0);
2377 newPath = gFalse;
2379 dPath->lineTo(xa, ya);
2381 x0 = xa;
2382 y0 = ya;
2383 segLen -= lineDashDist;
2384 lineDashDist = 0;
2387 // get the next entry in the dash array
2388 if (lineDashDist <= 0) {
2389 lineDashOn = !lineDashOn;
2390 if (++lineDashIdx == state->lineDashLength) {
2391 lineDashIdx = 0;
2393 lineDashDist = state->lineDash[lineDashIdx];
2394 newPath = gTrue;
2398 i = j + 1;
2401 if (dPath->length == 0) {
2402 GBool allSame = gTrue;
2403 for (int i = 0; allSame && i < path->length - 1; ++i) {
2404 allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y;
2406 if (allSame) {
2407 x0 = path->pts[0].x;
2408 y0 = path->pts[0].y;
2409 dPath->moveTo(x0, y0);
2410 dPath->lineTo(x0, y0);
2414 return dPath;
2417 SplashError Splash::fill(SplashPath *path, GBool eo) {
2418 if (debugMode) {
2419 printf("fill [eo:%d]:\n", eo);
2420 dumpPath(path);
2422 return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
2425 inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA,
2426 SplashCoord *xMaxA, SplashCoord *yMaxA) {
2427 SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty;
2429 // make compiler happy:
2430 xMinFP = xMaxFP = yMinFP = yMaxFP = 0;
2431 for (int i = 0; i < path->length; ++i) {
2432 transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty);
2433 if (i == 0) {
2434 xMinFP = xMaxFP = tx;
2435 yMinFP = yMaxFP = ty;
2436 } else {
2437 if (tx < xMinFP) xMinFP = tx;
2438 if (tx > xMaxFP) xMaxFP = tx;
2439 if (ty < yMinFP) yMinFP = ty;
2440 if (ty > yMaxFP) yMaxFP = ty;
2444 *xMinA = xMinFP;
2445 *yMinA = yMinFP;
2446 *xMaxA = xMaxFP;
2447 *yMaxA = yMaxFP;
2450 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
2451 SplashPattern *pattern,
2452 SplashCoord alpha) {
2453 SplashPipe pipe;
2454 SplashXPath *xPath;
2455 SplashXPathScanner *scanner;
2456 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2457 SplashClipResult clipRes, clipRes2;
2458 GBool adjustLine = gFalse;
2459 int linePosI = 0;
2461 if (path->length == 0) {
2462 return splashErrEmptyPath;
2464 if (pathAllOutside(path)) {
2465 opClipRes = splashClipAllOutside;
2466 return splashOk;
2469 // add stroke adjustment hints for filled rectangles -- this only
2470 // applies to paths that consist of a single subpath
2471 // (this appears to match Acrobat's behavior)
2472 if (state->strokeAdjust && !path->hints) {
2473 int n;
2474 n = path->getLength();
2475 if (n == 4 &&
2476 !(path->flags[0] & splashPathClosed) &&
2477 !(path->flags[1] & splashPathLast) &&
2478 !(path->flags[2] & splashPathLast)) {
2479 path->close(gTrue);
2480 path->addStrokeAdjustHint(0, 2, 0, 4);
2481 path->addStrokeAdjustHint(1, 3, 0, 4);
2482 } else if (n == 5 &&
2483 (path->flags[0] & splashPathClosed) &&
2484 !(path->flags[1] & splashPathLast) &&
2485 !(path->flags[2] & splashPathLast) &&
2486 !(path->flags[3] & splashPathLast)) {
2487 path->addStrokeAdjustHint(0, 2, 0, 4);
2488 path->addStrokeAdjustHint(1, 3, 0, 4);
2492 if (thinLineMode != splashThinLineDefault) {
2493 if (state->clip->getXMinI() == state->clip->getXMaxI()) {
2494 linePosI = state->clip->getXMinI();
2495 adjustLine = gTrue;
2496 } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) {
2497 adjustLine = gTrue;
2498 linePosI = splashFloor(state->clip->getXMin() + state->lineWidth);
2499 } else if (state->clip->getYMinI() == state->clip->getYMaxI()) {
2500 linePosI = state->clip->getYMinI();
2501 adjustLine = gTrue;
2502 } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) {
2503 adjustLine = gTrue;
2504 linePosI = splashFloor(state->clip->getYMin() + state->lineWidth);
2508 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue,
2509 adjustLine, linePosI);
2510 if (vectorAntialias && !inShading) {
2511 xPath->aaScale();
2513 xPath->sort();
2514 yMinI = state->clip->getYMinI();
2515 yMaxI = state->clip->getYMaxI();
2516 if (vectorAntialias && !inShading) {
2517 yMinI = yMinI * splashAASize;
2518 yMaxI = (yMaxI + 1) * splashAASize - 1;
2520 scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI);
2522 // get the min and max x and y values
2523 if (vectorAntialias && !inShading) {
2524 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
2525 } else {
2526 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2529 if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) {
2530 SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP;
2531 getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP);
2532 delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP;
2533 if (delta < 0.2) {
2534 opClipRes = splashClipAllOutside;
2535 delete scanner;
2536 delete xPath;
2537 return splashOk;
2541 // check clipping
2542 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2543 != splashClipAllOutside) {
2544 if (scanner->hasPartialClip()) {
2545 clipRes = splashClipPartial;
2548 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255),
2549 vectorAntialias && !inShading, gFalse);
2551 // draw the spans
2552 if (vectorAntialias && !inShading) {
2553 for (y = yMinI; y <= yMaxI; ++y) {
2554 scanner->renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2555 if (clipRes != splashClipAllInside) {
2556 state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2558 Guchar lineShape = 255;
2559 GBool adjustLine = gFalse;
2560 if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) {
2561 // compute line shape for thin lines:
2562 SplashCoord mx, my, delta;
2563 transform(state->matrix, 0, 0, &mx, &my);
2564 transform(state->matrix, state->lineWidth, 0, &delta, &my);
2565 adjustLine = gTrue;
2566 lineShape = clip255((delta - mx) * 255);
2568 drawAALine(&pipe, x0, x1, y, adjustLine, lineShape);
2570 } else {
2571 for (y = yMinI; y <= yMaxI; ++y) {
2572 while (scanner->getNextSpan(y, &x0, &x1)) {
2573 if (clipRes == splashClipAllInside) {
2574 drawSpan(&pipe, x0, x1, y, gTrue);
2575 } else {
2576 // limit the x range
2577 if (x0 < state->clip->getXMinI()) {
2578 x0 = state->clip->getXMinI();
2580 if (x1 > state->clip->getXMaxI()) {
2581 x1 = state->clip->getXMaxI();
2583 clipRes2 = state->clip->testSpan(x0, x1, y);
2584 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2590 opClipRes = clipRes;
2592 delete scanner;
2593 delete xPath;
2594 return splashOk;
2597 GBool Splash::pathAllOutside(SplashPath *path) {
2598 SplashCoord xMin1, yMin1, xMax1, yMax1;
2599 SplashCoord xMin2, yMin2, xMax2, yMax2;
2600 SplashCoord x, y;
2601 int xMinI, yMinI, xMaxI, yMaxI;
2602 int i;
2604 xMin1 = xMax1 = path->pts[0].x;
2605 yMin1 = yMax1 = path->pts[0].y;
2606 for (i = 1; i < path->length; ++i) {
2607 if (path->pts[i].x < xMin1) {
2608 xMin1 = path->pts[i].x;
2609 } else if (path->pts[i].x > xMax1) {
2610 xMax1 = path->pts[i].x;
2612 if (path->pts[i].y < yMin1) {
2613 yMin1 = path->pts[i].y;
2614 } else if (path->pts[i].y > yMax1) {
2615 yMax1 = path->pts[i].y;
2619 transform(state->matrix, xMin1, yMin1, &x, &y);
2620 xMin2 = xMax2 = x;
2621 yMin2 = yMax2 = y;
2622 transform(state->matrix, xMin1, yMax1, &x, &y);
2623 if (x < xMin2) {
2624 xMin2 = x;
2625 } else if (x > xMax2) {
2626 xMax2 = x;
2628 if (y < yMin2) {
2629 yMin2 = y;
2630 } else if (y > yMax2) {
2631 yMax2 = y;
2633 transform(state->matrix, xMax1, yMin1, &x, &y);
2634 if (x < xMin2) {
2635 xMin2 = x;
2636 } else if (x > xMax2) {
2637 xMax2 = x;
2639 if (y < yMin2) {
2640 yMin2 = y;
2641 } else if (y > yMax2) {
2642 yMax2 = y;
2644 transform(state->matrix, xMax1, yMax1, &x, &y);
2645 if (x < xMin2) {
2646 xMin2 = x;
2647 } else if (x > xMax2) {
2648 xMax2 = x;
2650 if (y < yMin2) {
2651 yMin2 = y;
2652 } else if (y > yMax2) {
2653 yMax2 = y;
2655 xMinI = splashFloor(xMin2);
2656 yMinI = splashFloor(yMin2);
2657 xMaxI = splashFloor(xMax2);
2658 yMaxI = splashFloor(yMax2);
2660 return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) ==
2661 splashClipAllOutside;
2664 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
2665 SplashPipe pipe;
2666 SplashXPath *xPath;
2667 SplashXPathScanner *scanner;
2668 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2669 SplashClipResult clipRes, clipRes2;
2670 SplashBlendFunc origBlendFunc;
2672 if (path->length == 0) {
2673 return splashErrEmptyPath;
2675 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
2676 xPath->sort();
2677 scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(),
2678 state->clip->getYMaxI());
2680 // get the min and max x and y values
2681 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2683 // check clipping
2684 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2685 != splashClipAllOutside) {
2686 if (scanner->hasPartialClip()) {
2687 clipRes = splashClipPartial;
2690 origBlendFunc = state->blendFunc;
2691 state->blendFunc = &blendXor;
2692 pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse);
2694 // draw the spans
2695 for (y = yMinI; y <= yMaxI; ++y) {
2696 while (scanner->getNextSpan(y, &x0, &x1)) {
2697 if (clipRes == splashClipAllInside) {
2698 drawSpan(&pipe, x0, x1, y, gTrue);
2699 } else {
2700 // limit the x range
2701 if (x0 < state->clip->getXMinI()) {
2702 x0 = state->clip->getXMinI();
2704 if (x1 > state->clip->getXMaxI()) {
2705 x1 = state->clip->getXMaxI();
2707 clipRes2 = state->clip->testSpan(x0, x1, y);
2708 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2712 state->blendFunc = origBlendFunc;
2714 opClipRes = clipRes;
2716 delete scanner;
2717 delete xPath;
2718 return splashOk;
2721 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
2722 int c, SplashFont *font) {
2723 SplashGlyphBitmap glyph;
2724 SplashCoord xt, yt;
2725 int x0, y0, xFrac, yFrac;
2726 SplashClipResult clipRes;
2728 if (debugMode) {
2729 printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
2730 (double)x, (double)y, c, c, c);
2732 transform(state->matrix, x, y, &xt, &yt);
2733 x0 = splashFloor(xt);
2734 xFrac = splashFloor((xt - x0) * splashFontFraction);
2735 y0 = splashFloor(yt);
2736 yFrac = splashFloor((yt - y0) * splashFontFraction);
2737 if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
2738 return splashErrNoGlyph;
2740 if (clipRes != splashClipAllOutside) {
2741 fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
2743 opClipRes = clipRes;
2744 if (glyph.freeData) {
2745 gfree(glyph.data);
2747 return splashOk;
2750 void Splash::fillGlyph(SplashCoord x, SplashCoord y,
2751 SplashGlyphBitmap *glyph) {
2752 SplashCoord xt, yt;
2753 int x0, y0;
2755 transform(state->matrix, x, y, &xt, &yt);
2756 x0 = splashFloor(xt);
2757 y0 = splashFloor(yt);
2758 SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
2759 y0 - glyph->y,
2760 x0 - glyph->x + glyph->w - 1,
2761 y0 - glyph->y + glyph->h - 1);
2762 if (clipRes != splashClipAllOutside) {
2763 fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
2765 opClipRes = clipRes;
2768 void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
2769 SplashPipe pipe;
2770 int alpha0;
2771 Guchar alpha;
2772 Guchar *p;
2773 int x1, y1, xx, xx1, yy;
2775 p = glyph->data;
2776 int xStart = x0 - glyph->x;
2777 int yStart = y0 - glyph->y;
2778 int xxLimit = glyph->w;
2779 int yyLimit = glyph->h;
2780 int xShift = 0;
2782 if (yStart < 0)
2784 p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row
2785 yyLimit += yStart;
2786 yStart = 0;
2789 if (xStart < 0)
2791 if (glyph->aa) {
2792 p += -xStart;
2793 } else {
2794 p += (-xStart) / 8;
2795 xShift = (-xStart) % 8;
2797 xxLimit += xStart;
2798 xStart = 0;
2801 if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
2802 if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
2804 if (noClip) {
2805 if (glyph->aa) {
2806 pipeInit(&pipe, xStart, yStart,
2807 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2808 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2809 pipeSetXY(&pipe, xStart, y1);
2810 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2811 alpha = p[xx];
2812 if (alpha != 0) {
2813 pipe.shape = alpha;
2814 (this->*pipe.run)(&pipe);
2815 updateModX(x1);
2816 updateModY(y1);
2817 } else {
2818 pipeIncX(&pipe);
2821 p += glyph->w;
2823 } else {
2824 const int widthEight = splashCeil(glyph->w / 8.0);
2826 pipeInit(&pipe, xStart, yStart,
2827 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2828 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2829 pipeSetXY(&pipe, xStart, y1);
2830 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2831 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2832 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2833 if (alpha0 & 0x80) {
2834 (this->*pipe.run)(&pipe);
2835 updateModX(x1);
2836 updateModY(y1);
2837 } else {
2838 pipeIncX(&pipe);
2840 alpha0 <<= 1;
2843 p += widthEight;
2846 } else {
2847 if (glyph->aa) {
2848 pipeInit(&pipe, xStart, yStart,
2849 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2850 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2851 pipeSetXY(&pipe, xStart, y1);
2852 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2853 if (state->clip->test(x1, y1)) {
2854 alpha = p[xx];
2855 if (alpha != 0) {
2856 pipe.shape = alpha;
2857 (this->*pipe.run)(&pipe);
2858 updateModX(x1);
2859 updateModY(y1);
2860 } else {
2861 pipeIncX(&pipe);
2863 } else {
2864 pipeIncX(&pipe);
2867 p += glyph->w;
2869 } else {
2870 const int widthEight = splashCeil(glyph->w / 8.0);
2872 pipeInit(&pipe, xStart, yStart,
2873 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2874 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2875 pipeSetXY(&pipe, xStart, y1);
2876 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2877 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2878 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2879 if (state->clip->test(x1, y1)) {
2880 if (alpha0 & 0x80) {
2881 (this->*pipe.run)(&pipe);
2882 updateModX(x1);
2883 updateModY(y1);
2884 } else {
2885 pipeIncX(&pipe);
2887 } else {
2888 pipeIncX(&pipe);
2890 alpha0 <<= 1;
2893 p += widthEight;
2899 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
2900 int w, int h, SplashCoord *mat,
2901 GBool glyphMode) {
2902 SplashBitmap *scaledMask;
2903 SplashClipResult clipRes;
2904 GBool minorAxisZero;
2905 int x0, y0, x1, y1, scaledWidth, scaledHeight;
2906 int yp;
2908 if (debugMode) {
2909 printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2910 w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2911 (double)mat[3], (double)mat[4], (double)mat[5]);
2914 if (w == 0 && h == 0) return splashErrZeroImage;
2916 // check for singular matrix
2917 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
2918 return splashErrSingularMatrix;
2921 minorAxisZero = mat[1] == 0 && mat[2] == 0;
2923 // scaling only
2924 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
2925 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2926 y0 = imgCoordMungeLowerC(mat[5], glyphMode);
2927 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2928 y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
2929 // make sure narrow images cover at least one pixel
2930 if (x0 == x1) {
2931 ++x1;
2933 if (y0 == y1) {
2934 ++y1;
2936 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2937 opClipRes = clipRes;
2938 if (clipRes != splashClipAllOutside) {
2939 scaledWidth = x1 - x0;
2940 scaledHeight = y1 - y0;
2941 yp = h / scaledHeight;
2942 if (yp < 0 || yp > INT_MAX - 1) {
2943 return splashErrBadArg;
2945 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2946 blitMask(scaledMask, x0, y0, clipRes);
2947 delete scaledMask;
2950 // scaling plus vertical flip
2951 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
2952 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2953 y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
2954 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2955 y1 = imgCoordMungeUpperC(mat[5], glyphMode);
2956 // make sure narrow images cover at least one pixel
2957 if (x0 == x1) {
2958 ++x1;
2960 if (y0 == y1) {
2961 ++y1;
2963 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2964 opClipRes = clipRes;
2965 if (clipRes != splashClipAllOutside) {
2966 scaledWidth = x1 - x0;
2967 scaledHeight = y1 - y0;
2968 yp = h / scaledHeight;
2969 if (yp < 0 || yp > INT_MAX - 1) {
2970 return splashErrBadArg;
2972 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2973 vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
2974 blitMask(scaledMask, x0, y0, clipRes);
2975 delete scaledMask;
2978 // all other cases
2979 } else {
2980 arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
2983 return splashOk;
2986 void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
2987 int srcWidth, int srcHeight,
2988 SplashCoord *mat, GBool glyphMode) {
2989 SplashBitmap *scaledMask;
2990 SplashClipResult clipRes, clipRes2;
2991 SplashPipe pipe;
2992 int scaledWidth, scaledHeight, t0, t1;
2993 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
2994 SplashCoord vx[4], vy[4];
2995 int xMin, yMin, xMax, yMax;
2996 ImageSection section[3];
2997 int nSections;
2998 int y, xa, xb, x, i, xx, yy;
3000 // compute the four vertices of the target quadrilateral
3001 vx[0] = mat[4]; vy[0] = mat[5];
3002 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3003 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3004 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3006 // clipping
3007 xMin = imgCoordMungeLowerC(vx[0], glyphMode);
3008 xMax = imgCoordMungeUpperC(vx[0], glyphMode);
3009 yMin = imgCoordMungeLowerC(vy[0], glyphMode);
3010 yMax = imgCoordMungeUpperC(vy[0], glyphMode);
3011 for (i = 1; i < 4; ++i) {
3012 t0 = imgCoordMungeLowerC(vx[i], glyphMode);
3013 if (t0 < xMin) {
3014 xMin = t0;
3016 t0 = imgCoordMungeUpperC(vx[i], glyphMode);
3017 if (t0 > xMax) {
3018 xMax = t0;
3020 t1 = imgCoordMungeLowerC(vy[i], glyphMode);
3021 if (t1 < yMin) {
3022 yMin = t1;
3024 t1 = imgCoordMungeUpperC(vy[i], glyphMode);
3025 if (t1 > yMax) {
3026 yMax = t1;
3029 clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
3030 opClipRes = clipRes;
3031 if (clipRes == splashClipAllOutside) {
3032 return;
3035 // compute the scale factors
3036 if (mat[0] >= 0) {
3037 t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) -
3038 imgCoordMungeLowerC(mat[4], glyphMode);
3039 } else {
3040 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3041 imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
3043 if (mat[1] >= 0) {
3044 t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) -
3045 imgCoordMungeLowerC(mat[5], glyphMode);
3046 } else {
3047 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3048 imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
3050 scaledWidth = t0 > t1 ? t0 : t1;
3051 if (mat[2] >= 0) {
3052 t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) -
3053 imgCoordMungeLowerC(mat[4], glyphMode);
3054 } else {
3055 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3056 imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
3058 if (mat[3] >= 0) {
3059 t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) -
3060 imgCoordMungeLowerC(mat[5], glyphMode);
3061 } else {
3062 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3063 imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
3065 scaledHeight = t0 > t1 ? t0 : t1;
3066 if (scaledWidth == 0) {
3067 scaledWidth = 1;
3069 if (scaledHeight == 0) {
3070 scaledHeight = 1;
3073 // compute the inverse transform (after scaling) matrix
3074 r00 = mat[0] / scaledWidth;
3075 r01 = mat[1] / scaledWidth;
3076 r10 = mat[2] / scaledHeight;
3077 r11 = mat[3] / scaledHeight;
3078 det = r00 * r11 - r01 * r10;
3079 if (splashAbs(det) < 1e-6) {
3080 // this should be caught by the singular matrix check in fillImageMask
3081 return;
3083 ir00 = r11 / det;
3084 ir01 = -r01 / det;
3085 ir10 = -r10 / det;
3086 ir11 = r00 / det;
3088 // scale the input image
3089 scaledMask = scaleMask(src, srcData, srcWidth, srcHeight,
3090 scaledWidth, scaledHeight);
3091 if (scaledMask->data == NULL) {
3092 error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask");
3093 delete scaledMask;
3094 return;
3097 // construct the three sections
3098 i = (vy[2] <= vy[3]) ? 2 : 3;
3099 if (vy[1] <= vy[i]) {
3100 i = 1;
3102 if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
3103 i = 0;
3105 if (vy[i] == vy[(i+1) & 3]) {
3106 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3107 section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3108 if (vx[i] < vx[(i+1) & 3]) {
3109 section[0].ia0 = i;
3110 section[0].ia1 = (i+3) & 3;
3111 section[0].ib0 = (i+1) & 3;
3112 section[0].ib1 = (i+2) & 3;
3113 } else {
3114 section[0].ia0 = (i+1) & 3;
3115 section[0].ia1 = (i+2) & 3;
3116 section[0].ib0 = i;
3117 section[0].ib1 = (i+3) & 3;
3119 nSections = 1;
3120 } else {
3121 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3122 section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3123 section[0].ia0 = section[0].ib0 = i;
3124 section[2].ia1 = section[2].ib1 = (i+2) & 3;
3125 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3126 section[0].ia1 = section[2].ia0 = (i+1) & 3;
3127 section[0].ib1 = section[2].ib0 = (i+3) & 3;
3128 } else {
3129 section[0].ia1 = section[2].ia0 = (i+3) & 3;
3130 section[0].ib1 = section[2].ib0 = (i+1) & 3;
3132 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
3133 section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode);
3134 section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode);
3135 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3136 section[1].ia0 = (i+1) & 3;
3137 section[1].ia1 = (i+2) & 3;
3138 section[1].ib0 = i;
3139 section[1].ib1 = (i+3) & 3;
3140 } else {
3141 section[1].ia0 = i;
3142 section[1].ia1 = (i+3) & 3;
3143 section[1].ib0 = (i+1) & 3;
3144 section[1].ib1 = (i+2) & 3;
3146 } else {
3147 section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode);
3148 section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode);
3149 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3150 section[1].ia0 = i;
3151 section[1].ia1 = (i+1) & 3;
3152 section[1].ib0 = (i+3) & 3;
3153 section[1].ib1 = (i+2) & 3;
3154 } else {
3155 section[1].ia0 = (i+3) & 3;
3156 section[1].ia1 = (i+2) & 3;
3157 section[1].ib0 = i;
3158 section[1].ib1 = (i+1) & 3;
3161 section[0].y1 = section[1].y0 - 1;
3162 section[1].y1 = section[2].y0 - 1;
3163 nSections = 3;
3165 for (i = 0; i < nSections; ++i) {
3166 section[i].xa0 = vx[section[i].ia0];
3167 section[i].ya0 = vy[section[i].ia0];
3168 section[i].xa1 = vx[section[i].ia1];
3169 section[i].ya1 = vy[section[i].ia1];
3170 section[i].xb0 = vx[section[i].ib0];
3171 section[i].yb0 = vy[section[i].ib0];
3172 section[i].xb1 = vx[section[i].ib1];
3173 section[i].yb1 = vy[section[i].ib1];
3174 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
3175 (section[i].ya1 - section[i].ya0);
3176 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
3177 (section[i].yb1 - section[i].yb0);
3180 // initialize the pixel pipe
3181 pipeInit(&pipe, 0, 0, state->fillPattern, NULL,
3182 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3183 if (vectorAntialias) {
3184 drawAAPixelInit();
3187 // make sure narrow images cover at least one pixel
3188 if (nSections == 1) {
3189 if (section[0].y0 == section[0].y1) {
3190 ++section[0].y1;
3191 clipRes = opClipRes = splashClipPartial;
3193 } else {
3194 if (section[0].y0 == section[2].y1) {
3195 ++section[1].y1;
3196 clipRes = opClipRes = splashClipPartial;
3200 // scan all pixels inside the target region
3201 for (i = 0; i < nSections; ++i) {
3202 for (y = section[i].y0; y <= section[i].y1; ++y) {
3203 xa = imgCoordMungeLowerC(section[i].xa0 +
3204 ((SplashCoord)y + 0.5 - section[i].ya0) *
3205 section[i].dxdya,
3206 glyphMode);
3207 xb = imgCoordMungeUpperC(section[i].xb0 +
3208 ((SplashCoord)y + 0.5 - section[i].yb0) *
3209 section[i].dxdyb,
3210 glyphMode);
3211 if (unlikely(xa < 0))
3212 xa = 0;
3213 // make sure narrow images cover at least one pixel
3214 if (xa == xb) {
3215 ++xb;
3217 if (clipRes != splashClipAllInside) {
3218 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
3219 } else {
3220 clipRes2 = clipRes;
3222 for (x = xa; x < xb; ++x) {
3223 // map (x+0.5, y+0.5) back to the scaled image
3224 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
3225 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3226 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
3227 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3228 // xx should always be within bounds, but floating point
3229 // inaccuracy can cause problems
3230 if (xx < 0) {
3231 xx = 0;
3232 } else if (xx >= scaledWidth) {
3233 xx = scaledWidth - 1;
3235 if (yy < 0) {
3236 yy = 0;
3237 } else if (yy >= scaledHeight) {
3238 yy = scaledHeight - 1;
3240 pipe.shape = scaledMask->data[yy * scaledWidth + xx];
3241 if (vectorAntialias && clipRes2 != splashClipAllInside) {
3242 drawAAPixel(&pipe, x, y);
3243 } else {
3244 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
3250 delete scaledMask;
3253 // Scale an image mask into a SplashBitmap.
3254 SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
3255 int srcWidth, int srcHeight,
3256 int scaledWidth, int scaledHeight) {
3257 SplashBitmap *dest;
3259 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8,
3260 gFalse);
3261 if (scaledHeight < srcHeight) {
3262 if (scaledWidth < srcWidth) {
3263 scaleMaskYdXd(src, srcData, srcWidth, srcHeight,
3264 scaledWidth, scaledHeight, dest);
3265 } else {
3266 scaleMaskYdXu(src, srcData, srcWidth, srcHeight,
3267 scaledWidth, scaledHeight, dest);
3269 } else {
3270 if (scaledWidth < srcWidth) {
3271 scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
3272 scaledWidth, scaledHeight, dest);
3273 } else {
3274 scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
3275 scaledWidth, scaledHeight, dest);
3278 return dest;
3281 void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
3282 int srcWidth, int srcHeight,
3283 int scaledWidth, int scaledHeight,
3284 SplashBitmap *dest) {
3285 Guchar *lineBuf;
3286 Guint *pixBuf;
3287 Guint pix;
3288 Guchar *destPtr;
3289 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3290 int i, j;
3292 // Bresenham parameters for y scale
3293 yp = srcHeight / scaledHeight;
3294 yq = srcHeight % scaledHeight;
3296 // Bresenham parameters for x scale
3297 xp = srcWidth / scaledWidth;
3298 xq = srcWidth % scaledWidth;
3300 // allocate buffers
3301 lineBuf = (Guchar *)gmalloc(srcWidth);
3302 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3304 // init y scale Bresenham
3305 yt = 0;
3307 destPtr = dest->data;
3308 for (y = 0; y < scaledHeight; ++y) {
3310 // y scale Bresenham
3311 if ((yt += yq) >= scaledHeight) {
3312 yt -= scaledHeight;
3313 yStep = yp + 1;
3314 } else {
3315 yStep = yp;
3318 // read rows from image
3319 memset(pixBuf, 0, srcWidth * sizeof(int));
3320 for (i = 0; i < yStep; ++i) {
3321 (*src)(srcData, lineBuf);
3322 for (j = 0; j < srcWidth; ++j) {
3323 pixBuf[j] += lineBuf[j];
3327 // init x scale Bresenham
3328 xt = 0;
3329 d0 = (255 << 23) / (yStep * xp);
3330 d1 = (255 << 23) / (yStep * (xp + 1));
3332 xx = 0;
3333 for (x = 0; x < scaledWidth; ++x) {
3335 // x scale Bresenham
3336 if ((xt += xq) >= scaledWidth) {
3337 xt -= scaledWidth;
3338 xStep = xp + 1;
3339 d = d1;
3340 } else {
3341 xStep = xp;
3342 d = d0;
3345 // compute the final pixel
3346 pix = 0;
3347 for (i = 0; i < xStep; ++i) {
3348 pix += pixBuf[xx++];
3350 // (255 * pix) / xStep * yStep
3351 pix = (pix * d) >> 23;
3353 // store the pixel
3354 *destPtr++ = (Guchar)pix;
3358 gfree(pixBuf);
3359 gfree(lineBuf);
3362 void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
3363 int srcWidth, int srcHeight,
3364 int scaledWidth, int scaledHeight,
3365 SplashBitmap *dest) {
3366 Guchar *lineBuf;
3367 Guint *pixBuf;
3368 Guint pix;
3369 Guchar *destPtr;
3370 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
3371 int i, j;
3373 destPtr = dest->data;
3374 if (destPtr == NULL) {
3375 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdXu");
3376 return;
3379 // Bresenham parameters for y scale
3380 yp = srcHeight / scaledHeight;
3381 yq = srcHeight % scaledHeight;
3383 // Bresenham parameters for x scale
3384 xp = scaledWidth / srcWidth;
3385 xq = scaledWidth % srcWidth;
3387 // allocate buffers
3388 lineBuf = (Guchar *)gmalloc(srcWidth);
3389 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3391 // init y scale Bresenham
3392 yt = 0;
3394 for (y = 0; y < scaledHeight; ++y) {
3396 // y scale Bresenham
3397 if ((yt += yq) >= scaledHeight) {
3398 yt -= scaledHeight;
3399 yStep = yp + 1;
3400 } else {
3401 yStep = yp;
3404 // read rows from image
3405 memset(pixBuf, 0, srcWidth * sizeof(int));
3406 for (i = 0; i < yStep; ++i) {
3407 (*src)(srcData, lineBuf);
3408 for (j = 0; j < srcWidth; ++j) {
3409 pixBuf[j] += lineBuf[j];
3413 // init x scale Bresenham
3414 xt = 0;
3415 d = (255 << 23) / yStep;
3417 for (x = 0; x < srcWidth; ++x) {
3419 // x scale Bresenham
3420 if ((xt += xq) >= srcWidth) {
3421 xt -= srcWidth;
3422 xStep = xp + 1;
3423 } else {
3424 xStep = xp;
3427 // compute the final pixel
3428 pix = pixBuf[x];
3429 // (255 * pix) / yStep
3430 pix = (pix * d) >> 23;
3432 // store the pixel
3433 for (i = 0; i < xStep; ++i) {
3434 *destPtr++ = (Guchar)pix;
3439 gfree(pixBuf);
3440 gfree(lineBuf);
3443 void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
3444 int srcWidth, int srcHeight,
3445 int scaledWidth, int scaledHeight,
3446 SplashBitmap *dest) {
3447 Guchar *lineBuf;
3448 Guint pix;
3449 Guchar *destPtr0, *destPtr;
3450 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3451 int i;
3453 destPtr0 = dest->data;
3454 if (destPtr0 == NULL) {
3455 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXd");
3456 return;
3459 // Bresenham parameters for y scale
3460 yp = scaledHeight / srcHeight;
3461 yq = scaledHeight % srcHeight;
3463 // Bresenham parameters for x scale
3464 xp = srcWidth / scaledWidth;
3465 xq = srcWidth % scaledWidth;
3467 // allocate buffers
3468 lineBuf = (Guchar *)gmalloc(srcWidth);
3470 // init y scale Bresenham
3471 yt = 0;
3473 for (y = 0; y < srcHeight; ++y) {
3475 // y scale Bresenham
3476 if ((yt += yq) >= srcHeight) {
3477 yt -= srcHeight;
3478 yStep = yp + 1;
3479 } else {
3480 yStep = yp;
3483 // read row from image
3484 (*src)(srcData, lineBuf);
3486 // init x scale Bresenham
3487 xt = 0;
3488 d0 = (255 << 23) / xp;
3489 d1 = (255 << 23) / (xp + 1);
3491 xx = 0;
3492 for (x = 0; x < scaledWidth; ++x) {
3494 // x scale Bresenham
3495 if ((xt += xq) >= scaledWidth) {
3496 xt -= scaledWidth;
3497 xStep = xp + 1;
3498 d = d1;
3499 } else {
3500 xStep = xp;
3501 d = d0;
3504 // compute the final pixel
3505 pix = 0;
3506 for (i = 0; i < xStep; ++i) {
3507 pix += lineBuf[xx++];
3509 // (255 * pix) / xStep
3510 pix = (pix * d) >> 23;
3512 // store the pixel
3513 for (i = 0; i < yStep; ++i) {
3514 destPtr = destPtr0 + i * scaledWidth + x;
3515 *destPtr = (Guchar)pix;
3519 destPtr0 += yStep * scaledWidth;
3522 gfree(lineBuf);
3525 void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
3526 int srcWidth, int srcHeight,
3527 int scaledWidth, int scaledHeight,
3528 SplashBitmap *dest) {
3529 Guchar *lineBuf;
3530 Guint pix;
3531 Guchar *destPtr0, *destPtr;
3532 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
3533 int i, j;
3535 destPtr0 = dest->data;
3536 if (destPtr0 == NULL) {
3537 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXu");
3538 return;
3541 // Bresenham parameters for y scale
3542 yp = scaledHeight / srcHeight;
3543 yq = scaledHeight % srcHeight;
3545 // Bresenham parameters for x scale
3546 xp = scaledWidth / srcWidth;
3547 xq = scaledWidth % srcWidth;
3549 // allocate buffers
3550 lineBuf = (Guchar *)gmalloc(srcWidth);
3552 // init y scale Bresenham
3553 yt = 0;
3555 for (y = 0; y < srcHeight; ++y) {
3557 // y scale Bresenham
3558 if ((yt += yq) >= srcHeight) {
3559 yt -= srcHeight;
3560 yStep = yp + 1;
3561 } else {
3562 yStep = yp;
3565 // read row from image
3566 (*src)(srcData, lineBuf);
3568 // init x scale Bresenham
3569 xt = 0;
3571 xx = 0;
3572 for (x = 0; x < srcWidth; ++x) {
3574 // x scale Bresenham
3575 if ((xt += xq) >= srcWidth) {
3576 xt -= srcWidth;
3577 xStep = xp + 1;
3578 } else {
3579 xStep = xp;
3582 // compute the final pixel
3583 pix = lineBuf[x] ? 255 : 0;
3585 // store the pixel
3586 for (i = 0; i < yStep; ++i) {
3587 for (j = 0; j < xStep; ++j) {
3588 destPtr = destPtr0 + i * scaledWidth + xx + j;
3589 *destPtr++ = (Guchar)pix;
3593 xx += xStep;
3596 destPtr0 += yStep * scaledWidth;
3599 gfree(lineBuf);
3602 void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
3603 SplashClipResult clipRes) {
3604 SplashPipe pipe;
3605 Guchar *p;
3606 int w, h, x, y;
3608 w = src->getWidth();
3609 h = src->getHeight();
3610 p = src->getDataPtr();
3611 if (p == NULL) {
3612 error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask");
3613 return;
3615 if (vectorAntialias && clipRes != splashClipAllInside) {
3616 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3617 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3618 drawAAPixelInit();
3619 for (y = 0; y < h; ++y) {
3620 for (x = 0; x < w; ++x) {
3621 pipe.shape = *p++;
3622 drawAAPixel(&pipe, xDest + x, yDest + y);
3625 } else {
3626 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3627 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3628 if (clipRes == splashClipAllInside) {
3629 for (y = 0; y < h; ++y) {
3630 pipeSetXY(&pipe, xDest, yDest + y);
3631 for (x = 0; x < w; ++x) {
3632 if (*p) {
3633 pipe.shape = *p;
3634 (this->*pipe.run)(&pipe);
3635 } else {
3636 pipeIncX(&pipe);
3638 ++p;
3641 updateModX(xDest);
3642 updateModX(xDest + w - 1);
3643 updateModY(yDest);
3644 updateModY(yDest + h - 1);
3645 } else {
3646 for (y = 0; y < h; ++y) {
3647 pipeSetXY(&pipe, xDest, yDest + y);
3648 for (x = 0; x < w; ++x) {
3649 if (*p && state->clip->test(xDest + x, yDest + y)) {
3650 pipe.shape = *p;
3651 (this->*pipe.run)(&pipe);
3652 updateModX(xDest + x);
3653 updateModY(yDest + y);
3654 } else {
3655 pipeIncX(&pipe);
3657 ++p;
3664 SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3665 SplashColorMode srcMode, GBool srcAlpha,
3666 int w, int h, SplashCoord *mat, GBool interpolate,
3667 GBool tilingPattern) {
3668 GBool ok;
3669 SplashBitmap *scaledImg;
3670 SplashClipResult clipRes;
3671 GBool minorAxisZero;
3672 int x0, y0, x1, y1, scaledWidth, scaledHeight;
3673 int nComps;
3674 int yp;
3676 if (debugMode) {
3677 printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
3678 srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
3679 (double)mat[3], (double)mat[4], (double)mat[5]);
3682 // check color modes
3683 ok = gFalse; // make gcc happy
3684 nComps = 0; // make gcc happy
3685 switch (bitmap->mode) {
3686 case splashModeMono1:
3687 case splashModeMono8:
3688 ok = srcMode == splashModeMono8;
3689 nComps = 1;
3690 break;
3691 case splashModeRGB8:
3692 ok = srcMode == splashModeRGB8;
3693 nComps = 3;
3694 break;
3695 case splashModeXBGR8:
3696 ok = srcMode == splashModeXBGR8;
3697 nComps = 4;
3698 break;
3699 case splashModeBGR8:
3700 ok = srcMode == splashModeBGR8;
3701 nComps = 3;
3702 break;
3703 #if SPLASH_CMYK
3704 case splashModeCMYK8:
3705 ok = srcMode == splashModeCMYK8;
3706 nComps = 4;
3707 break;
3708 case splashModeDeviceN8:
3709 ok = srcMode == splashModeDeviceN8;
3710 nComps = SPOT_NCOMPS+4;
3711 break;
3712 #endif
3713 default:
3714 ok = gFalse;
3715 break;
3717 if (!ok) {
3718 return splashErrModeMismatch;
3721 // check for singular matrix
3722 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
3723 return splashErrSingularMatrix;
3726 minorAxisZero = mat[1] == 0 && mat[2] == 0;
3728 // scaling only
3729 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
3730 x0 = imgCoordMungeLower(mat[4]);
3731 y0 = imgCoordMungeLower(mat[5]);
3732 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3733 y1 = imgCoordMungeUpper(mat[3] + mat[5]);
3734 // make sure narrow images cover at least one pixel
3735 if (x0 == x1) {
3736 ++x1;
3738 if (y0 == y1) {
3739 ++y1;
3741 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3742 opClipRes = clipRes;
3743 if (clipRes != splashClipAllOutside) {
3744 scaledWidth = x1 - x0;
3745 scaledHeight = y1 - y0;
3746 yp = h / scaledHeight;
3747 if (yp < 0 || yp > INT_MAX - 1) {
3748 return splashErrBadArg;
3750 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3751 scaledWidth, scaledHeight, interpolate, tilingPattern);
3752 if (scaledImg == NULL) {
3753 return splashErrBadArg;
3755 if (tf != NULL) {
3756 (*tf)(srcData, scaledImg);
3758 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3759 delete scaledImg;
3762 // scaling plus vertical flip
3763 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
3764 x0 = imgCoordMungeLower(mat[4]);
3765 y0 = imgCoordMungeLower(mat[3] + mat[5]);
3766 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3767 y1 = imgCoordMungeUpper(mat[5]);
3768 if (x0 == x1) {
3769 if (mat[4] + mat[0] * 0.5 < x0) {
3770 --x0;
3771 } else {
3772 ++x1;
3775 if (y0 == y1) {
3776 if (mat[5] + mat[1] * 0.5 < y0) {
3777 --y0;
3778 } else {
3779 ++y1;
3782 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3783 opClipRes = clipRes;
3784 if (clipRes != splashClipAllOutside) {
3785 scaledWidth = x1 - x0;
3786 scaledHeight = y1 - y0;
3787 yp = h / scaledHeight;
3788 if (yp < 0 || yp > INT_MAX - 1) {
3789 return splashErrBadArg;
3791 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3792 scaledWidth, scaledHeight, interpolate, tilingPattern);
3793 if (scaledImg == NULL) {
3794 return splashErrBadArg;
3796 if (tf != NULL) {
3797 (*tf)(srcData, scaledImg);
3799 vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
3800 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3801 delete scaledImg;
3804 // all other cases
3805 } else {
3806 return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha,
3807 w, h, mat, interpolate, tilingPattern);
3810 return splashOk;
3813 SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3814 SplashColorMode srcMode, int nComps,
3815 GBool srcAlpha,
3816 int srcWidth, int srcHeight,
3817 SplashCoord *mat, GBool interpolate,
3818 GBool tilingPattern) {
3819 SplashBitmap *scaledImg;
3820 SplashClipResult clipRes, clipRes2;
3821 SplashPipe pipe;
3822 SplashColor pixel;
3823 int scaledWidth, scaledHeight, t0, t1, th;
3824 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3825 SplashCoord vx[4], vy[4];
3826 int xMin, yMin, xMax, yMax;
3827 ImageSection section[3];
3828 int nSections;
3829 int y, xa, xb, x, i, xx, yy, yp;
3831 // compute the four vertices of the target quadrilateral
3832 vx[0] = mat[4]; vy[0] = mat[5];
3833 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3834 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3835 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3837 // clipping
3838 xMin = imgCoordMungeLower(vx[0]);
3839 xMax = imgCoordMungeUpper(vx[0]);
3840 yMin = imgCoordMungeLower(vy[0]);
3841 yMax = imgCoordMungeUpper(vy[0]);
3842 for (i = 1; i < 4; ++i) {
3843 t0 = imgCoordMungeLower(vx[i]);
3844 if (t0 < xMin) {
3845 xMin = t0;
3847 t0 = imgCoordMungeUpper(vx[i]);
3848 if (t0 > xMax) {
3849 xMax = t0;
3851 t1 = imgCoordMungeLower(vy[i]);
3852 if (t1 < yMin) {
3853 yMin = t1;
3855 t1 = imgCoordMungeUpper(vy[i]);
3856 if (t1 > yMax) {
3857 yMax = t1;
3860 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
3861 opClipRes = clipRes;
3862 if (clipRes == splashClipAllOutside) {
3863 return splashOk;
3866 // compute the scale factors
3867 if (splashAbs(mat[0]) >= splashAbs(mat[1])) {
3868 scaledWidth = xMax - xMin;
3869 scaledHeight = yMax - yMin;
3870 } else {
3871 scaledWidth = yMax - yMin;
3872 scaledHeight = xMax - xMin;
3874 if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) {
3875 if (mat[0] >= 0) {
3876 t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
3877 } else {
3878 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
3880 if (mat[1] >= 0) {
3881 t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
3882 } else {
3883 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
3885 scaledWidth = t0 > t1 ? t0 : t1;
3886 if (mat[2] >= 0) {
3887 t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
3888 if (splashAbs(mat[1]) >= 1) {
3889 th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]);
3890 if (th > t0) t0 = th;
3892 } else {
3893 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
3894 if (splashAbs(mat[1]) >= 1) {
3895 th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]);
3896 if (th > t0) t0 = th;
3899 if (mat[3] >= 0) {
3900 t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
3901 if (splashAbs(mat[0]) >= 1) {
3902 th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]);
3903 if (th > t1) t1 = th;
3905 } else {
3906 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
3907 if (splashAbs(mat[0]) >= 1) {
3908 th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]);
3909 if (th > t1) t1 = th;
3912 scaledHeight = t0 > t1 ? t0 : t1;
3914 if (scaledWidth == 0) {
3915 scaledWidth = 1;
3917 if (scaledHeight == 0) {
3918 scaledHeight = 1;
3921 // compute the inverse transform (after scaling) matrix
3922 r00 = mat[0] / scaledWidth;
3923 r01 = mat[1] / scaledWidth;
3924 r10 = mat[2] / scaledHeight;
3925 r11 = mat[3] / scaledHeight;
3926 det = r00 * r11 - r01 * r10;
3927 if (splashAbs(det) < 1e-6) {
3928 // this should be caught by the singular matrix check in drawImage
3929 return splashErrBadArg;
3931 ir00 = r11 / det;
3932 ir01 = -r01 / det;
3933 ir10 = -r10 / det;
3934 ir11 = r00 / det;
3936 // scale the input image
3937 yp = srcHeight / scaledHeight;
3938 if (yp < 0 || yp > INT_MAX - 1) {
3939 return splashErrBadArg;
3941 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
3942 srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
3944 if (scaledImg == NULL) {
3945 return splashErrBadArg;
3948 if (tf != NULL) {
3949 (*tf)(srcData, scaledImg);
3951 // construct the three sections
3952 i = 0;
3953 if (vy[1] < vy[i]) {
3954 i = 1;
3956 if (vy[2] < vy[i]) {
3957 i = 2;
3959 if (vy[3] < vy[i]) {
3960 i = 3;
3962 // NB: if using fixed point, 0.000001 will be truncated to zero,
3963 // so these two comparisons must be <=, not <
3964 if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
3965 vy[(i-1) & 3] < vy[(i+1) & 3]) {
3966 i = (i-1) & 3;
3968 if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
3969 section[0].y0 = imgCoordMungeLower(vy[i]);
3970 section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
3971 if (vx[i] < vx[(i+1) & 3]) {
3972 section[0].ia0 = i;
3973 section[0].ia1 = (i+3) & 3;
3974 section[0].ib0 = (i+1) & 3;
3975 section[0].ib1 = (i+2) & 3;
3976 } else {
3977 section[0].ia0 = (i+1) & 3;
3978 section[0].ia1 = (i+2) & 3;
3979 section[0].ib0 = i;
3980 section[0].ib1 = (i+3) & 3;
3982 nSections = 1;
3983 } else {
3984 section[0].y0 = imgCoordMungeLower(vy[i]);
3985 section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
3986 section[0].ia0 = section[0].ib0 = i;
3987 section[2].ia1 = section[2].ib1 = (i+2) & 3;
3988 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3989 section[0].ia1 = section[2].ia0 = (i+1) & 3;
3990 section[0].ib1 = section[2].ib0 = (i+3) & 3;
3991 } else {
3992 section[0].ia1 = section[2].ia0 = (i+3) & 3;
3993 section[0].ib1 = section[2].ib0 = (i+1) & 3;
3995 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
3996 section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]);
3997 section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]);
3998 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3999 section[1].ia0 = (i+1) & 3;
4000 section[1].ia1 = (i+2) & 3;
4001 section[1].ib0 = i;
4002 section[1].ib1 = (i+3) & 3;
4003 } else {
4004 section[1].ia0 = i;
4005 section[1].ia1 = (i+3) & 3;
4006 section[1].ib0 = (i+1) & 3;
4007 section[1].ib1 = (i+2) & 3;
4009 } else {
4010 section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]);
4011 section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]);
4012 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4013 section[1].ia0 = i;
4014 section[1].ia1 = (i+1) & 3;
4015 section[1].ib0 = (i+3) & 3;
4016 section[1].ib1 = (i+2) & 3;
4017 } else {
4018 section[1].ia0 = (i+3) & 3;
4019 section[1].ia1 = (i+2) & 3;
4020 section[1].ib0 = i;
4021 section[1].ib1 = (i+1) & 3;
4024 section[0].y1 = section[1].y0 - 1;
4025 section[1].y1 = section[2].y0 - 1;
4026 nSections = 3;
4028 for (i = 0; i < nSections; ++i) {
4029 section[i].xa0 = vx[section[i].ia0];
4030 section[i].ya0 = vy[section[i].ia0];
4031 section[i].xa1 = vx[section[i].ia1];
4032 section[i].ya1 = vy[section[i].ia1];
4033 section[i].xb0 = vx[section[i].ib0];
4034 section[i].yb0 = vy[section[i].ib0];
4035 section[i].xb1 = vx[section[i].ib1];
4036 section[i].yb1 = vy[section[i].ib1];
4037 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
4038 (section[i].ya1 - section[i].ya0);
4039 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
4040 (section[i].yb1 - section[i].yb0);
4043 // initialize the pixel pipe
4044 pipeInit(&pipe, 0, 0, NULL, pixel,
4045 (Guchar)splashRound(state->fillAlpha * 255),
4046 srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
4047 gFalse);
4048 if (vectorAntialias) {
4049 drawAAPixelInit();
4052 // make sure narrow images cover at least one pixel
4053 if (nSections == 1) {
4054 if (section[0].y0 == section[0].y1) {
4055 ++section[0].y1;
4056 clipRes = opClipRes = splashClipPartial;
4058 } else {
4059 if (section[0].y0 == section[2].y1) {
4060 ++section[1].y1;
4061 clipRes = opClipRes = splashClipPartial;
4065 // scan all pixels inside the target region
4066 for (i = 0; i < nSections; ++i) {
4067 for (y = section[i].y0; y <= section[i].y1; ++y) {
4068 xa = imgCoordMungeLower(section[i].xa0 +
4069 ((SplashCoord)y + 0.5 - section[i].ya0) *
4070 section[i].dxdya);
4071 if (unlikely(xa < 0))
4072 xa = 0;
4073 xb = imgCoordMungeUpper(section[i].xb0 +
4074 ((SplashCoord)y + 0.5 - section[i].yb0) *
4075 section[i].dxdyb);
4076 // make sure narrow images cover at least one pixel
4077 if (xa == xb) {
4078 ++xb;
4080 if (clipRes != splashClipAllInside) {
4081 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
4082 } else {
4083 clipRes2 = clipRes;
4085 for (x = xa; x < xb; ++x) {
4086 // map (x+0.5, y+0.5) back to the scaled image
4087 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
4088 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
4089 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
4090 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
4091 // xx should always be within bounds, but floating point
4092 // inaccuracy can cause problems
4093 if (xx < 0) {
4094 xx = 0;
4095 } else if (xx >= scaledWidth) {
4096 xx = scaledWidth - 1;
4098 if (yy < 0) {
4099 yy = 0;
4100 } else if (yy >= scaledHeight) {
4101 yy = scaledHeight - 1;
4103 scaledImg->getPixel(xx, yy, pixel);
4104 if (srcAlpha) {
4105 pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
4106 } else {
4107 pipe.shape = 255;
4109 if (vectorAntialias && clipRes2 != splashClipAllInside) {
4110 drawAAPixel(&pipe, x, y);
4111 } else {
4112 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
4118 delete scaledImg;
4119 return splashOk;
4122 // determine if a scaled image requires interpolation based on the scale and
4123 // the interpolate flag from the image dictionary
4124 static GBool isImageInterpolationRequired(int srcWidth, int srcHeight,
4125 int scaledWidth, int scaledHeight,
4126 GBool interpolate) {
4127 if (interpolate)
4128 return gTrue;
4130 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
4131 if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4)
4132 return gFalse;
4134 return gTrue;
4137 // Scale an image into a SplashBitmap.
4138 SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
4139 SplashColorMode srcMode, int nComps,
4140 GBool srcAlpha, int srcWidth, int srcHeight,
4141 int scaledWidth, int scaledHeight, GBool interpolate, GBool tilingPattern) {
4142 SplashBitmap *dest;
4144 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList());
4145 if (dest->getDataPtr() != NULL) {
4146 if (scaledHeight < srcHeight) {
4147 if (scaledWidth < srcWidth) {
4148 scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha,
4149 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4150 } else {
4151 scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha,
4152 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4154 } else {
4155 if (scaledWidth < srcWidth) {
4156 scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
4157 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4158 } else {
4159 if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
4160 scaleImageYuXuBilinear(src, srcData, srcMode, nComps, srcAlpha,
4161 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4162 } else {
4163 scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
4164 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4168 } else {
4169 delete dest;
4170 dest = NULL;
4172 return dest;
4175 void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
4176 SplashColorMode srcMode, int nComps,
4177 GBool srcAlpha, int srcWidth, int srcHeight,
4178 int scaledWidth, int scaledHeight,
4179 SplashBitmap *dest) {
4180 Guchar *lineBuf, *alphaLineBuf;
4181 Guint *pixBuf, *alphaPixBuf;
4182 Guint pix0, pix1, pix2;
4183 #if SPLASH_CMYK
4184 Guint pix3;
4185 Guint pix[SPOT_NCOMPS+4], cp;
4186 #endif
4187 Guint alpha;
4188 Guchar *destPtr, *destAlphaPtr;
4189 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4190 int i, j;
4192 // Bresenham parameters for y scale
4193 yp = srcHeight / scaledHeight;
4194 yq = srcHeight % scaledHeight;
4196 // Bresenham parameters for x scale
4197 xp = srcWidth / scaledWidth;
4198 xq = srcWidth % scaledWidth;
4200 // allocate buffers
4201 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4202 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4203 if (srcAlpha) {
4204 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4205 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4206 } else {
4207 alphaLineBuf = NULL;
4208 alphaPixBuf = NULL;
4211 // init y scale Bresenham
4212 yt = 0;
4214 destPtr = dest->data;
4215 destAlphaPtr = dest->alpha;
4216 for (y = 0; y < scaledHeight; ++y) {
4218 // y scale Bresenham
4219 if ((yt += yq) >= scaledHeight) {
4220 yt -= scaledHeight;
4221 yStep = yp + 1;
4222 } else {
4223 yStep = yp;
4226 // read rows from image
4227 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4228 if (srcAlpha) {
4229 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4231 for (i = 0; i < yStep; ++i) {
4232 (*src)(srcData, lineBuf, alphaLineBuf);
4233 for (j = 0; j < srcWidth * nComps; ++j) {
4234 pixBuf[j] += lineBuf[j];
4236 if (srcAlpha) {
4237 for (j = 0; j < srcWidth; ++j) {
4238 alphaPixBuf[j] += alphaLineBuf[j];
4243 // init x scale Bresenham
4244 xt = 0;
4245 d0 = (1 << 23) / (yStep * xp);
4246 d1 = (1 << 23) / (yStep * (xp + 1));
4248 xx = xxa = 0;
4249 for (x = 0; x < scaledWidth; ++x) {
4251 // x scale Bresenham
4252 if ((xt += xq) >= scaledWidth) {
4253 xt -= scaledWidth;
4254 xStep = xp + 1;
4255 d = d1;
4256 } else {
4257 xStep = xp;
4258 d = d0;
4261 switch (srcMode) {
4263 case splashModeMono8:
4265 // compute the final pixel
4266 pix0 = 0;
4267 for (i = 0; i < xStep; ++i) {
4268 pix0 += pixBuf[xx++];
4270 // pix / xStep * yStep
4271 pix0 = (pix0 * d) >> 23;
4273 // store the pixel
4274 *destPtr++ = (Guchar)pix0;
4275 break;
4277 case splashModeRGB8:
4279 // compute the final pixel
4280 pix0 = pix1 = pix2 = 0;
4281 for (i = 0; i < xStep; ++i) {
4282 pix0 += pixBuf[xx];
4283 pix1 += pixBuf[xx+1];
4284 pix2 += pixBuf[xx+2];
4285 xx += 3;
4287 // pix / xStep * yStep
4288 pix0 = (pix0 * d) >> 23;
4289 pix1 = (pix1 * d) >> 23;
4290 pix2 = (pix2 * d) >> 23;
4292 // store the pixel
4293 *destPtr++ = (Guchar)pix0;
4294 *destPtr++ = (Guchar)pix1;
4295 *destPtr++ = (Guchar)pix2;
4296 break;
4298 case splashModeXBGR8:
4300 // compute the final pixel
4301 pix0 = pix1 = pix2 = 0;
4302 for (i = 0; i < xStep; ++i) {
4303 pix0 += pixBuf[xx];
4304 pix1 += pixBuf[xx+1];
4305 pix2 += pixBuf[xx+2];
4306 xx += 4;
4308 // pix / xStep * yStep
4309 pix0 = (pix0 * d) >> 23;
4310 pix1 = (pix1 * d) >> 23;
4311 pix2 = (pix2 * d) >> 23;
4313 // store the pixel
4314 *destPtr++ = (Guchar)pix2;
4315 *destPtr++ = (Guchar)pix1;
4316 *destPtr++ = (Guchar)pix0;
4317 *destPtr++ = (Guchar)255;
4318 break;
4320 case splashModeBGR8:
4322 // compute the final pixel
4323 pix0 = pix1 = pix2 = 0;
4324 for (i = 0; i < xStep; ++i) {
4325 pix0 += pixBuf[xx];
4326 pix1 += pixBuf[xx+1];
4327 pix2 += pixBuf[xx+2];
4328 xx += 3;
4330 // pix / xStep * yStep
4331 pix0 = (pix0 * d) >> 23;
4332 pix1 = (pix1 * d) >> 23;
4333 pix2 = (pix2 * d) >> 23;
4335 // store the pixel
4336 *destPtr++ = (Guchar)pix2;
4337 *destPtr++ = (Guchar)pix1;
4338 *destPtr++ = (Guchar)pix0;
4339 break;
4341 #if SPLASH_CMYK
4342 case splashModeCMYK8:
4344 // compute the final pixel
4345 pix0 = pix1 = pix2 = pix3 = 0;
4346 for (i = 0; i < xStep; ++i) {
4347 pix0 += pixBuf[xx];
4348 pix1 += pixBuf[xx+1];
4349 pix2 += pixBuf[xx+2];
4350 pix3 += pixBuf[xx+3];
4351 xx += 4;
4353 // pix / xStep * yStep
4354 pix0 = (pix0 * d) >> 23;
4355 pix1 = (pix1 * d) >> 23;
4356 pix2 = (pix2 * d) >> 23;
4357 pix3 = (pix3 * d) >> 23;
4359 // store the pixel
4360 *destPtr++ = (Guchar)pix0;
4361 *destPtr++ = (Guchar)pix1;
4362 *destPtr++ = (Guchar)pix2;
4363 *destPtr++ = (Guchar)pix3;
4364 break;
4365 case splashModeDeviceN8:
4367 // compute the final pixel
4368 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4369 pix[cp] = 0;
4370 for (i = 0; i < xStep; ++i) {
4371 for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
4372 pix[cp] += pixBuf[xx + cp];
4374 xx += (SPOT_NCOMPS+4);
4376 // pix / xStep * yStep
4377 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4378 pix[cp] = (pix[cp] * d) >> 23;
4380 // store the pixel
4381 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4382 *destPtr++ = (Guchar)pix[cp];
4383 break;
4384 #endif
4387 case splashModeMono1: // mono1 is not allowed
4388 default:
4389 break;
4392 // process alpha
4393 if (srcAlpha) {
4394 alpha = 0;
4395 for (i = 0; i < xStep; ++i, ++xxa) {
4396 alpha += alphaPixBuf[xxa];
4398 // alpha / xStep * yStep
4399 alpha = (alpha * d) >> 23;
4400 *destAlphaPtr++ = (Guchar)alpha;
4405 gfree(alphaPixBuf);
4406 gfree(alphaLineBuf);
4407 gfree(pixBuf);
4408 gfree(lineBuf);
4411 void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
4412 SplashColorMode srcMode, int nComps,
4413 GBool srcAlpha, int srcWidth, int srcHeight,
4414 int scaledWidth, int scaledHeight,
4415 SplashBitmap *dest) {
4416 Guchar *lineBuf, *alphaLineBuf;
4417 Guint *pixBuf, *alphaPixBuf;
4418 Guint pix[splashMaxColorComps];
4419 Guint alpha;
4420 Guchar *destPtr, *destAlphaPtr;
4421 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
4422 int i, j;
4424 // Bresenham parameters for y scale
4425 yp = srcHeight / scaledHeight;
4426 yq = srcHeight % scaledHeight;
4428 // Bresenham parameters for x scale
4429 xp = scaledWidth / srcWidth;
4430 xq = scaledWidth % srcWidth;
4432 // allocate buffers
4433 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4434 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4435 if (srcAlpha) {
4436 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4437 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4438 } else {
4439 alphaLineBuf = NULL;
4440 alphaPixBuf = NULL;
4443 // init y scale Bresenham
4444 yt = 0;
4446 destPtr = dest->data;
4447 destAlphaPtr = dest->alpha;
4448 for (y = 0; y < scaledHeight; ++y) {
4450 // y scale Bresenham
4451 if ((yt += yq) >= scaledHeight) {
4452 yt -= scaledHeight;
4453 yStep = yp + 1;
4454 } else {
4455 yStep = yp;
4458 // read rows from image
4459 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4460 if (srcAlpha) {
4461 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4463 for (i = 0; i < yStep; ++i) {
4464 (*src)(srcData, lineBuf, alphaLineBuf);
4465 for (j = 0; j < srcWidth * nComps; ++j) {
4466 pixBuf[j] += lineBuf[j];
4468 if (srcAlpha) {
4469 for (j = 0; j < srcWidth; ++j) {
4470 alphaPixBuf[j] += alphaLineBuf[j];
4475 // init x scale Bresenham
4476 xt = 0;
4477 d = (1 << 23) / yStep;
4479 for (x = 0; x < srcWidth; ++x) {
4481 // x scale Bresenham
4482 if ((xt += xq) >= srcWidth) {
4483 xt -= srcWidth;
4484 xStep = xp + 1;
4485 } else {
4486 xStep = xp;
4489 // compute the final pixel
4490 for (i = 0; i < nComps; ++i) {
4491 // pixBuf[] / yStep
4492 pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
4495 // store the pixel
4496 switch (srcMode) {
4497 case splashModeMono1: // mono1 is not allowed
4498 break;
4499 case splashModeMono8:
4500 for (i = 0; i < xStep; ++i) {
4501 *destPtr++ = (Guchar)pix[0];
4503 break;
4504 case splashModeRGB8:
4505 for (i = 0; i < xStep; ++i) {
4506 *destPtr++ = (Guchar)pix[0];
4507 *destPtr++ = (Guchar)pix[1];
4508 *destPtr++ = (Guchar)pix[2];
4510 break;
4511 case splashModeXBGR8:
4512 for (i = 0; i < xStep; ++i) {
4513 *destPtr++ = (Guchar)pix[2];
4514 *destPtr++ = (Guchar)pix[1];
4515 *destPtr++ = (Guchar)pix[0];
4516 *destPtr++ = (Guchar)255;
4518 break;
4519 case splashModeBGR8:
4520 for (i = 0; i < xStep; ++i) {
4521 *destPtr++ = (Guchar)pix[2];
4522 *destPtr++ = (Guchar)pix[1];
4523 *destPtr++ = (Guchar)pix[0];
4525 break;
4526 #if SPLASH_CMYK
4527 case splashModeCMYK8:
4528 for (i = 0; i < xStep; ++i) {
4529 *destPtr++ = (Guchar)pix[0];
4530 *destPtr++ = (Guchar)pix[1];
4531 *destPtr++ = (Guchar)pix[2];
4532 *destPtr++ = (Guchar)pix[3];
4534 break;
4535 case splashModeDeviceN8:
4536 for (i = 0; i < xStep; ++i) {
4537 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4538 *destPtr++ = (Guchar)pix[cp];
4540 break;
4541 #endif
4544 // process alpha
4545 if (srcAlpha) {
4546 // alphaPixBuf[] / yStep
4547 alpha = (alphaPixBuf[x] * d) >> 23;
4548 for (i = 0; i < xStep; ++i) {
4549 *destAlphaPtr++ = (Guchar)alpha;
4555 gfree(alphaPixBuf);
4556 gfree(alphaLineBuf);
4557 gfree(pixBuf);
4558 gfree(lineBuf);
4561 void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
4562 SplashColorMode srcMode, int nComps,
4563 GBool srcAlpha, int srcWidth, int srcHeight,
4564 int scaledWidth, int scaledHeight,
4565 SplashBitmap *dest) {
4566 Guchar *lineBuf, *alphaLineBuf;
4567 Guint pix[splashMaxColorComps];
4568 Guint alpha;
4569 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4570 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4571 int i, j;
4573 // Bresenham parameters for y scale
4574 yp = scaledHeight / srcHeight;
4575 yq = scaledHeight % srcHeight;
4577 // Bresenham parameters for x scale
4578 xp = srcWidth / scaledWidth;
4579 xq = srcWidth % scaledWidth;
4581 // allocate buffers
4582 lineBuf = (Guchar *)gmallocn_checkoverflow(srcWidth, nComps);
4583 if (unlikely(!lineBuf))
4584 return;
4585 if (srcAlpha) {
4586 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4587 } else {
4588 alphaLineBuf = NULL;
4591 // init y scale Bresenham
4592 yt = 0;
4594 destPtr0 = dest->data;
4595 destAlphaPtr0 = dest->alpha;
4596 for (y = 0; y < srcHeight; ++y) {
4598 // y scale Bresenham
4599 if ((yt += yq) >= srcHeight) {
4600 yt -= srcHeight;
4601 yStep = yp + 1;
4602 } else {
4603 yStep = yp;
4606 // read row from image
4607 (*src)(srcData, lineBuf, alphaLineBuf);
4609 // init x scale Bresenham
4610 xt = 0;
4611 d0 = (1 << 23) / xp;
4612 d1 = (1 << 23) / (xp + 1);
4614 xx = xxa = 0;
4615 for (x = 0; x < scaledWidth; ++x) {
4617 // x scale Bresenham
4618 if ((xt += xq) >= scaledWidth) {
4619 xt -= scaledWidth;
4620 xStep = xp + 1;
4621 d = d1;
4622 } else {
4623 xStep = xp;
4624 d = d0;
4627 // compute the final pixel
4628 for (i = 0; i < nComps; ++i) {
4629 pix[i] = 0;
4631 for (i = 0; i < xStep; ++i) {
4632 for (j = 0; j < nComps; ++j, ++xx) {
4633 pix[j] += lineBuf[xx];
4636 for (i = 0; i < nComps; ++i) {
4637 // pix[] / xStep
4638 pix[i] = (pix[i] * d) >> 23;
4641 // store the pixel
4642 switch (srcMode) {
4643 case splashModeMono1: // mono1 is not allowed
4644 break;
4645 case splashModeMono8:
4646 for (i = 0; i < yStep; ++i) {
4647 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4648 *destPtr++ = (Guchar)pix[0];
4650 break;
4651 case splashModeRGB8:
4652 for (i = 0; i < yStep; ++i) {
4653 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4654 *destPtr++ = (Guchar)pix[0];
4655 *destPtr++ = (Guchar)pix[1];
4656 *destPtr++ = (Guchar)pix[2];
4658 break;
4659 case splashModeXBGR8:
4660 for (i = 0; i < yStep; ++i) {
4661 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4662 *destPtr++ = (Guchar)pix[2];
4663 *destPtr++ = (Guchar)pix[1];
4664 *destPtr++ = (Guchar)pix[0];
4665 *destPtr++ = (Guchar)255;
4667 break;
4668 case splashModeBGR8:
4669 for (i = 0; i < yStep; ++i) {
4670 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4671 *destPtr++ = (Guchar)pix[2];
4672 *destPtr++ = (Guchar)pix[1];
4673 *destPtr++ = (Guchar)pix[0];
4675 break;
4676 #if SPLASH_CMYK
4677 case splashModeCMYK8:
4678 for (i = 0; i < yStep; ++i) {
4679 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4680 *destPtr++ = (Guchar)pix[0];
4681 *destPtr++ = (Guchar)pix[1];
4682 *destPtr++ = (Guchar)pix[2];
4683 *destPtr++ = (Guchar)pix[3];
4685 break;
4686 case splashModeDeviceN8:
4687 for (i = 0; i < yStep; ++i) {
4688 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4689 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4690 *destPtr++ = (Guchar)pix[cp];
4692 break;
4693 #endif
4696 // process alpha
4697 if (srcAlpha) {
4698 alpha = 0;
4699 for (i = 0; i < xStep; ++i, ++xxa) {
4700 alpha += alphaLineBuf[xxa];
4702 // alpha / xStep
4703 alpha = (alpha * d) >> 23;
4704 for (i = 0; i < yStep; ++i) {
4705 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
4706 *destAlphaPtr = (Guchar)alpha;
4711 destPtr0 += yStep * scaledWidth * nComps;
4712 if (srcAlpha) {
4713 destAlphaPtr0 += yStep * scaledWidth;
4717 gfree(alphaLineBuf);
4718 gfree(lineBuf);
4721 void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
4722 SplashColorMode srcMode, int nComps,
4723 GBool srcAlpha, int srcWidth, int srcHeight,
4724 int scaledWidth, int scaledHeight,
4725 SplashBitmap *dest) {
4726 Guchar *lineBuf, *alphaLineBuf;
4727 Guint pix[splashMaxColorComps];
4728 Guint alpha;
4729 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4730 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
4731 int i, j;
4733 // Bresenham parameters for y scale
4734 yp = scaledHeight / srcHeight;
4735 yq = scaledHeight % srcHeight;
4737 // Bresenham parameters for x scale
4738 xp = scaledWidth / srcWidth;
4739 xq = scaledWidth % srcWidth;
4741 // allocate buffers
4742 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4743 if (srcAlpha) {
4744 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4745 } else {
4746 alphaLineBuf = NULL;
4749 // init y scale Bresenham
4750 yt = 0;
4752 destPtr0 = dest->data;
4753 destAlphaPtr0 = dest->alpha;
4754 for (y = 0; y < srcHeight; ++y) {
4756 // y scale Bresenham
4757 if ((yt += yq) >= srcHeight) {
4758 yt -= srcHeight;
4759 yStep = yp + 1;
4760 } else {
4761 yStep = yp;
4764 // read row from image
4765 (*src)(srcData, lineBuf, alphaLineBuf);
4767 // init x scale Bresenham
4768 xt = 0;
4770 xx = 0;
4771 for (x = 0; x < srcWidth; ++x) {
4773 // x scale Bresenham
4774 if ((xt += xq) >= srcWidth) {
4775 xt -= srcWidth;
4776 xStep = xp + 1;
4777 } else {
4778 xStep = xp;
4781 // compute the final pixel
4782 for (i = 0; i < nComps; ++i) {
4783 pix[i] = lineBuf[x * nComps + i];
4786 // store the pixel
4787 switch (srcMode) {
4788 case splashModeMono1: // mono1 is not allowed
4789 break;
4790 case splashModeMono8:
4791 for (i = 0; i < yStep; ++i) {
4792 for (j = 0; j < xStep; ++j) {
4793 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4794 *destPtr++ = (Guchar)pix[0];
4797 break;
4798 case splashModeRGB8:
4799 for (i = 0; i < yStep; ++i) {
4800 for (j = 0; j < xStep; ++j) {
4801 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4802 *destPtr++ = (Guchar)pix[0];
4803 *destPtr++ = (Guchar)pix[1];
4804 *destPtr++ = (Guchar)pix[2];
4807 break;
4808 case splashModeXBGR8:
4809 for (i = 0; i < yStep; ++i) {
4810 for (j = 0; j < xStep; ++j) {
4811 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4812 *destPtr++ = (Guchar)pix[2];
4813 *destPtr++ = (Guchar)pix[1];
4814 *destPtr++ = (Guchar)pix[0];
4815 *destPtr++ = (Guchar)255;
4818 break;
4819 case splashModeBGR8:
4820 for (i = 0; i < yStep; ++i) {
4821 for (j = 0; j < xStep; ++j) {
4822 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4823 *destPtr++ = (Guchar)pix[2];
4824 *destPtr++ = (Guchar)pix[1];
4825 *destPtr++ = (Guchar)pix[0];
4828 break;
4829 #if SPLASH_CMYK
4830 case splashModeCMYK8:
4831 for (i = 0; i < yStep; ++i) {
4832 for (j = 0; j < xStep; ++j) {
4833 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4834 *destPtr++ = (Guchar)pix[0];
4835 *destPtr++ = (Guchar)pix[1];
4836 *destPtr++ = (Guchar)pix[2];
4837 *destPtr++ = (Guchar)pix[3];
4840 break;
4841 case splashModeDeviceN8:
4842 for (i = 0; i < yStep; ++i) {
4843 for (j = 0; j < xStep; ++j) {
4844 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4845 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4846 *destPtr++ = (Guchar)pix[cp];
4849 break;
4850 #endif
4853 // process alpha
4854 if (srcAlpha) {
4855 alpha = alphaLineBuf[x];
4856 for (i = 0; i < yStep; ++i) {
4857 for (j = 0; j < xStep; ++j) {
4858 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j;
4859 *destAlphaPtr = (Guchar)alpha;
4864 xx += xStep;
4867 destPtr0 += yStep * scaledWidth * nComps;
4868 if (srcAlpha) {
4869 destAlphaPtr0 += yStep * scaledWidth;
4873 gfree(alphaLineBuf);
4874 gfree(lineBuf);
4877 // expand source row to scaledWidth using linear interpolation
4878 static void expandRow(Guchar *srcBuf, Guchar *dstBuf, int srcWidth, int scaledWidth, int nComps)
4880 double xStep = (double)srcWidth/scaledWidth;
4881 double xSrc = 0.0;
4882 double xFrac, xInt;
4883 int p;
4885 // pad the source with an extra pixel equal to the last pixel
4886 // so that when xStep is inside the last pixel we still have two
4887 // pixels to interpolate between.
4888 for (int i = 0; i < nComps; i++)
4889 srcBuf[srcWidth*nComps + i] = srcBuf[(srcWidth-1)*nComps + i];
4891 for (int x = 0; x < scaledWidth; x++) {
4892 xFrac = modf(xSrc, &xInt);
4893 p = (int)xInt;
4894 for (int c = 0; c < nComps; c++) {
4895 dstBuf[nComps*x + c] = srcBuf[nComps*p + c]*(1.0 - xFrac) + srcBuf[nComps*(p+1) + c]*xFrac;
4897 xSrc += xStep;
4901 // Scale up image using bilinear interpolation
4902 void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData,
4903 SplashColorMode srcMode, int nComps,
4904 GBool srcAlpha, int srcWidth, int srcHeight,
4905 int scaledWidth, int scaledHeight,
4906 SplashBitmap *dest) {
4907 Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2;
4908 Guint pix[splashMaxColorComps];
4909 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4910 int i;
4912 if (srcWidth < 1 || srcHeight < 1)
4913 return;
4915 // allocate buffers
4916 srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding
4917 lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
4918 lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps);
4919 if (srcAlpha) {
4920 alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding
4921 alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
4922 alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth);
4923 } else {
4924 alphaSrcBuf = NULL;
4925 alphaLineBuf1 = NULL;
4926 alphaLineBuf2 = NULL;
4929 double ySrc = 0.0;
4930 double yStep = (double)srcHeight/scaledHeight;
4931 double yFrac, yInt;
4932 int currentSrcRow = -1;
4933 (*src)(srcData, srcBuf, alphaSrcBuf);
4934 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4935 if (srcAlpha)
4936 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4938 destPtr0 = dest->data;
4939 destAlphaPtr0 = dest->alpha;
4940 for (int y = 0; y < scaledHeight; y++) {
4941 yFrac = modf(ySrc, &yInt);
4942 if ((int)yInt > currentSrcRow) {
4943 currentSrcRow++;
4944 // Copy line2 data to line1 and get next line2 data.
4945 // If line2 already contains the last source row we don't touch it.
4946 // This effectively adds an extra row of padding for interpolating the
4947 // last source row with.
4948 memcpy(lineBuf1, lineBuf2, scaledWidth * nComps);
4949 if (srcAlpha)
4950 memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth);
4951 if (currentSrcRow < srcHeight) {
4952 (*src)(srcData, srcBuf, alphaSrcBuf);
4953 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4954 if (srcAlpha)
4955 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4959 // write row y using linear interpolation on lineBuf1 and lineBuf2
4960 for (int x = 0; x < scaledWidth; ++x) {
4961 // compute the final pixel
4962 for (i = 0; i < nComps; ++i) {
4963 pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac;
4966 // store the pixel
4967 destPtr = destPtr0 + (y * scaledWidth + x) * nComps;
4968 switch (srcMode) {
4969 case splashModeMono1: // mono1 is not allowed
4970 break;
4971 case splashModeMono8:
4972 *destPtr++ = (Guchar)pix[0];
4973 break;
4974 case splashModeRGB8:
4975 *destPtr++ = (Guchar)pix[0];
4976 *destPtr++ = (Guchar)pix[1];
4977 *destPtr++ = (Guchar)pix[2];
4978 break;
4979 case splashModeXBGR8:
4980 *destPtr++ = (Guchar)pix[2];
4981 *destPtr++ = (Guchar)pix[1];
4982 *destPtr++ = (Guchar)pix[0];
4983 *destPtr++ = (Guchar)255;
4984 break;
4985 case splashModeBGR8:
4986 *destPtr++ = (Guchar)pix[2];
4987 *destPtr++ = (Guchar)pix[1];
4988 *destPtr++ = (Guchar)pix[0];
4989 break;
4990 #if SPLASH_CMYK
4991 case splashModeCMYK8:
4992 *destPtr++ = (Guchar)pix[0];
4993 *destPtr++ = (Guchar)pix[1];
4994 *destPtr++ = (Guchar)pix[2];
4995 *destPtr++ = (Guchar)pix[3];
4996 break;
4997 case splashModeDeviceN8:
4998 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4999 *destPtr++ = (Guchar)pix[cp];
5000 break;
5001 #endif
5004 // process alpha
5005 if (srcAlpha) {
5006 destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x;
5007 *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac;
5011 ySrc += yStep;
5014 gfree(alphaSrcBuf);
5015 gfree(alphaLineBuf1);
5016 gfree(alphaLineBuf2);
5017 gfree(srcBuf);
5018 gfree(lineBuf1);
5019 gfree(lineBuf2);
5022 void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
5023 int nComps) {
5024 Guchar *lineBuf;
5025 Guchar *p0, *p1;
5026 int w;
5028 if (unlikely(img->data == NULL)) {
5029 error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage");
5030 return;
5033 w = width * nComps;
5034 lineBuf = (Guchar *)gmalloc(w);
5035 for (p0 = img->data, p1 = img->data + (height - 1) * w;
5036 p0 < p1;
5037 p0 += w, p1 -= w) {
5038 memcpy(lineBuf, p0, w);
5039 memcpy(p0, p1, w);
5040 memcpy(p1, lineBuf, w);
5042 if (img->alpha) {
5043 for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width;
5044 p0 < p1;
5045 p0 += width, p1 -= width) {
5046 memcpy(lineBuf, p0, width);
5047 memcpy(p0, p1, width);
5048 memcpy(p1, lineBuf, width);
5051 gfree(lineBuf);
5054 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest) {
5055 SplashClipResult clipRes = state->clip->testRect(xDest, yDest, xDest + src->getWidth() - 1, yDest + src->getHeight() - 1);
5056 if (clipRes != splashClipAllOutside) {
5057 blitImage(src, srcAlpha, xDest, yDest, clipRes);
5061 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
5062 SplashClipResult clipRes) {
5063 SplashPipe pipe;
5064 SplashColor pixel;
5065 Guchar *ap;
5066 int w, h, x0, y0, x1, y1, x, y;
5068 // split the image into clipped and unclipped regions
5069 w = src->getWidth();
5070 h = src->getHeight();
5071 if (clipRes == splashClipAllInside) {
5072 x0 = 0;
5073 y0 = 0;
5074 x1 = w;
5075 y1 = h;
5076 } else {
5077 if (state->clip->getNumPaths()) {
5078 x0 = x1 = w;
5079 y0 = y1 = h;
5080 } else {
5081 if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) {
5082 x0 = 0;
5084 if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) {
5085 y0 = 0;
5087 if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) {
5088 x1 = w;
5090 if (x1 < x0) {
5091 x1 = x0;
5093 if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
5094 y1 = h;
5096 if (y1 < y0) {
5097 y1 = y0;
5102 // draw the unclipped region
5103 if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
5104 pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
5105 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5106 if (srcAlpha) {
5107 for (y = y0; y < y1; ++y) {
5108 pipeSetXY(&pipe, xDest + x0, yDest + y);
5109 ap = src->getAlphaPtr() + y * w + x0;
5110 for (x = x0; x < x1; ++x) {
5111 src->getPixel(x, y, pixel);
5112 pipe.shape = *ap++;
5113 (this->*pipe.run)(&pipe);
5116 } else {
5117 for (y = y0; y < y1; ++y) {
5118 pipeSetXY(&pipe, xDest + x0, yDest + y);
5119 for (x = x0; x < x1; ++x) {
5120 src->getPixel(x, y, pixel);
5121 (this->*pipe.run)(&pipe);
5125 updateModX(xDest + x0);
5126 updateModX(xDest + x1 - 1);
5127 updateModY(yDest + y0);
5128 updateModY(yDest + y1 - 1);
5131 // draw the clipped regions
5132 if (y0 > 0) {
5133 blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0);
5135 if (y1 < h) {
5136 blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1);
5138 if (x0 > 0 && y0 < y1) {
5139 blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0);
5141 if (x1 < w && y0 < y1) {
5142 blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0,
5143 w - x1, y1 - y0);
5147 void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
5148 int xSrc, int ySrc, int xDest, int yDest,
5149 int w, int h) {
5150 SplashPipe pipe;
5151 SplashColor pixel;
5152 Guchar *ap;
5153 int x, y;
5155 if (vectorAntialias) {
5156 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5157 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
5158 drawAAPixelInit();
5159 if (srcAlpha) {
5160 for (y = 0; y < h; ++y) {
5161 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5162 for (x = 0; x < w; ++x) {
5163 src->getPixel(xSrc + x, ySrc + y, pixel);
5164 pipe.shape = *ap++;
5165 drawAAPixel(&pipe, xDest + x, yDest + y);
5168 } else {
5169 for (y = 0; y < h; ++y) {
5170 for (x = 0; x < w; ++x) {
5171 src->getPixel(xSrc + x, ySrc + y, pixel);
5172 pipe.shape = 255;
5173 drawAAPixel(&pipe, xDest + x, yDest + y);
5177 } else {
5178 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5179 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5180 if (srcAlpha) {
5181 for (y = 0; y < h; ++y) {
5182 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5183 pipeSetXY(&pipe, xDest, yDest + y);
5184 for (x = 0; x < w; ++x) {
5185 if (state->clip->test(xDest + x, yDest + y)) {
5186 src->getPixel(xSrc + x, ySrc + y, pixel);
5187 pipe.shape = *ap++;
5188 (this->*pipe.run)(&pipe);
5189 updateModX(xDest + x);
5190 updateModY(yDest + y);
5191 } else {
5192 pipeIncX(&pipe);
5193 ++ap;
5197 } else {
5198 for (y = 0; y < h; ++y) {
5199 pipeSetXY(&pipe, xDest, yDest + y);
5200 for (x = 0; x < w; ++x) {
5201 if (state->clip->test(xDest + x, yDest + y)) {
5202 src->getPixel(xSrc + x, ySrc + y, pixel);
5203 (this->*pipe.run)(&pipe);
5204 updateModX(xDest + x);
5205 updateModY(yDest + y);
5206 } else {
5207 pipeIncX(&pipe);
5215 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
5216 int xDest, int yDest, int w, int h,
5217 GBool noClip, GBool nonIsolated,
5218 GBool knockout, SplashCoord knockoutOpacity) {
5219 SplashPipe pipe;
5220 SplashColor pixel;
5221 Guchar alpha;
5222 Guchar *ap;
5223 int x, y;
5225 if (src->mode != bitmap->mode) {
5226 return splashErrModeMismatch;
5229 if (unlikely(!bitmap->data)) {
5230 return splashErrZeroImage;
5233 if(src->getSeparationList()->getLength() > bitmap->getSeparationList()->getLength()) {
5234 for (x = bitmap->getSeparationList()->getLength(); x < src->getSeparationList()->getLength(); x++)
5235 bitmap->getSeparationList()->append(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy());
5237 if (src->alpha) {
5238 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5239 (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated,
5240 knockout, (Guchar)splashRound(knockoutOpacity * 255));
5241 if (noClip) {
5242 for (y = 0; y < h; ++y) {
5243 pipeSetXY(&pipe, xDest, yDest + y);
5244 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5245 for (x = 0; x < w; ++x) {
5246 src->getPixel(xSrc + x, ySrc + y, pixel);
5247 alpha = *ap++;
5248 // this uses shape instead of alpha, which isn't technically
5249 // correct, but works out the same
5250 pipe.shape = alpha;
5251 (this->*pipe.run)(&pipe);
5254 updateModX(xDest);
5255 updateModX(xDest + w - 1);
5256 updateModY(yDest);
5257 updateModY(yDest + h - 1);
5258 } else {
5259 for (y = 0; y < h; ++y) {
5260 pipeSetXY(&pipe, xDest, yDest + y);
5261 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5262 for (x = 0; x < w; ++x) {
5263 src->getPixel(xSrc + x, ySrc + y, pixel);
5264 alpha = *ap++;
5265 if (state->clip->test(xDest + x, yDest + y)) {
5266 // this uses shape instead of alpha, which isn't technically
5267 // correct, but works out the same
5268 pipe.shape = alpha;
5269 (this->*pipe.run)(&pipe);
5270 updateModX(xDest + x);
5271 updateModY(yDest + y);
5272 } else {
5273 pipeIncX(&pipe);
5278 } else {
5279 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5280 (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated);
5281 if (noClip) {
5282 for (y = 0; y < h; ++y) {
5283 pipeSetXY(&pipe, xDest, yDest + y);
5284 for (x = 0; x < w; ++x) {
5285 src->getPixel(xSrc + x, ySrc + y, pixel);
5286 (this->*pipe.run)(&pipe);
5289 updateModX(xDest);
5290 updateModX(xDest + w - 1);
5291 updateModY(yDest);
5292 updateModY(yDest + h - 1);
5293 } else {
5294 for (y = 0; y < h; ++y) {
5295 pipeSetXY(&pipe, xDest, yDest + y);
5296 for (x = 0; x < w; ++x) {
5297 src->getPixel(xSrc + x, ySrc + y, pixel);
5298 if (state->clip->test(xDest + x, yDest + y)) {
5299 (this->*pipe.run)(&pipe);
5300 updateModX(xDest + x);
5301 updateModY(yDest + y);
5302 } else {
5303 pipeIncX(&pipe);
5310 return splashOk;
5313 void Splash::compositeBackground(SplashColorPtr color) {
5314 SplashColorPtr p;
5315 Guchar *q;
5316 Guchar alpha, alpha1, c, color0, color1, color2;
5317 #if SPLASH_CMYK
5318 Guchar color3;
5319 Guchar colorsp[SPOT_NCOMPS+4], cp;
5320 #endif
5321 int x, y, mask;
5323 if (unlikely(bitmap->alpha == NULL)) {
5324 error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground");
5325 return;
5328 switch (bitmap->mode) {
5329 case splashModeMono1:
5330 color0 = color[0];
5331 for (y = 0; y < bitmap->height; ++y) {
5332 p = &bitmap->data[y * bitmap->rowSize];
5333 q = &bitmap->alpha[y * bitmap->width];
5334 mask = 0x80;
5335 for (x = 0; x < bitmap->width; ++x) {
5336 alpha = *q++;
5337 alpha1 = 255 - alpha;
5338 c = (*p & mask) ? 0xff : 0x00;
5339 c = div255(alpha1 * color0 + alpha * c);
5340 if (c & 0x80) {
5341 *p |= mask;
5342 } else {
5343 *p &= ~mask;
5345 if (!(mask >>= 1)) {
5346 mask = 0x80;
5347 ++p;
5351 break;
5352 case splashModeMono8:
5353 color0 = color[0];
5354 for (y = 0; y < bitmap->height; ++y) {
5355 p = &bitmap->data[y * bitmap->rowSize];
5356 q = &bitmap->alpha[y * bitmap->width];
5357 for (x = 0; x < bitmap->width; ++x) {
5358 alpha = *q++;
5359 alpha1 = 255 - alpha;
5360 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5361 ++p;
5364 break;
5365 case splashModeRGB8:
5366 case splashModeBGR8:
5367 color0 = color[0];
5368 color1 = color[1];
5369 color2 = color[2];
5370 for (y = 0; y < bitmap->height; ++y) {
5371 p = &bitmap->data[y * bitmap->rowSize];
5372 q = &bitmap->alpha[y * bitmap->width];
5373 for (x = 0; x < bitmap->width; ++x) {
5374 alpha = *q++;
5375 if (alpha == 0)
5377 p[0] = color0;
5378 p[1] = color1;
5379 p[2] = color2;
5381 else if (alpha != 255)
5383 alpha1 = 255 - alpha;
5384 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5385 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5386 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5388 p += 3;
5391 break;
5392 case splashModeXBGR8:
5393 color0 = color[0];
5394 color1 = color[1];
5395 color2 = color[2];
5396 for (y = 0; y < bitmap->height; ++y) {
5397 p = &bitmap->data[y * bitmap->rowSize];
5398 q = &bitmap->alpha[y * bitmap->width];
5399 for (x = 0; x < bitmap->width; ++x) {
5400 alpha = *q++;
5401 if (alpha == 0)
5403 p[0] = color0;
5404 p[1] = color1;
5405 p[2] = color2;
5407 else if (alpha != 255)
5409 alpha1 = 255 - alpha;
5410 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5411 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5412 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5414 p[3] = 255;
5415 p += 4;
5418 break;
5419 #if SPLASH_CMYK
5420 case splashModeCMYK8:
5421 color0 = color[0];
5422 color1 = color[1];
5423 color2 = color[2];
5424 color3 = color[3];
5425 for (y = 0; y < bitmap->height; ++y) {
5426 p = &bitmap->data[y * bitmap->rowSize];
5427 q = &bitmap->alpha[y * bitmap->width];
5428 for (x = 0; x < bitmap->width; ++x) {
5429 alpha = *q++;
5430 if (alpha == 0)
5432 p[0] = color0;
5433 p[1] = color1;
5434 p[2] = color2;
5435 p[3] = color3;
5437 else if (alpha != 255)
5439 alpha1 = 255 - alpha;
5440 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5441 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5442 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5443 p[3] = div255(alpha1 * color3 + alpha * p[3]);
5445 p += 4;
5448 break;
5449 case splashModeDeviceN8:
5450 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5451 colorsp[cp] = color[cp];
5452 for (y = 0; y < bitmap->height; ++y) {
5453 p = &bitmap->data[y * bitmap->rowSize];
5454 q = &bitmap->alpha[y * bitmap->width];
5455 for (x = 0; x < bitmap->width; ++x) {
5456 alpha = *q++;
5457 if (alpha == 0)
5459 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5460 p[cp] = colorsp[cp];
5462 else if (alpha != 255)
5464 alpha1 = 255 - alpha;
5465 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5466 p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]);
5468 p += (SPOT_NCOMPS+4);
5471 break;
5472 #endif
5474 memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
5477 GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
5479 double xdbl[3] = {0., 0., 0.};
5480 double ydbl[3] = {0., 0., 0.};
5481 int x[3] = {0, 0, 0};
5482 int y[3] = {0, 0, 0};
5483 double xt=0., xa=0., yt=0.;
5484 double ca=0., ct=0.;
5486 // triangle interpolation:
5488 double scanLimitMapL[2] = {0., 0.};
5489 double scanLimitMapR[2] = {0., 0.};
5490 double scanColorMapL[2] = {0., 0.};
5491 double scanColorMapR[2] = {0., 0.};
5492 double scanColorMap[2] = {0., 0.};
5493 int scanEdgeL[2] = { 0, 0 };
5494 int scanEdgeR[2] = { 0, 0 };
5495 GBool hasFurtherSegment = gFalse;
5497 int scanLineOff = 0;
5498 int bitmapOff = 0;
5499 int scanLimitR = 0, scanLimitL = 0;
5501 int bitmapWidth = bitmap->getWidth();
5502 SplashClip* clip = getClip();
5503 SplashBitmap *blitTarget = bitmap;
5504 SplashColorPtr bitmapData = bitmap->getDataPtr();
5505 int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
5506 SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
5507 SplashColorPtr cur = NULL;
5508 SplashCoord* userToCanvasMatrix = getMatrix();
5509 SplashColorMode bitmapMode = bitmap->getMode();
5510 GBool hasAlpha = (bitmapAlpha != NULL);
5511 int rowSize = bitmap->getRowSize();
5512 int colorComps = 0;
5513 switch (bitmapMode) {
5514 case splashModeMono1:
5515 break;
5516 case splashModeMono8:
5517 colorComps=1;
5518 break;
5519 case splashModeRGB8:
5520 colorComps=3;
5521 break;
5522 case splashModeBGR8:
5523 colorComps=3;
5524 break;
5525 case splashModeXBGR8:
5526 colorComps=4;
5527 break;
5528 #if SPLASH_CMYK
5529 case splashModeCMYK8:
5530 colorComps=4;
5531 break;
5532 case splashModeDeviceN8:
5533 colorComps=SPOT_NCOMPS+4;
5534 break;
5535 #endif
5538 SplashPipe pipe;
5539 SplashColor cSrcVal;
5541 pipeInit(&pipe, 0, 0, NULL, cSrcVal, (Guchar)splashRound(state->strokeAlpha * 255), gFalse, gFalse);
5543 if (vectorAntialias) {
5544 if (aaBuf == NULL)
5545 return gFalse; // fall back to old behaviour
5546 drawAAPixelInit();
5549 // idea:
5550 // 1. If pipe->noTransparency && !state->blendFunc
5551 // -> blit directly into the drawing surface!
5552 // -> disable alpha manually.
5553 // 2. Otherwise:
5554 // - blit also directly, but into an intermediate surface.
5555 // Afterwards, blit the intermediate surface using the drawing pipeline.
5556 // This is necessary because triangle elements can be on top of each
5557 // other, so the complete shading needs to be drawn before opacity is
5558 // applied.
5559 // - the final step, is performed using a SplashPipe:
5560 // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference
5561 // - invoke drawPixel(&pipe,X,Y,bNoClip);
5562 GBool bDirectBlit = vectorAntialias ? gFalse : pipe.noTransparency && !state->blendFunc;
5563 if (!bDirectBlit) {
5564 blitTarget = new SplashBitmap(bitmap->getWidth(),
5565 bitmap->getHeight(),
5566 bitmap->getRowPad(),
5567 bitmap->getMode(),
5568 gTrue,
5569 bitmap->getRowSize() >= 0);
5570 bitmapData = blitTarget->getDataPtr();
5571 bitmapAlpha = blitTarget->getAlphaPtr();
5573 // initialisation seems to be necessary:
5574 int S = bitmap->getWidth() * bitmap->getHeight();
5575 for (int i = 0; i < S; ++i)
5576 bitmapAlpha[i] = 0;
5577 hasAlpha = gTrue;
5580 if (shading->isParameterized()) {
5581 double color[3];
5582 double colorinterp;
5584 for (int i = 0; i < shading->getNTriangles(); ++i) {
5585 shading->getTriangle(i,
5586 xdbl + 0, ydbl + 0, color + 0,
5587 xdbl + 1, ydbl + 1, color + 1,
5588 xdbl + 2, ydbl + 2, color + 2);
5589 for (int m = 0; m < 3; ++m) {
5590 xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
5591 yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5];
5592 xdbl[m] = xt;
5593 ydbl[m] = yt;
5594 // we operate on scanlines which are integer offsets into the
5595 // raster image. The double offsets are of no use here.
5596 x[m] = splashRound(xt);
5597 y[m] = splashRound(yt);
5599 // sort according to y coordinate to simplify sweep through scanlines:
5600 // INSERTION SORT.
5601 if (y[0] > y[1]) {
5602 Guswap(x[0], x[1]);
5603 Guswap(y[0], y[1]);
5604 Guswap(color[0], color[1]);
5606 // first two are sorted.
5607 assert(y[0] <= y[1]);
5608 if (y[1] > y[2]) {
5609 int tmpX = x[2];
5610 int tmpY = y[2];
5611 double tmpC = color[2];
5612 x[2] = x[1]; y[2] = y[1]; color[2] = color[1];
5614 if (y[0] > tmpY) {
5615 x[1] = x[0]; y[1] = y[0]; color[1] = color[0];
5616 x[0] = tmpX; y[0] = tmpY; color[0] = tmpC;
5617 } else {
5618 x[1] = tmpX; y[1] = tmpY; color[1] = tmpC;
5621 // first three are sorted
5622 assert(y[0] <= y[1]);
5623 assert(y[1] <= y[2]);
5624 /////
5626 // this here is det( T ) == 0
5627 // where T is the matrix to map to barycentric coordinates.
5628 if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0)
5629 continue; // degenerate triangle.
5631 // this here initialises the scanline generation.
5632 // We start with low Y coordinates and sweep up to the large Y
5633 // coordinates.
5635 // scanEdgeL[m] in {0,1,2} m=0,1
5636 // scanEdgeR[m] in {0,1,2} m=0,1
5638 // are the two edges between which scanlines are (currently)
5639 // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
5640 // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
5642 scanEdgeL[0] = 0;
5643 scanEdgeR[0] = 0;
5644 if (y[0] == y[1]) {
5645 scanEdgeL[0] = 1;
5646 scanEdgeL[1] = scanEdgeR[1] = 2;
5648 } else {
5649 scanEdgeL[1] = 1; scanEdgeR[1] = 2;
5651 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5652 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5654 // Ok. Now prepare the linear maps which map the y coordinate of
5655 // the current scanline to the corresponding LEFT and RIGHT x
5656 // coordinate (which define the scanline).
5657 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5658 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5659 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5660 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5662 xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
5663 xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
5664 if (xa > xt) {
5665 // I have "left" is to the right of "right".
5666 // Exchange sides!
5667 Guswap(scanEdgeL[0], scanEdgeR[0]);
5668 Guswap(scanEdgeL[1], scanEdgeR[1]);
5669 Guswap(scanLimitMapL[0], scanLimitMapR[0]);
5670 Guswap(scanLimitMapL[1], scanLimitMapR[1]);
5671 // FIXME I'm sure there is a more efficient way to check this.
5674 // Same game: we can linearly interpolate the color based on the
5675 // current y coordinate (that's correct for triangle
5676 // interpolation due to linearity. We could also have done it in
5677 // barycentric coordinates, but that's slightly more involved)
5678 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5679 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5680 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5681 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5683 hasFurtherSegment = (y[1] < y[2]);
5684 scanLineOff = y[0] * rowSize;
5686 for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
5687 if (hasFurtherSegment && Y == y[1]) {
5688 // SWEEP EVENT: we encountered the next segment.
5690 // switch to next segment, either at left end or at right
5691 // end:
5692 if (scanEdgeL[1] == 1) {
5693 scanEdgeL[0] = 1;
5694 scanEdgeL[1] = 2;
5695 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5696 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5698 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5699 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5700 } else if (scanEdgeR[1] == 1) {
5701 scanEdgeR[0] = 1;
5702 scanEdgeR[1] = 2;
5703 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5704 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5706 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5707 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5709 assert( y[scanEdgeL[0]] < y[scanEdgeL[1]] );
5710 assert( y[scanEdgeR[0]] < y[scanEdgeR[1]] );
5711 hasFurtherSegment = gFalse;
5714 yt = Y;
5716 xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
5717 xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
5719 ca = yt * scanColorMapL[0] + scanColorMapL[1];
5720 ct = yt * scanColorMapR[0] + scanColorMapR[1];
5722 scanLimitL = splashRound(xa);
5723 scanLimitR = splashRound(xt);
5725 // Ok. Now: init the color interpolation depending on the X
5726 // coordinate inside of the current scanline:
5727 scanColorMap[0] = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL));
5728 scanColorMap[1] = ca - scanLimitL * scanColorMap[0];
5730 // handled by clipping:
5731 // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
5732 assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
5733 assert(scanLineOff == Y * rowSize);
5735 colorinterp = scanColorMap[0] * scanLimitL + scanColorMap[1];
5737 bitmapOff = scanLineOff + scanLimitL * colorComps;
5738 for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap[0], bitmapOff += colorComps) {
5739 // FIXME : standard rectangular clipping can be done for a
5740 // complete scanline which is faster
5741 // --> see SplashClip and its methods
5742 if (!clip->test(X, Y))
5743 continue;
5745 assert(fabs(colorinterp - (scanColorMap[0] * X + scanColorMap[1])) < 1e-10);
5746 assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
5748 shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]);
5750 // make the shading visible.
5751 // Note that opacity is handled by the bDirectBlit stuff, see
5752 // above for comments and below for implementation.
5753 if (hasAlpha)
5754 bitmapAlpha[Y * bitmapWidth + X] = 255;
5758 } else {
5759 return gFalse;
5762 if (!bDirectBlit) {
5763 // ok. Finalize the stuff by blitting the shading into the final
5764 // geometry, this time respecting the rendering pipe.
5765 int W = blitTarget->getWidth();
5766 int H = blitTarget->getHeight();
5767 cur = cSrcVal;
5769 for (int X = 0; X < W; ++X) {
5770 for (int Y = 0; Y < H; ++Y) {
5771 if (!bitmapAlpha[Y * bitmapWidth + X])
5772 continue; // draw only parts of the shading!
5773 bitmapOff = Y * rowSize + colorComps * X;
5775 for (int m = 0; m < colorComps; ++m)
5776 cur[m] = bitmapData[bitmapOff + m];
5777 if (vectorAntialias) {
5778 drawAAPixel(&pipe, X, Y);
5779 } else {
5780 drawPixel(&pipe, X, Y, gTrue); // no clipping - has already been done.
5785 delete blitTarget;
5786 blitTarget = NULL;
5789 return gTrue;
5792 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
5793 int xDest, int yDest, int w, int h) {
5794 SplashColorPtr p, sp;
5795 Guchar *q;
5796 int x, y, mask, srcMask;
5798 if (src->mode != bitmap->mode) {
5799 return splashErrModeMismatch;
5802 if (unlikely(!bitmap->data)) {
5803 return splashErrZeroImage;
5806 switch (bitmap->mode) {
5807 case splashModeMono1:
5808 for (y = 0; y < h; ++y) {
5809 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
5810 mask = 0x80 >> (xDest & 7);
5811 sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
5812 srcMask = 0x80 >> (xSrc & 7);
5813 for (x = 0; x < w; ++x) {
5814 if (*sp & srcMask) {
5815 *p |= mask;
5816 } else {
5817 *p &= ~mask;
5819 if (!(mask >>= 1)) {
5820 mask = 0x80;
5821 ++p;
5823 if (!(srcMask >>= 1)) {
5824 srcMask = 0x80;
5825 ++sp;
5829 break;
5830 case splashModeMono8:
5831 for (y = 0; y < h; ++y) {
5832 p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
5833 sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc];
5834 for (x = 0; x < w; ++x) {
5835 *p++ = *sp++;
5838 break;
5839 case splashModeRGB8:
5840 case splashModeBGR8:
5841 for (y = 0; y < h; ++y) {
5842 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
5843 sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
5844 for (x = 0; x < w; ++x) {
5845 *p++ = *sp++;
5846 *p++ = *sp++;
5847 *p++ = *sp++;
5850 break;
5851 case splashModeXBGR8:
5852 for (y = 0; y < h; ++y) {
5853 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5854 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5855 for (x = 0; x < w; ++x) {
5856 *p++ = *sp++;
5857 *p++ = *sp++;
5858 *p++ = *sp++;
5859 *p++ = 255;
5860 sp++;
5863 break;
5864 #if SPLASH_CMYK
5865 case splashModeCMYK8:
5866 for (y = 0; y < h; ++y) {
5867 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5868 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5869 for (x = 0; x < w; ++x) {
5870 *p++ = *sp++;
5871 *p++ = *sp++;
5872 *p++ = *sp++;
5873 *p++ = *sp++;
5876 break;
5877 case splashModeDeviceN8:
5878 for (y = 0; y < h; ++y) {
5879 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS+4) * xDest];
5880 sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS+4) * xSrc];
5881 for (x = 0; x < w; ++x) {
5882 for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
5883 *p++ = *sp++;
5886 break;
5887 #endif
5890 if (bitmap->alpha) {
5891 for (y = 0; y < h; ++y) {
5892 q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
5893 memset(q, 0x00, w);
5897 return splashOk;
5900 SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
5901 GBool flatten) {
5902 SplashPath *pathIn, *dashPath, *pathOut;
5903 SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
5904 SplashCoord crossprod, dotprod, miter, m;
5905 GBool first, last, closed;
5906 int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
5907 int left0, left1, left2, right0, right1, right2, join0, join1, join2;
5908 int leftFirst, rightFirst, firstPt;
5910 pathOut = new SplashPath();
5912 if (path->length == 0) {
5913 return pathOut;
5916 if (flatten) {
5917 pathIn = flattenPath(path, state->matrix, state->flatness);
5918 if (state->lineDashLength > 0) {
5919 dashPath = makeDashedPath(pathIn);
5920 delete pathIn;
5921 pathIn = dashPath;
5922 if (pathIn->length == 0) {
5923 delete pathIn;
5924 return pathOut;
5927 } else {
5928 pathIn = path;
5931 subpathStart0 = subpathStart1 = 0; // make gcc happy
5932 seg = 0; // make gcc happy
5933 closed = gFalse; // make gcc happy
5934 left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
5935 leftFirst = rightFirst = firstPt = 0; // make gcc happy
5937 i0 = 0;
5938 for (i1 = i0;
5939 !(pathIn->flags[i1] & splashPathLast) &&
5940 i1 + 1 < pathIn->length &&
5941 pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
5942 pathIn->pts[i1+1].y == pathIn->pts[i1].y;
5943 ++i1) ;
5945 while (i1 < pathIn->length) {
5946 if ((first = pathIn->flags[i0] & splashPathFirst)) {
5947 subpathStart0 = i0;
5948 subpathStart1 = i1;
5949 seg = 0;
5950 closed = pathIn->flags[i0] & splashPathClosed;
5952 j0 = i1 + 1;
5953 if (j0 < pathIn->length) {
5954 for (j1 = j0;
5955 !(pathIn->flags[j1] & splashPathLast) &&
5956 j1 + 1 < pathIn->length &&
5957 pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
5958 pathIn->pts[j1+1].y == pathIn->pts[j1].y;
5959 ++j1) ;
5960 } else {
5961 j1 = j0;
5963 if (pathIn->flags[i1] & splashPathLast) {
5964 if (first && state->lineCap == splashLineCapRound) {
5965 // special case: zero-length subpath with round line caps -->
5966 // draw a circle
5967 pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5968 pathIn->pts[i0].y);
5969 pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5970 pathIn->pts[i0].y + bezierCircle2 * w,
5971 pathIn->pts[i0].x + bezierCircle2 * w,
5972 pathIn->pts[i0].y + (SplashCoord)0.5 * w,
5973 pathIn->pts[i0].x,
5974 pathIn->pts[i0].y + (SplashCoord)0.5 * w);
5975 pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
5976 pathIn->pts[i0].y + (SplashCoord)0.5 * w,
5977 pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5978 pathIn->pts[i0].y + bezierCircle2 * w,
5979 pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5980 pathIn->pts[i0].y);
5981 pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5982 pathIn->pts[i0].y - bezierCircle2 * w,
5983 pathIn->pts[i0].x - bezierCircle2 * w,
5984 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
5985 pathIn->pts[i0].x,
5986 pathIn->pts[i0].y - (SplashCoord)0.5 * w);
5987 pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
5988 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
5989 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5990 pathIn->pts[i0].y - bezierCircle2 * w,
5991 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5992 pathIn->pts[i0].y);
5993 pathOut->close();
5995 i0 = j0;
5996 i1 = j1;
5997 continue;
5999 last = pathIn->flags[j1] & splashPathLast;
6000 if (last) {
6001 k0 = subpathStart1 + 1;
6002 } else {
6003 k0 = j1 + 1;
6005 for (k1 = k0;
6006 !(pathIn->flags[k1] & splashPathLast) &&
6007 k1 + 1 < pathIn->length &&
6008 pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
6009 pathIn->pts[k1+1].y == pathIn->pts[k1].y;
6010 ++k1) ;
6012 // compute the deltas for segment (i1, j0)
6013 #if USE_FIXEDPOINT
6014 // the 1/d value can be small, which introduces significant
6015 // inaccuracies in fixed point mode
6016 d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6017 pathIn->pts[j0].x, pathIn->pts[j0].y);
6018 dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
6019 dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
6020 #else
6021 d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6022 pathIn->pts[j0].x, pathIn->pts[j0].y);
6023 dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
6024 dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
6025 #endif
6026 wdx = (SplashCoord)0.5 * w * dx;
6027 wdy = (SplashCoord)0.5 * w * dy;
6029 // draw the start cap
6030 pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
6031 if (i0 == subpathStart0) {
6032 firstPt = pathOut->length - 1;
6034 if (first && !closed) {
6035 switch (state->lineCap) {
6036 case splashLineCapButt:
6037 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6038 break;
6039 case splashLineCapRound:
6040 pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
6041 pathIn->pts[i0].y + wdx - bezierCircle * wdy,
6042 pathIn->pts[i0].x - wdx - bezierCircle * wdy,
6043 pathIn->pts[i0].y - wdy + bezierCircle * wdx,
6044 pathIn->pts[i0].x - wdx,
6045 pathIn->pts[i0].y - wdy);
6046 pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
6047 pathIn->pts[i0].y - wdy - bezierCircle * wdx,
6048 pathIn->pts[i0].x + wdy - bezierCircle * wdx,
6049 pathIn->pts[i0].y - wdx - bezierCircle * wdy,
6050 pathIn->pts[i0].x + wdy,
6051 pathIn->pts[i0].y - wdx);
6052 break;
6053 case splashLineCapProjecting:
6054 pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy,
6055 pathIn->pts[i0].y + wdx - wdy);
6056 pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
6057 pathIn->pts[i0].y - wdx - wdy);
6058 pathOut->lineTo(pathIn->pts[i0].x + wdy,
6059 pathIn->pts[i0].y - wdx);
6060 break;
6062 } else {
6063 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6066 // draw the left side of the segment rectangle
6067 left2 = pathOut->length - 1;
6068 pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
6070 // draw the end cap
6071 if (last && !closed) {
6072 switch (state->lineCap) {
6073 case splashLineCapButt:
6074 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6075 break;
6076 case splashLineCapRound:
6077 pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
6078 pathIn->pts[j0].y - wdx + bezierCircle * wdy,
6079 pathIn->pts[j0].x + wdx + bezierCircle * wdy,
6080 pathIn->pts[j0].y + wdy - bezierCircle * wdx,
6081 pathIn->pts[j0].x + wdx,
6082 pathIn->pts[j0].y + wdy);
6083 pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
6084 pathIn->pts[j0].y + wdy + bezierCircle * wdx,
6085 pathIn->pts[j0].x - wdy + bezierCircle * wdx,
6086 pathIn->pts[j0].y + wdx + bezierCircle * wdy,
6087 pathIn->pts[j0].x - wdy,
6088 pathIn->pts[j0].y + wdx);
6089 break;
6090 case splashLineCapProjecting:
6091 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
6092 pathIn->pts[j0].y - wdx + wdy);
6093 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
6094 pathIn->pts[j0].y + wdx + wdy);
6095 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6096 pathIn->pts[j0].y + wdx);
6097 break;
6099 } else {
6100 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6103 // draw the right side of the segment rectangle
6104 // (NB: if stroke adjustment is enabled, the closepath operation MUST
6105 // add a segment because this segment is used for a hint)
6106 right2 = pathOut->length - 1;
6107 pathOut->close(state->strokeAdjust);
6109 // draw the join
6110 join2 = pathOut->length;
6111 if (!last || closed) {
6113 // compute the deltas for segment (j1, k0)
6114 #if USE_FIXEDPOINT
6115 // the 1/d value can be small, which introduces significant
6116 // inaccuracies in fixed point mode
6117 d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6118 pathIn->pts[k0].x, pathIn->pts[k0].y);
6119 dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
6120 dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
6121 #else
6122 d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6123 pathIn->pts[k0].x, pathIn->pts[k0].y);
6124 dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
6125 dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
6126 #endif
6127 wdxNext = (SplashCoord)0.5 * w * dxNext;
6128 wdyNext = (SplashCoord)0.5 * w * dyNext;
6130 // compute the join parameters
6131 crossprod = dx * dyNext - dy * dxNext;
6132 dotprod = -(dx * dxNext + dy * dyNext);
6133 if (dotprod > 0.9999) {
6134 // avoid a divide-by-zero -- set miter to something arbitrary
6135 // such that sqrt(miter) will exceed miterLimit (and m is never
6136 // used in that situation)
6137 // (note: the comparison value (0.9999) has to be less than
6138 // 1-epsilon, where epsilon is the smallest value
6139 // representable in the fixed point format)
6140 miter = (state->miterLimit + 1) * (state->miterLimit + 1);
6141 m = 0;
6142 } else {
6143 miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
6144 if (miter < 1) {
6145 // this can happen because of floating point inaccuracies
6146 miter = 1;
6148 m = splashSqrt(miter - 1);
6151 // round join
6152 if (state->lineJoin == splashLineJoinRound) {
6153 pathOut->moveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6154 pathIn->pts[j0].y);
6155 pathOut->curveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6156 pathIn->pts[j0].y + bezierCircle2 * w,
6157 pathIn->pts[j0].x + bezierCircle2 * w,
6158 pathIn->pts[j0].y + (SplashCoord)0.5 * w,
6159 pathIn->pts[j0].x,
6160 pathIn->pts[j0].y + (SplashCoord)0.5 * w);
6161 pathOut->curveTo(pathIn->pts[j0].x - bezierCircle2 * w,
6162 pathIn->pts[j0].y + (SplashCoord)0.5 * w,
6163 pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6164 pathIn->pts[j0].y + bezierCircle2 * w,
6165 pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6166 pathIn->pts[j0].y);
6167 pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6168 pathIn->pts[j0].y - bezierCircle2 * w,
6169 pathIn->pts[j0].x - bezierCircle2 * w,
6170 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6171 pathIn->pts[j0].x,
6172 pathIn->pts[j0].y - (SplashCoord)0.5 * w);
6173 pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w,
6174 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6175 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6176 pathIn->pts[j0].y - bezierCircle2 * w,
6177 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6178 pathIn->pts[j0].y);
6180 } else {
6181 pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
6183 // angle < 180
6184 if (crossprod < 0) {
6185 pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
6186 pathIn->pts[j0].y + wdxNext);
6187 // miter join inside limit
6188 if (state->lineJoin == splashLineJoinMiter &&
6189 splashSqrt(miter) <= state->miterLimit) {
6190 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
6191 pathIn->pts[j0].y + wdx + wdy * m);
6192 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6193 pathIn->pts[j0].y + wdx);
6194 // bevel join or miter join outside limit
6195 } else {
6196 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6197 pathIn->pts[j0].y + wdx);
6200 // angle >= 180
6201 } else {
6202 pathOut->lineTo(pathIn->pts[j0].x + wdy,
6203 pathIn->pts[j0].y - wdx);
6204 // miter join inside limit
6205 if (state->lineJoin == splashLineJoinMiter &&
6206 splashSqrt(miter) <= state->miterLimit) {
6207 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
6208 pathIn->pts[j0].y - wdx + wdy * m);
6209 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6210 pathIn->pts[j0].y - wdxNext);
6211 // bevel join or miter join outside limit
6212 } else {
6213 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6214 pathIn->pts[j0].y - wdxNext);
6219 pathOut->close();
6222 // add stroke adjustment hints
6223 if (state->strokeAdjust) {
6224 if (seg == 0 && !closed) {
6225 if (state->lineCap == splashLineCapButt) {
6226 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6227 firstPt, firstPt + 1);
6228 if (last) {
6229 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6230 left2 + 1, left2 + 2);
6232 } else if (state->lineCap == splashLineCapProjecting) {
6233 if (last) {
6234 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6235 firstPt + 1, firstPt + 2);
6236 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6237 left2 + 2, left2 + 3);
6238 } else {
6239 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1,
6240 firstPt + 1, firstPt + 2);
6244 if (seg >= 1) {
6245 if (seg >= 2) {
6246 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6247 pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
6248 } else {
6249 pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
6251 pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
6253 left0 = left1;
6254 left1 = left2;
6255 right0 = right1;
6256 right1 = right2;
6257 join0 = join1;
6258 join1 = join2;
6259 if (seg == 0) {
6260 leftFirst = left2;
6261 rightFirst = right2;
6263 if (last) {
6264 if (seg >= 2) {
6265 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6266 pathOut->addStrokeAdjustHint(left1, right1,
6267 join0, pathOut->length - 1);
6268 } else {
6269 pathOut->addStrokeAdjustHint(left1, right1,
6270 firstPt, pathOut->length - 1);
6272 if (closed) {
6273 pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
6274 pathOut->addStrokeAdjustHint(left1, right1,
6275 rightFirst + 1, rightFirst + 1);
6276 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6277 left1 + 1, right1);
6278 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6279 join1, pathOut->length - 1);
6281 if (!closed && seg > 0) {
6282 if (state->lineCap == splashLineCapButt) {
6283 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1,
6284 left1 + 1, left1 + 2);
6285 } else if (state->lineCap == splashLineCapProjecting) {
6286 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2,
6287 left1 + 2, left1 + 3);
6293 i0 = j0;
6294 i1 = j1;
6295 ++seg;
6298 if (pathIn != path) {
6299 delete pathIn;
6302 return pathOut;
6305 void Splash::dumpPath(SplashPath *path) {
6306 int i;
6308 for (i = 0; i < path->length; ++i) {
6309 printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
6310 i, (double)path->pts[i].x, (double)path->pts[i].y,
6311 (path->flags[i] & splashPathFirst) ? " first" : "",
6312 (path->flags[i] & splashPathLast) ? " last" : "",
6313 (path->flags[i] & splashPathClosed) ? " closed" : "",
6314 (path->flags[i] & splashPathCurve) ? " curve" : "");
6318 void Splash::dumpXPath(SplashXPath *path) {
6319 int i;
6321 for (i = 0; i < path->length; ++i) {
6322 printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n",
6323 i, (double)path->segs[i].x0, (double)path->segs[i].y0,
6324 (double)path->segs[i].x1, (double)path->segs[i].y1,
6325 (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
6326 (path->segs[i].flags & splashXPathVert) ? "V" : " ",
6327 (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
6331 SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox,
6332 SplashPattern *pattern) {
6333 SplashPipe pipe;
6334 SplashXPath *xPath;
6335 SplashXPathScanner *scanner;
6336 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
6337 SplashClipResult clipRes;
6339 if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure
6340 return splashErrGeneric;
6342 if (path->length == 0) {
6343 return splashErrEmptyPath;
6345 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
6346 if (vectorAntialias) {
6347 xPath->aaScale();
6349 xPath->sort();
6350 yMinI = state->clip->getYMinI();
6351 yMaxI = state->clip->getYMaxI();
6352 if (vectorAntialias && !inShading) {
6353 yMinI = yMinI * splashAASize;
6354 yMaxI = (yMaxI + 1) * splashAASize - 1;
6356 scanner = new SplashXPathScanner(xPath, gFalse, yMinI, yMaxI);
6358 // get the min and max x and y values
6359 if (vectorAntialias) {
6360 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
6361 } else {
6362 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
6365 // check clipping
6366 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
6367 // limit the y range
6368 if (yMinI < state->clip->getYMinI()) {
6369 yMinI = state->clip->getYMinI();
6371 if (yMaxI > state->clip->getYMaxI()) {
6372 yMaxI = state->clip->getYMaxI();
6375 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, gFalse);
6377 // draw the spans
6378 if (vectorAntialias) {
6379 for (y = yMinI; y <= yMaxI; ++y) {
6380 scanner->renderAALine(aaBuf, &x0, &x1, y);
6381 if (clipRes != splashClipAllInside) {
6382 state->clip->clipAALine(aaBuf, &x0, &x1, y);
6384 #if splashAASize == 4
6385 if (!hasBBox && y > yMinI && y < yMaxI) {
6386 // correct shape on left side if clip is
6387 // vertical through the middle of shading:
6388 Guchar *p0, *p1, *p2, *p3;
6389 Guchar c1, c2, c3, c4;
6390 p0 = aaBuf->getDataPtr() + (x0 >> 1);
6391 p1 = p0 + aaBuf->getRowSize();
6392 p2 = p1 + aaBuf->getRowSize();
6393 p3 = p2 + aaBuf->getRowSize();
6394 if (x0 & 1) {
6395 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6396 } else {
6397 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6399 if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03
6400 && c1 == c2 && c2 == c3 && c3 == c4 &&
6401 pattern->testPosition(x0 - 1, y) )
6403 Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0;
6404 *p0 |= shapeCorrection;
6405 *p1 |= shapeCorrection;
6406 *p2 |= shapeCorrection;
6407 *p3 |= shapeCorrection;
6409 // correct shape on right side if clip is
6410 // through the middle of shading:
6411 p0 = aaBuf->getDataPtr() + (x1 >> 1);
6412 p1 = p0 + aaBuf->getRowSize();
6413 p2 = p1 + aaBuf->getRowSize();
6414 p3 = p2 + aaBuf->getRowSize();
6415 if (x1 & 1) {
6416 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6417 } else {
6418 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6421 if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c
6422 && c1 == c2 && c2 == c3 && c3 == c4 &&
6423 pattern->testPosition(x1 + 1, y) )
6425 Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0;
6426 *p0 |= shapeCorrection;
6427 *p1 |= shapeCorrection;
6428 *p2 |= shapeCorrection;
6429 *p3 |= shapeCorrection;
6432 #endif
6433 drawAALine(&pipe, x0, x1, y);
6435 } else {
6436 SplashClipResult clipRes2;
6437 for (y = yMinI; y <= yMaxI; ++y) {
6438 while (scanner->getNextSpan(y, &x0, &x1)) {
6439 if (clipRes == splashClipAllInside) {
6440 drawSpan(&pipe, x0, x1, y, gTrue);
6441 } else {
6442 // limit the x range
6443 if (x0 < state->clip->getXMinI()) {
6444 x0 = state->clip->getXMinI();
6446 if (x1 > state->clip->getXMaxI()) {
6447 x1 = state->clip->getXMaxI();
6449 clipRes2 = state->clip->testSpan(x0, x1, y);
6450 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
6456 opClipRes = clipRes;
6458 delete scanner;
6459 delete xPath;
6460 return splashOk;