beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / splash / Splash.cc
blob5bc7767a3057b45fab5e03670960554f0e63a4dc
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 = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha;
1758 void Splash::setFillAlpha(SplashCoord alpha) {
1759 state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha;
1762 void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) {
1763 state->patternStrokeAlpha = strokeAlpha;
1764 state->patternFillAlpha = fillAlpha;
1765 state->multiplyPatternAlpha = gTrue;
1768 void Splash::clearPatternAlpha() {
1769 state->patternStrokeAlpha = 1;
1770 state->patternFillAlpha = 1;
1771 state->multiplyPatternAlpha = gFalse;
1774 void Splash::setFillOverprint(GBool fop) {
1775 state->fillOverprint = fop;
1778 void Splash::setStrokeOverprint(GBool gop) {
1779 state->strokeOverprint = gop;
1782 void Splash::setOverprintMode(int opm) {
1783 state->overprintMode = opm;
1786 void Splash::setLineWidth(SplashCoord lineWidth) {
1787 state->lineWidth = lineWidth;
1790 void Splash::setLineCap(int lineCap) {
1791 state->lineCap = lineCap;
1794 void Splash::setLineJoin(int lineJoin) {
1795 state->lineJoin = lineJoin;
1798 void Splash::setMiterLimit(SplashCoord miterLimit) {
1799 state->miterLimit = miterLimit;
1802 void Splash::setFlatness(SplashCoord flatness) {
1803 if (flatness < 1) {
1804 state->flatness = 1;
1805 } else {
1806 state->flatness = flatness;
1810 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
1811 SplashCoord lineDashPhase) {
1812 state->setLineDash(lineDash, lineDashLength, lineDashPhase);
1815 void Splash::setStrokeAdjust(GBool strokeAdjust) {
1816 state->strokeAdjust = strokeAdjust;
1819 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
1820 SplashCoord x1, SplashCoord y1) {
1821 state->clip->resetToRect(x0, y0, x1, y1);
1824 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
1825 SplashCoord x1, SplashCoord y1) {
1826 return state->clip->clipToRect(x0, y0, x1, y1);
1829 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
1830 return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
1833 void Splash::setSoftMask(SplashBitmap *softMask) {
1834 state->setSoftMask(softMask);
1837 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
1838 int alpha0XA, int alpha0YA) {
1839 alpha0Bitmap = alpha0BitmapA;
1840 alpha0X = alpha0XA;
1841 alpha0Y = alpha0YA;
1842 state->inNonIsolatedGroup = gTrue;
1845 void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
1846 Guchar *gray) {
1847 state->setTransfer(red, green, blue, gray);
1850 void Splash::setOverprintMask(Guint overprintMask, GBool additive) {
1851 state->overprintMask = overprintMask;
1852 state->overprintAdditive = additive;
1855 //------------------------------------------------------------------------
1856 // state save/restore
1857 //------------------------------------------------------------------------
1859 void Splash::saveState() {
1860 SplashState *newState;
1862 newState = state->copy();
1863 newState->next = state;
1864 state = newState;
1867 SplashError Splash::restoreState() {
1868 SplashState *oldState;
1870 if (!state->next) {
1871 return splashErrNoSave;
1873 oldState = state;
1874 state = state->next;
1875 delete oldState;
1876 return splashOk;
1879 //------------------------------------------------------------------------
1880 // drawing operations
1881 //------------------------------------------------------------------------
1883 void Splash::clear(SplashColorPtr color, Guchar alpha) {
1884 SplashColorPtr row, p;
1885 Guchar mono;
1886 int x, y;
1888 switch (bitmap->mode) {
1889 case splashModeMono1:
1890 mono = (color[0] & 0x80) ? 0xff : 0x00;
1891 if (bitmap->rowSize < 0) {
1892 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1893 mono, -bitmap->rowSize * bitmap->height);
1894 } else {
1895 memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1897 break;
1898 case splashModeMono8:
1899 if (bitmap->rowSize < 0) {
1900 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1901 color[0], -bitmap->rowSize * bitmap->height);
1902 } else {
1903 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1905 break;
1906 case splashModeRGB8:
1907 if (color[0] == color[1] && color[1] == color[2]) {
1908 if (bitmap->rowSize < 0) {
1909 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1910 color[0], -bitmap->rowSize * bitmap->height);
1911 } else {
1912 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1914 } else {
1915 row = bitmap->data;
1916 for (y = 0; y < bitmap->height; ++y) {
1917 p = row;
1918 for (x = 0; x < bitmap->width; ++x) {
1919 *p++ = color[2];
1920 *p++ = color[1];
1921 *p++ = color[0];
1923 row += bitmap->rowSize;
1926 break;
1927 case splashModeXBGR8:
1928 if (color[0] == color[1] && color[1] == color[2]) {
1929 if (bitmap->rowSize < 0) {
1930 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1931 color[0], -bitmap->rowSize * bitmap->height);
1932 } else {
1933 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1935 } else {
1936 row = bitmap->data;
1937 for (y = 0; y < bitmap->height; ++y) {
1938 p = row;
1939 for (x = 0; x < bitmap->width; ++x) {
1940 *p++ = color[0];
1941 *p++ = color[1];
1942 *p++ = color[2];
1943 *p++ = 255;
1945 row += bitmap->rowSize;
1948 break;
1949 case splashModeBGR8:
1950 if (color[0] == color[1] && color[1] == color[2]) {
1951 if (bitmap->rowSize < 0) {
1952 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1953 color[0], -bitmap->rowSize * bitmap->height);
1954 } else {
1955 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1957 } else {
1958 row = bitmap->data;
1959 for (y = 0; y < bitmap->height; ++y) {
1960 p = row;
1961 for (x = 0; x < bitmap->width; ++x) {
1962 *p++ = color[0];
1963 *p++ = color[1];
1964 *p++ = color[2];
1966 row += bitmap->rowSize;
1969 break;
1970 #if SPLASH_CMYK
1971 case splashModeCMYK8:
1972 if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1973 if (bitmap->rowSize < 0) {
1974 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1975 color[0], -bitmap->rowSize * bitmap->height);
1976 } else {
1977 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1979 } else {
1980 row = bitmap->data;
1981 for (y = 0; y < bitmap->height; ++y) {
1982 p = row;
1983 for (x = 0; x < bitmap->width; ++x) {
1984 *p++ = color[0];
1985 *p++ = color[1];
1986 *p++ = color[2];
1987 *p++ = color[3];
1989 row += bitmap->rowSize;
1992 break;
1993 case splashModeDeviceN8:
1994 row = bitmap->data;
1995 for (y = 0; y < bitmap->height; ++y) {
1996 p = row;
1997 for (x = 0; x < bitmap->width; ++x) {
1998 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
1999 *p++ = color[cp];
2001 row += bitmap->rowSize;
2003 break;
2004 #endif
2007 if (bitmap->alpha) {
2008 memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
2011 updateModX(0);
2012 updateModY(0);
2013 updateModX(bitmap->width - 1);
2014 updateModY(bitmap->height - 1);
2017 SplashError Splash::stroke(SplashPath *path) {
2018 SplashPath *path2, *dPath;
2019 SplashCoord d1, d2, t1, t2, w;
2021 if (debugMode) {
2022 printf("stroke [dash:%d] [width:%.2f]:\n",
2023 state->lineDashLength, (double)state->lineWidth);
2024 dumpPath(path);
2026 opClipRes = splashClipAllOutside;
2027 if (path->length == 0) {
2028 return splashErrEmptyPath;
2030 path2 = flattenPath(path, state->matrix, state->flatness);
2031 if (state->lineDashLength > 0) {
2032 dPath = makeDashedPath(path2);
2033 delete path2;
2034 path2 = dPath;
2035 if (path2->length == 0) {
2036 delete path2;
2037 return splashErrEmptyPath;
2041 // transform a unit square, and take the half the max of the two
2042 // diagonals; the product of this number and the line width is the
2043 // (approximate) transformed line width
2044 t1 = state->matrix[0] + state->matrix[2];
2045 t2 = state->matrix[1] + state->matrix[3];
2046 d1 = t1 * t1 + t2 * t2;
2047 t1 = state->matrix[0] - state->matrix[2];
2048 t2 = state->matrix[1] - state->matrix[3];
2049 d2 = t1 * t1 + t2 * t2;
2050 if (d2 > d1) {
2051 d1 = d2;
2053 d1 *= 0.5;
2054 if (d1 > 0 &&
2055 d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
2056 w = minLineWidth / splashSqrt(d1);
2057 strokeWide(path2, w);
2058 } else if (bitmap->mode == splashModeMono1) {
2059 // this gets close to Adobe's behavior in mono mode
2060 if (d1 * state->lineWidth <= 2) {
2061 strokeNarrow(path2);
2062 } else {
2063 strokeWide(path2, state->lineWidth);
2065 } else {
2066 if (state->lineWidth == 0) {
2067 strokeNarrow(path2);
2068 } else {
2069 strokeWide(path2, state->lineWidth);
2073 delete path2;
2074 return splashOk;
2077 void Splash::strokeNarrow(SplashPath *path) {
2078 SplashPipe pipe;
2079 SplashXPath *xPath;
2080 SplashXPathSeg *seg;
2081 int x0, x1, y0, y1, xa, xb, y;
2082 SplashCoord dxdy;
2083 SplashClipResult clipRes;
2084 int nClipRes[3];
2085 int i;
2087 nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
2089 xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
2091 pipeInit(&pipe, 0, 0, state->strokePattern, NULL,
2092 (Guchar)splashRound(state->strokeAlpha * 255),
2093 gFalse, gFalse);
2095 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
2096 if (seg->y0 <= seg->y1) {
2097 y0 = splashFloor(seg->y0);
2098 y1 = splashFloor(seg->y1);
2099 x0 = splashFloor(seg->x0);
2100 x1 = splashFloor(seg->x1);
2101 } else {
2102 y0 = splashFloor(seg->y1);
2103 y1 = splashFloor(seg->y0);
2104 x0 = splashFloor(seg->x1);
2105 x1 = splashFloor(seg->x0);
2107 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
2108 x0 <= x1 ? x1 : x0, y1))
2109 != splashClipAllOutside) {
2110 if (y0 == y1) {
2111 if (x0 <= x1) {
2112 drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
2113 } else {
2114 drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
2116 } else {
2117 dxdy = seg->dxdy;
2118 if (y0 < state->clip->getYMinI()) {
2119 y0 = state->clip->getYMinI();
2120 x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
2122 if (y1 > state->clip->getYMaxI()) {
2123 y1 = state->clip->getYMaxI();
2124 x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
2126 if (x0 <= x1) {
2127 xa = x0;
2128 for (y = y0; y <= y1; ++y) {
2129 if (y < y1) {
2130 xb = splashFloor(seg->x0 +
2131 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2132 } else {
2133 xb = x1 + 1;
2135 if (xa == xb) {
2136 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2137 } else {
2138 drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
2140 xa = xb;
2142 } else {
2143 xa = x0;
2144 for (y = y0; y <= y1; ++y) {
2145 if (y < y1) {
2146 xb = splashFloor(seg->x0 +
2147 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2148 } else {
2149 xb = x1 - 1;
2151 if (xa == xb) {
2152 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2153 } else {
2154 drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
2156 xa = xb;
2161 ++nClipRes[clipRes];
2163 if (nClipRes[splashClipPartial] ||
2164 (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
2165 opClipRes = splashClipPartial;
2166 } else if (nClipRes[splashClipAllInside]) {
2167 opClipRes = splashClipAllInside;
2168 } else {
2169 opClipRes = splashClipAllOutside;
2172 delete xPath;
2175 void Splash::strokeWide(SplashPath *path, SplashCoord w) {
2176 SplashPath *path2;
2178 path2 = makeStrokePath(path, w, gFalse);
2179 fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
2180 delete path2;
2183 SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
2184 SplashCoord flatness) {
2185 SplashPath *fPath;
2186 SplashCoord flatness2;
2187 Guchar flag;
2188 int i;
2190 fPath = new SplashPath();
2191 #if USE_FIXEDPOINT
2192 flatness2 = flatness;
2193 #else
2194 flatness2 = flatness * flatness;
2195 #endif
2196 i = 0;
2197 while (i < path->length) {
2198 flag = path->flags[i];
2199 if (flag & splashPathFirst) {
2200 fPath->moveTo(path->pts[i].x, path->pts[i].y);
2201 ++i;
2202 } else {
2203 if (flag & splashPathCurve) {
2204 flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
2205 path->pts[i ].x, path->pts[i ].y,
2206 path->pts[i+1].x, path->pts[i+1].y,
2207 path->pts[i+2].x, path->pts[i+2].y,
2208 matrix, flatness2, fPath);
2209 i += 3;
2210 } else {
2211 fPath->lineTo(path->pts[i].x, path->pts[i].y);
2212 ++i;
2214 if (path->flags[i-1] & splashPathClosed) {
2215 fPath->close();
2219 return fPath;
2222 void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
2223 SplashCoord x1, SplashCoord y1,
2224 SplashCoord x2, SplashCoord y2,
2225 SplashCoord x3, SplashCoord y3,
2226 SplashCoord *matrix, SplashCoord flatness2,
2227 SplashPath *fPath) {
2228 SplashCoord cx[splashMaxCurveSplits + 1][3];
2229 SplashCoord cy[splashMaxCurveSplits + 1][3];
2230 int cNext[splashMaxCurveSplits + 1];
2231 SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
2232 SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
2233 SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
2234 int p1, p2, p3;
2236 // initial segment
2237 p1 = 0;
2238 p2 = splashMaxCurveSplits;
2239 cx[p1][0] = x0; cy[p1][0] = y0;
2240 cx[p1][1] = x1; cy[p1][1] = y1;
2241 cx[p1][2] = x2; cy[p1][2] = y2;
2242 cx[p2][0] = x3; cy[p2][0] = y3;
2243 cNext[p1] = p2;
2245 while (p1 < splashMaxCurveSplits) {
2247 // get the next segment
2248 xl0 = cx[p1][0]; yl0 = cy[p1][0];
2249 xx1 = cx[p1][1]; yy1 = cy[p1][1];
2250 xx2 = cx[p1][2]; yy2 = cy[p1][2];
2251 p2 = cNext[p1];
2252 xr3 = cx[p2][0]; yr3 = cy[p2][0];
2254 // compute the distances (in device space) from the control points
2255 // to the midpoint of the straight line (this is a bit of a hack,
2256 // but it's much faster than computing the actual distances to the
2257 // line)
2258 transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
2259 transform(matrix, xx1, yy1, &tx, &ty);
2260 #if USE_FIXEDPOINT
2261 d1 = splashDist(tx, ty, mx, my);
2262 #else
2263 dx = tx - mx;
2264 dy = ty - my;
2265 d1 = dx*dx + dy*dy;
2266 #endif
2267 transform(matrix, xx2, yy2, &tx, &ty);
2268 #if USE_FIXEDPOINT
2269 d2 = splashDist(tx, ty, mx, my);
2270 #else
2271 dx = tx - mx;
2272 dy = ty - my;
2273 d2 = dx*dx + dy*dy;
2274 #endif
2276 // if the curve is flat enough, or no more subdivisions are
2277 // allowed, add the straight line segment
2278 if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
2279 fPath->lineTo(xr3, yr3);
2280 p1 = p2;
2282 // otherwise, subdivide the curve
2283 } else {
2284 xl1 = splashAvg(xl0, xx1);
2285 yl1 = splashAvg(yl0, yy1);
2286 xh = splashAvg(xx1, xx2);
2287 yh = splashAvg(yy1, yy2);
2288 xl2 = splashAvg(xl1, xh);
2289 yl2 = splashAvg(yl1, yh);
2290 xr2 = splashAvg(xx2, xr3);
2291 yr2 = splashAvg(yy2, yr3);
2292 xr1 = splashAvg(xh, xr2);
2293 yr1 = splashAvg(yh, yr2);
2294 xr0 = splashAvg(xl2, xr1);
2295 yr0 = splashAvg(yl2, yr1);
2296 // add the new subdivision points
2297 p3 = (p1 + p2) / 2;
2298 cx[p1][1] = xl1; cy[p1][1] = yl1;
2299 cx[p1][2] = xl2; cy[p1][2] = yl2;
2300 cNext[p1] = p3;
2301 cx[p3][0] = xr0; cy[p3][0] = yr0;
2302 cx[p3][1] = xr1; cy[p3][1] = yr1;
2303 cx[p3][2] = xr2; cy[p3][2] = yr2;
2304 cNext[p3] = p2;
2309 SplashPath *Splash::makeDashedPath(SplashPath *path) {
2310 SplashPath *dPath;
2311 SplashCoord lineDashTotal;
2312 SplashCoord lineDashStartPhase, lineDashDist, segLen;
2313 SplashCoord x0, y0, x1, y1, xa, ya;
2314 GBool lineDashStartOn, lineDashOn, newPath;
2315 int lineDashStartIdx, lineDashIdx;
2316 int i, j, k;
2318 lineDashTotal = 0;
2319 for (i = 0; i < state->lineDashLength; ++i) {
2320 lineDashTotal += state->lineDash[i];
2322 // Acrobat simply draws nothing if the dash array is [0]
2323 if (lineDashTotal == 0) {
2324 return new SplashPath();
2326 lineDashStartPhase = state->lineDashPhase;
2327 i = splashFloor(lineDashStartPhase / lineDashTotal);
2328 lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
2329 lineDashStartOn = gTrue;
2330 lineDashStartIdx = 0;
2331 if (lineDashStartPhase > 0) {
2332 while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
2333 lineDashStartOn = !lineDashStartOn;
2334 lineDashStartPhase -= state->lineDash[lineDashStartIdx];
2335 ++lineDashStartIdx;
2337 if (unlikely(lineDashStartIdx == state->lineDashLength)) {
2338 return new SplashPath();
2342 dPath = new SplashPath();
2344 // process each subpath
2345 i = 0;
2346 while (i < path->length) {
2348 // find the end of the subpath
2349 for (j = i;
2350 j < path->length - 1 && !(path->flags[j] & splashPathLast);
2351 ++j) ;
2353 // initialize the dash parameters
2354 lineDashOn = lineDashStartOn;
2355 lineDashIdx = lineDashStartIdx;
2356 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
2358 // process each segment of the subpath
2359 newPath = gTrue;
2360 for (k = i; k < j; ++k) {
2362 // grab the segment
2363 x0 = path->pts[k].x;
2364 y0 = path->pts[k].y;
2365 x1 = path->pts[k+1].x;
2366 y1 = path->pts[k+1].y;
2367 segLen = splashDist(x0, y0, x1, y1);
2369 // process the segment
2370 while (segLen > 0) {
2372 if (lineDashDist >= segLen) {
2373 if (lineDashOn) {
2374 if (newPath) {
2375 dPath->moveTo(x0, y0);
2376 newPath = gFalse;
2378 dPath->lineTo(x1, y1);
2380 lineDashDist -= segLen;
2381 segLen = 0;
2383 } else {
2384 xa = x0 + (lineDashDist / segLen) * (x1 - x0);
2385 ya = y0 + (lineDashDist / segLen) * (y1 - y0);
2386 if (lineDashOn) {
2387 if (newPath) {
2388 dPath->moveTo(x0, y0);
2389 newPath = gFalse;
2391 dPath->lineTo(xa, ya);
2393 x0 = xa;
2394 y0 = ya;
2395 segLen -= lineDashDist;
2396 lineDashDist = 0;
2399 // get the next entry in the dash array
2400 if (lineDashDist <= 0) {
2401 lineDashOn = !lineDashOn;
2402 if (++lineDashIdx == state->lineDashLength) {
2403 lineDashIdx = 0;
2405 lineDashDist = state->lineDash[lineDashIdx];
2406 newPath = gTrue;
2410 i = j + 1;
2413 if (dPath->length == 0) {
2414 GBool allSame = gTrue;
2415 for (int i = 0; allSame && i < path->length - 1; ++i) {
2416 allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y;
2418 if (allSame) {
2419 x0 = path->pts[0].x;
2420 y0 = path->pts[0].y;
2421 dPath->moveTo(x0, y0);
2422 dPath->lineTo(x0, y0);
2426 return dPath;
2429 SplashError Splash::fill(SplashPath *path, GBool eo) {
2430 if (debugMode) {
2431 printf("fill [eo:%d]:\n", eo);
2432 dumpPath(path);
2434 return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
2437 inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA,
2438 SplashCoord *xMaxA, SplashCoord *yMaxA) {
2439 SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty;
2441 // make compiler happy:
2442 xMinFP = xMaxFP = yMinFP = yMaxFP = 0;
2443 for (int i = 0; i < path->length; ++i) {
2444 transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty);
2445 if (i == 0) {
2446 xMinFP = xMaxFP = tx;
2447 yMinFP = yMaxFP = ty;
2448 } else {
2449 if (tx < xMinFP) xMinFP = tx;
2450 if (tx > xMaxFP) xMaxFP = tx;
2451 if (ty < yMinFP) yMinFP = ty;
2452 if (ty > yMaxFP) yMaxFP = ty;
2456 *xMinA = xMinFP;
2457 *yMinA = yMinFP;
2458 *xMaxA = xMaxFP;
2459 *yMaxA = yMaxFP;
2462 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
2463 SplashPattern *pattern,
2464 SplashCoord alpha) {
2465 SplashPipe pipe;
2466 SplashXPath *xPath;
2467 SplashXPathScanner *scanner;
2468 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2469 SplashClipResult clipRes, clipRes2;
2470 GBool adjustLine = gFalse;
2471 int linePosI = 0;
2473 if (path->length == 0) {
2474 return splashErrEmptyPath;
2476 if (pathAllOutside(path)) {
2477 opClipRes = splashClipAllOutside;
2478 return splashOk;
2481 // add stroke adjustment hints for filled rectangles -- this only
2482 // applies to paths that consist of a single subpath
2483 // (this appears to match Acrobat's behavior)
2484 if (state->strokeAdjust && !path->hints) {
2485 int n;
2486 n = path->getLength();
2487 if (n == 4 &&
2488 !(path->flags[0] & splashPathClosed) &&
2489 !(path->flags[1] & splashPathLast) &&
2490 !(path->flags[2] & splashPathLast)) {
2491 path->close(gTrue);
2492 path->addStrokeAdjustHint(0, 2, 0, 4);
2493 path->addStrokeAdjustHint(1, 3, 0, 4);
2494 } else if (n == 5 &&
2495 (path->flags[0] & splashPathClosed) &&
2496 !(path->flags[1] & splashPathLast) &&
2497 !(path->flags[2] & splashPathLast) &&
2498 !(path->flags[3] & splashPathLast)) {
2499 path->addStrokeAdjustHint(0, 2, 0, 4);
2500 path->addStrokeAdjustHint(1, 3, 0, 4);
2504 if (thinLineMode != splashThinLineDefault) {
2505 if (state->clip->getXMinI() == state->clip->getXMaxI()) {
2506 linePosI = state->clip->getXMinI();
2507 adjustLine = gTrue;
2508 } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) {
2509 adjustLine = gTrue;
2510 linePosI = splashFloor(state->clip->getXMin() + state->lineWidth);
2511 } else if (state->clip->getYMinI() == state->clip->getYMaxI()) {
2512 linePosI = state->clip->getYMinI();
2513 adjustLine = gTrue;
2514 } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) {
2515 adjustLine = gTrue;
2516 linePosI = splashFloor(state->clip->getYMin() + state->lineWidth);
2520 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue,
2521 adjustLine, linePosI);
2522 if (vectorAntialias && !inShading) {
2523 xPath->aaScale();
2525 xPath->sort();
2526 yMinI = state->clip->getYMinI();
2527 yMaxI = state->clip->getYMaxI();
2528 if (vectorAntialias && !inShading) {
2529 yMinI = yMinI * splashAASize;
2530 yMaxI = (yMaxI + 1) * splashAASize - 1;
2532 scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI);
2534 // get the min and max x and y values
2535 if (vectorAntialias && !inShading) {
2536 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
2537 } else {
2538 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2541 if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) {
2542 SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP;
2543 getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP);
2544 delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP;
2545 if (delta < 0.2) {
2546 opClipRes = splashClipAllOutside;
2547 delete scanner;
2548 delete xPath;
2549 return splashOk;
2553 // check clipping
2554 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2555 != splashClipAllOutside) {
2556 if (scanner->hasPartialClip()) {
2557 clipRes = splashClipPartial;
2560 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255),
2561 vectorAntialias && !inShading, gFalse);
2563 // draw the spans
2564 if (vectorAntialias && !inShading) {
2565 for (y = yMinI; y <= yMaxI; ++y) {
2566 scanner->renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2567 if (clipRes != splashClipAllInside) {
2568 state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2570 Guchar lineShape = 255;
2571 GBool adjustLine = gFalse;
2572 if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) {
2573 // compute line shape for thin lines:
2574 SplashCoord mx, my, delta;
2575 transform(state->matrix, 0, 0, &mx, &my);
2576 transform(state->matrix, state->lineWidth, 0, &delta, &my);
2577 adjustLine = gTrue;
2578 lineShape = clip255((delta - mx) * 255);
2580 drawAALine(&pipe, x0, x1, y, adjustLine, lineShape);
2582 } else {
2583 for (y = yMinI; y <= yMaxI; ++y) {
2584 while (scanner->getNextSpan(y, &x0, &x1)) {
2585 if (clipRes == splashClipAllInside) {
2586 drawSpan(&pipe, x0, x1, y, gTrue);
2587 } else {
2588 // limit the x range
2589 if (x0 < state->clip->getXMinI()) {
2590 x0 = state->clip->getXMinI();
2592 if (x1 > state->clip->getXMaxI()) {
2593 x1 = state->clip->getXMaxI();
2595 clipRes2 = state->clip->testSpan(x0, x1, y);
2596 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2602 opClipRes = clipRes;
2604 delete scanner;
2605 delete xPath;
2606 return splashOk;
2609 GBool Splash::pathAllOutside(SplashPath *path) {
2610 SplashCoord xMin1, yMin1, xMax1, yMax1;
2611 SplashCoord xMin2, yMin2, xMax2, yMax2;
2612 SplashCoord x, y;
2613 int xMinI, yMinI, xMaxI, yMaxI;
2614 int i;
2616 xMin1 = xMax1 = path->pts[0].x;
2617 yMin1 = yMax1 = path->pts[0].y;
2618 for (i = 1; i < path->length; ++i) {
2619 if (path->pts[i].x < xMin1) {
2620 xMin1 = path->pts[i].x;
2621 } else if (path->pts[i].x > xMax1) {
2622 xMax1 = path->pts[i].x;
2624 if (path->pts[i].y < yMin1) {
2625 yMin1 = path->pts[i].y;
2626 } else if (path->pts[i].y > yMax1) {
2627 yMax1 = path->pts[i].y;
2631 transform(state->matrix, xMin1, yMin1, &x, &y);
2632 xMin2 = xMax2 = x;
2633 yMin2 = yMax2 = y;
2634 transform(state->matrix, xMin1, yMax1, &x, &y);
2635 if (x < xMin2) {
2636 xMin2 = x;
2637 } else if (x > xMax2) {
2638 xMax2 = x;
2640 if (y < yMin2) {
2641 yMin2 = y;
2642 } else if (y > yMax2) {
2643 yMax2 = y;
2645 transform(state->matrix, xMax1, yMin1, &x, &y);
2646 if (x < xMin2) {
2647 xMin2 = x;
2648 } else if (x > xMax2) {
2649 xMax2 = x;
2651 if (y < yMin2) {
2652 yMin2 = y;
2653 } else if (y > yMax2) {
2654 yMax2 = y;
2656 transform(state->matrix, xMax1, yMax1, &x, &y);
2657 if (x < xMin2) {
2658 xMin2 = x;
2659 } else if (x > xMax2) {
2660 xMax2 = x;
2662 if (y < yMin2) {
2663 yMin2 = y;
2664 } else if (y > yMax2) {
2665 yMax2 = y;
2667 xMinI = splashFloor(xMin2);
2668 yMinI = splashFloor(yMin2);
2669 xMaxI = splashFloor(xMax2);
2670 yMaxI = splashFloor(yMax2);
2672 return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) ==
2673 splashClipAllOutside;
2676 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
2677 SplashPipe pipe;
2678 SplashXPath *xPath;
2679 SplashXPathScanner *scanner;
2680 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2681 SplashClipResult clipRes, clipRes2;
2682 SplashBlendFunc origBlendFunc;
2684 if (path->length == 0) {
2685 return splashErrEmptyPath;
2687 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
2688 xPath->sort();
2689 scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(),
2690 state->clip->getYMaxI());
2692 // get the min and max x and y values
2693 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2695 // check clipping
2696 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2697 != splashClipAllOutside) {
2698 if (scanner->hasPartialClip()) {
2699 clipRes = splashClipPartial;
2702 origBlendFunc = state->blendFunc;
2703 state->blendFunc = &blendXor;
2704 pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse);
2706 // draw the spans
2707 for (y = yMinI; y <= yMaxI; ++y) {
2708 while (scanner->getNextSpan(y, &x0, &x1)) {
2709 if (clipRes == splashClipAllInside) {
2710 drawSpan(&pipe, x0, x1, y, gTrue);
2711 } else {
2712 // limit the x range
2713 if (x0 < state->clip->getXMinI()) {
2714 x0 = state->clip->getXMinI();
2716 if (x1 > state->clip->getXMaxI()) {
2717 x1 = state->clip->getXMaxI();
2719 clipRes2 = state->clip->testSpan(x0, x1, y);
2720 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2724 state->blendFunc = origBlendFunc;
2726 opClipRes = clipRes;
2728 delete scanner;
2729 delete xPath;
2730 return splashOk;
2733 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
2734 int c, SplashFont *font) {
2735 SplashGlyphBitmap glyph;
2736 SplashCoord xt, yt;
2737 int x0, y0, xFrac, yFrac;
2738 SplashClipResult clipRes;
2740 if (debugMode) {
2741 printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
2742 (double)x, (double)y, c, c, c);
2744 transform(state->matrix, x, y, &xt, &yt);
2745 x0 = splashFloor(xt);
2746 xFrac = splashFloor((xt - x0) * splashFontFraction);
2747 y0 = splashFloor(yt);
2748 yFrac = splashFloor((yt - y0) * splashFontFraction);
2749 if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
2750 return splashErrNoGlyph;
2752 if (clipRes != splashClipAllOutside) {
2753 fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
2755 opClipRes = clipRes;
2756 if (glyph.freeData) {
2757 gfree(glyph.data);
2759 return splashOk;
2762 void Splash::fillGlyph(SplashCoord x, SplashCoord y,
2763 SplashGlyphBitmap *glyph) {
2764 SplashCoord xt, yt;
2765 int x0, y0;
2767 transform(state->matrix, x, y, &xt, &yt);
2768 x0 = splashFloor(xt);
2769 y0 = splashFloor(yt);
2770 SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
2771 y0 - glyph->y,
2772 x0 - glyph->x + glyph->w - 1,
2773 y0 - glyph->y + glyph->h - 1);
2774 if (clipRes != splashClipAllOutside) {
2775 fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
2777 opClipRes = clipRes;
2780 void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
2781 SplashPipe pipe;
2782 int alpha0;
2783 Guchar alpha;
2784 Guchar *p;
2785 int x1, y1, xx, xx1, yy;
2787 p = glyph->data;
2788 int xStart = x0 - glyph->x;
2789 int yStart = y0 - glyph->y;
2790 int xxLimit = glyph->w;
2791 int yyLimit = glyph->h;
2792 int xShift = 0;
2794 if (yStart < 0)
2796 p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row
2797 yyLimit += yStart;
2798 yStart = 0;
2801 if (xStart < 0)
2803 if (glyph->aa) {
2804 p += -xStart;
2805 } else {
2806 p += (-xStart) / 8;
2807 xShift = (-xStart) % 8;
2809 xxLimit += xStart;
2810 xStart = 0;
2813 if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
2814 if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
2816 if (noClip) {
2817 if (glyph->aa) {
2818 pipeInit(&pipe, xStart, yStart,
2819 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2820 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2821 pipeSetXY(&pipe, xStart, y1);
2822 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2823 alpha = p[xx];
2824 if (alpha != 0) {
2825 pipe.shape = alpha;
2826 (this->*pipe.run)(&pipe);
2827 updateModX(x1);
2828 updateModY(y1);
2829 } else {
2830 pipeIncX(&pipe);
2833 p += glyph->w;
2835 } else {
2836 const int widthEight = splashCeil(glyph->w / 8.0);
2838 pipeInit(&pipe, xStart, yStart,
2839 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2840 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2841 pipeSetXY(&pipe, xStart, y1);
2842 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2843 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2844 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2845 if (alpha0 & 0x80) {
2846 (this->*pipe.run)(&pipe);
2847 updateModX(x1);
2848 updateModY(y1);
2849 } else {
2850 pipeIncX(&pipe);
2852 alpha0 <<= 1;
2855 p += widthEight;
2858 } else {
2859 if (glyph->aa) {
2860 pipeInit(&pipe, xStart, yStart,
2861 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2862 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2863 pipeSetXY(&pipe, xStart, y1);
2864 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2865 if (state->clip->test(x1, y1)) {
2866 alpha = p[xx];
2867 if (alpha != 0) {
2868 pipe.shape = alpha;
2869 (this->*pipe.run)(&pipe);
2870 updateModX(x1);
2871 updateModY(y1);
2872 } else {
2873 pipeIncX(&pipe);
2875 } else {
2876 pipeIncX(&pipe);
2879 p += glyph->w;
2881 } else {
2882 const int widthEight = splashCeil(glyph->w / 8.0);
2884 pipeInit(&pipe, xStart, yStart,
2885 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2886 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2887 pipeSetXY(&pipe, xStart, y1);
2888 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2889 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2890 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2891 if (state->clip->test(x1, y1)) {
2892 if (alpha0 & 0x80) {
2893 (this->*pipe.run)(&pipe);
2894 updateModX(x1);
2895 updateModY(y1);
2896 } else {
2897 pipeIncX(&pipe);
2899 } else {
2900 pipeIncX(&pipe);
2902 alpha0 <<= 1;
2905 p += widthEight;
2911 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
2912 int w, int h, SplashCoord *mat,
2913 GBool glyphMode) {
2914 SplashBitmap *scaledMask;
2915 SplashClipResult clipRes;
2916 GBool minorAxisZero;
2917 int x0, y0, x1, y1, scaledWidth, scaledHeight;
2918 int yp;
2920 if (debugMode) {
2921 printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2922 w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2923 (double)mat[3], (double)mat[4], (double)mat[5]);
2926 if (w == 0 && h == 0) return splashErrZeroImage;
2928 // check for singular matrix
2929 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
2930 return splashErrSingularMatrix;
2933 minorAxisZero = mat[1] == 0 && mat[2] == 0;
2935 // scaling only
2936 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
2937 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2938 y0 = imgCoordMungeLowerC(mat[5], glyphMode);
2939 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2940 y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
2941 // make sure narrow images cover at least one pixel
2942 if (x0 == x1) {
2943 ++x1;
2945 if (y0 == y1) {
2946 ++y1;
2948 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2949 opClipRes = clipRes;
2950 if (clipRes != splashClipAllOutside) {
2951 scaledWidth = x1 - x0;
2952 scaledHeight = y1 - y0;
2953 yp = h / scaledHeight;
2954 if (yp < 0 || yp > INT_MAX - 1) {
2955 return splashErrBadArg;
2957 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2958 blitMask(scaledMask, x0, y0, clipRes);
2959 delete scaledMask;
2962 // scaling plus vertical flip
2963 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
2964 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2965 y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
2966 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2967 y1 = imgCoordMungeUpperC(mat[5], glyphMode);
2968 // make sure narrow images cover at least one pixel
2969 if (x0 == x1) {
2970 ++x1;
2972 if (y0 == y1) {
2973 ++y1;
2975 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2976 opClipRes = clipRes;
2977 if (clipRes != splashClipAllOutside) {
2978 scaledWidth = x1 - x0;
2979 scaledHeight = y1 - y0;
2980 yp = h / scaledHeight;
2981 if (yp < 0 || yp > INT_MAX - 1) {
2982 return splashErrBadArg;
2984 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2985 vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
2986 blitMask(scaledMask, x0, y0, clipRes);
2987 delete scaledMask;
2990 // all other cases
2991 } else {
2992 arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
2995 return splashOk;
2998 void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
2999 int srcWidth, int srcHeight,
3000 SplashCoord *mat, GBool glyphMode) {
3001 SplashBitmap *scaledMask;
3002 SplashClipResult clipRes, clipRes2;
3003 SplashPipe pipe;
3004 int scaledWidth, scaledHeight, t0, t1;
3005 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3006 SplashCoord vx[4], vy[4];
3007 int xMin, yMin, xMax, yMax;
3008 ImageSection section[3];
3009 int nSections;
3010 int y, xa, xb, x, i, xx, yy;
3012 // compute the four vertices of the target quadrilateral
3013 vx[0] = mat[4]; vy[0] = mat[5];
3014 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3015 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3016 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3018 // clipping
3019 xMin = imgCoordMungeLowerC(vx[0], glyphMode);
3020 xMax = imgCoordMungeUpperC(vx[0], glyphMode);
3021 yMin = imgCoordMungeLowerC(vy[0], glyphMode);
3022 yMax = imgCoordMungeUpperC(vy[0], glyphMode);
3023 for (i = 1; i < 4; ++i) {
3024 t0 = imgCoordMungeLowerC(vx[i], glyphMode);
3025 if (t0 < xMin) {
3026 xMin = t0;
3028 t0 = imgCoordMungeUpperC(vx[i], glyphMode);
3029 if (t0 > xMax) {
3030 xMax = t0;
3032 t1 = imgCoordMungeLowerC(vy[i], glyphMode);
3033 if (t1 < yMin) {
3034 yMin = t1;
3036 t1 = imgCoordMungeUpperC(vy[i], glyphMode);
3037 if (t1 > yMax) {
3038 yMax = t1;
3041 clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
3042 opClipRes = clipRes;
3043 if (clipRes == splashClipAllOutside) {
3044 return;
3047 // compute the scale factors
3048 if (mat[0] >= 0) {
3049 t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) -
3050 imgCoordMungeLowerC(mat[4], glyphMode);
3051 } else {
3052 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3053 imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
3055 if (mat[1] >= 0) {
3056 t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) -
3057 imgCoordMungeLowerC(mat[5], glyphMode);
3058 } else {
3059 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3060 imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
3062 scaledWidth = t0 > t1 ? t0 : t1;
3063 if (mat[2] >= 0) {
3064 t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) -
3065 imgCoordMungeLowerC(mat[4], glyphMode);
3066 } else {
3067 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3068 imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
3070 if (mat[3] >= 0) {
3071 t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) -
3072 imgCoordMungeLowerC(mat[5], glyphMode);
3073 } else {
3074 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3075 imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
3077 scaledHeight = t0 > t1 ? t0 : t1;
3078 if (scaledWidth == 0) {
3079 scaledWidth = 1;
3081 if (scaledHeight == 0) {
3082 scaledHeight = 1;
3085 // compute the inverse transform (after scaling) matrix
3086 r00 = mat[0] / scaledWidth;
3087 r01 = mat[1] / scaledWidth;
3088 r10 = mat[2] / scaledHeight;
3089 r11 = mat[3] / scaledHeight;
3090 det = r00 * r11 - r01 * r10;
3091 if (splashAbs(det) < 1e-6) {
3092 // this should be caught by the singular matrix check in fillImageMask
3093 return;
3095 ir00 = r11 / det;
3096 ir01 = -r01 / det;
3097 ir10 = -r10 / det;
3098 ir11 = r00 / det;
3100 // scale the input image
3101 scaledMask = scaleMask(src, srcData, srcWidth, srcHeight,
3102 scaledWidth, scaledHeight);
3103 if (scaledMask->data == NULL) {
3104 error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask");
3105 delete scaledMask;
3106 return;
3109 // construct the three sections
3110 i = (vy[2] <= vy[3]) ? 2 : 3;
3111 if (vy[1] <= vy[i]) {
3112 i = 1;
3114 if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
3115 i = 0;
3117 if (vy[i] == vy[(i+1) & 3]) {
3118 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3119 section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3120 if (vx[i] < vx[(i+1) & 3]) {
3121 section[0].ia0 = i;
3122 section[0].ia1 = (i+3) & 3;
3123 section[0].ib0 = (i+1) & 3;
3124 section[0].ib1 = (i+2) & 3;
3125 } else {
3126 section[0].ia0 = (i+1) & 3;
3127 section[0].ia1 = (i+2) & 3;
3128 section[0].ib0 = i;
3129 section[0].ib1 = (i+3) & 3;
3131 nSections = 1;
3132 } else {
3133 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3134 section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3135 section[0].ia0 = section[0].ib0 = i;
3136 section[2].ia1 = section[2].ib1 = (i+2) & 3;
3137 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3138 section[0].ia1 = section[2].ia0 = (i+1) & 3;
3139 section[0].ib1 = section[2].ib0 = (i+3) & 3;
3140 } else {
3141 section[0].ia1 = section[2].ia0 = (i+3) & 3;
3142 section[0].ib1 = section[2].ib0 = (i+1) & 3;
3144 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
3145 section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode);
3146 section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode);
3147 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3148 section[1].ia0 = (i+1) & 3;
3149 section[1].ia1 = (i+2) & 3;
3150 section[1].ib0 = i;
3151 section[1].ib1 = (i+3) & 3;
3152 } else {
3153 section[1].ia0 = i;
3154 section[1].ia1 = (i+3) & 3;
3155 section[1].ib0 = (i+1) & 3;
3156 section[1].ib1 = (i+2) & 3;
3158 } else {
3159 section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode);
3160 section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode);
3161 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3162 section[1].ia0 = i;
3163 section[1].ia1 = (i+1) & 3;
3164 section[1].ib0 = (i+3) & 3;
3165 section[1].ib1 = (i+2) & 3;
3166 } else {
3167 section[1].ia0 = (i+3) & 3;
3168 section[1].ia1 = (i+2) & 3;
3169 section[1].ib0 = i;
3170 section[1].ib1 = (i+1) & 3;
3173 section[0].y1 = section[1].y0 - 1;
3174 section[1].y1 = section[2].y0 - 1;
3175 nSections = 3;
3177 for (i = 0; i < nSections; ++i) {
3178 section[i].xa0 = vx[section[i].ia0];
3179 section[i].ya0 = vy[section[i].ia0];
3180 section[i].xa1 = vx[section[i].ia1];
3181 section[i].ya1 = vy[section[i].ia1];
3182 section[i].xb0 = vx[section[i].ib0];
3183 section[i].yb0 = vy[section[i].ib0];
3184 section[i].xb1 = vx[section[i].ib1];
3185 section[i].yb1 = vy[section[i].ib1];
3186 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
3187 (section[i].ya1 - section[i].ya0);
3188 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
3189 (section[i].yb1 - section[i].yb0);
3192 // initialize the pixel pipe
3193 pipeInit(&pipe, 0, 0, state->fillPattern, NULL,
3194 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3195 if (vectorAntialias) {
3196 drawAAPixelInit();
3199 // make sure narrow images cover at least one pixel
3200 if (nSections == 1) {
3201 if (section[0].y0 == section[0].y1) {
3202 ++section[0].y1;
3203 clipRes = opClipRes = splashClipPartial;
3205 } else {
3206 if (section[0].y0 == section[2].y1) {
3207 ++section[1].y1;
3208 clipRes = opClipRes = splashClipPartial;
3212 // scan all pixels inside the target region
3213 for (i = 0; i < nSections; ++i) {
3214 for (y = section[i].y0; y <= section[i].y1; ++y) {
3215 xa = imgCoordMungeLowerC(section[i].xa0 +
3216 ((SplashCoord)y + 0.5 - section[i].ya0) *
3217 section[i].dxdya,
3218 glyphMode);
3219 xb = imgCoordMungeUpperC(section[i].xb0 +
3220 ((SplashCoord)y + 0.5 - section[i].yb0) *
3221 section[i].dxdyb,
3222 glyphMode);
3223 if (unlikely(xa < 0))
3224 xa = 0;
3225 // make sure narrow images cover at least one pixel
3226 if (xa == xb) {
3227 ++xb;
3229 if (clipRes != splashClipAllInside) {
3230 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
3231 } else {
3232 clipRes2 = clipRes;
3234 for (x = xa; x < xb; ++x) {
3235 // map (x+0.5, y+0.5) back to the scaled image
3236 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
3237 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3238 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
3239 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3240 // xx should always be within bounds, but floating point
3241 // inaccuracy can cause problems
3242 if (xx < 0) {
3243 xx = 0;
3244 } else if (xx >= scaledWidth) {
3245 xx = scaledWidth - 1;
3247 if (yy < 0) {
3248 yy = 0;
3249 } else if (yy >= scaledHeight) {
3250 yy = scaledHeight - 1;
3252 pipe.shape = scaledMask->data[yy * scaledWidth + xx];
3253 if (vectorAntialias && clipRes2 != splashClipAllInside) {
3254 drawAAPixel(&pipe, x, y);
3255 } else {
3256 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
3262 delete scaledMask;
3265 // Scale an image mask into a SplashBitmap.
3266 SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
3267 int srcWidth, int srcHeight,
3268 int scaledWidth, int scaledHeight) {
3269 SplashBitmap *dest;
3271 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8,
3272 gFalse);
3273 if (scaledHeight < srcHeight) {
3274 if (scaledWidth < srcWidth) {
3275 scaleMaskYdXd(src, srcData, srcWidth, srcHeight,
3276 scaledWidth, scaledHeight, dest);
3277 } else {
3278 scaleMaskYdXu(src, srcData, srcWidth, srcHeight,
3279 scaledWidth, scaledHeight, dest);
3281 } else {
3282 if (scaledWidth < srcWidth) {
3283 scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
3284 scaledWidth, scaledHeight, dest);
3285 } else {
3286 scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
3287 scaledWidth, scaledHeight, dest);
3290 return dest;
3293 void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
3294 int srcWidth, int srcHeight,
3295 int scaledWidth, int scaledHeight,
3296 SplashBitmap *dest) {
3297 Guchar *lineBuf;
3298 Guint *pixBuf;
3299 Guint pix;
3300 Guchar *destPtr;
3301 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3302 int i, j;
3304 // Bresenham parameters for y scale
3305 yp = srcHeight / scaledHeight;
3306 yq = srcHeight % scaledHeight;
3308 // Bresenham parameters for x scale
3309 xp = srcWidth / scaledWidth;
3310 xq = srcWidth % scaledWidth;
3312 // allocate buffers
3313 lineBuf = (Guchar *)gmalloc(srcWidth);
3314 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3316 // init y scale Bresenham
3317 yt = 0;
3319 destPtr = dest->data;
3320 for (y = 0; y < scaledHeight; ++y) {
3322 // y scale Bresenham
3323 if ((yt += yq) >= scaledHeight) {
3324 yt -= scaledHeight;
3325 yStep = yp + 1;
3326 } else {
3327 yStep = yp;
3330 // read rows from image
3331 memset(pixBuf, 0, srcWidth * sizeof(int));
3332 for (i = 0; i < yStep; ++i) {
3333 (*src)(srcData, lineBuf);
3334 for (j = 0; j < srcWidth; ++j) {
3335 pixBuf[j] += lineBuf[j];
3339 // init x scale Bresenham
3340 xt = 0;
3341 d0 = (255 << 23) / (yStep * xp);
3342 d1 = (255 << 23) / (yStep * (xp + 1));
3344 xx = 0;
3345 for (x = 0; x < scaledWidth; ++x) {
3347 // x scale Bresenham
3348 if ((xt += xq) >= scaledWidth) {
3349 xt -= scaledWidth;
3350 xStep = xp + 1;
3351 d = d1;
3352 } else {
3353 xStep = xp;
3354 d = d0;
3357 // compute the final pixel
3358 pix = 0;
3359 for (i = 0; i < xStep; ++i) {
3360 pix += pixBuf[xx++];
3362 // (255 * pix) / xStep * yStep
3363 pix = (pix * d) >> 23;
3365 // store the pixel
3366 *destPtr++ = (Guchar)pix;
3370 gfree(pixBuf);
3371 gfree(lineBuf);
3374 void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
3375 int srcWidth, int srcHeight,
3376 int scaledWidth, int scaledHeight,
3377 SplashBitmap *dest) {
3378 Guchar *lineBuf;
3379 Guint *pixBuf;
3380 Guint pix;
3381 Guchar *destPtr;
3382 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
3383 int i, j;
3385 destPtr = dest->data;
3386 if (destPtr == NULL) {
3387 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdXu");
3388 return;
3391 // Bresenham parameters for y scale
3392 yp = srcHeight / scaledHeight;
3393 yq = srcHeight % scaledHeight;
3395 // Bresenham parameters for x scale
3396 xp = scaledWidth / srcWidth;
3397 xq = scaledWidth % srcWidth;
3399 // allocate buffers
3400 lineBuf = (Guchar *)gmalloc(srcWidth);
3401 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3403 // init y scale Bresenham
3404 yt = 0;
3406 for (y = 0; y < scaledHeight; ++y) {
3408 // y scale Bresenham
3409 if ((yt += yq) >= scaledHeight) {
3410 yt -= scaledHeight;
3411 yStep = yp + 1;
3412 } else {
3413 yStep = yp;
3416 // read rows from image
3417 memset(pixBuf, 0, srcWidth * sizeof(int));
3418 for (i = 0; i < yStep; ++i) {
3419 (*src)(srcData, lineBuf);
3420 for (j = 0; j < srcWidth; ++j) {
3421 pixBuf[j] += lineBuf[j];
3425 // init x scale Bresenham
3426 xt = 0;
3427 d = (255 << 23) / yStep;
3429 for (x = 0; x < srcWidth; ++x) {
3431 // x scale Bresenham
3432 if ((xt += xq) >= srcWidth) {
3433 xt -= srcWidth;
3434 xStep = xp + 1;
3435 } else {
3436 xStep = xp;
3439 // compute the final pixel
3440 pix = pixBuf[x];
3441 // (255 * pix) / yStep
3442 pix = (pix * d) >> 23;
3444 // store the pixel
3445 for (i = 0; i < xStep; ++i) {
3446 *destPtr++ = (Guchar)pix;
3451 gfree(pixBuf);
3452 gfree(lineBuf);
3455 void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
3456 int srcWidth, int srcHeight,
3457 int scaledWidth, int scaledHeight,
3458 SplashBitmap *dest) {
3459 Guchar *lineBuf;
3460 Guint pix;
3461 Guchar *destPtr0, *destPtr;
3462 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3463 int i;
3465 destPtr0 = dest->data;
3466 if (destPtr0 == NULL) {
3467 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXd");
3468 return;
3471 // Bresenham parameters for y scale
3472 yp = scaledHeight / srcHeight;
3473 yq = scaledHeight % srcHeight;
3475 // Bresenham parameters for x scale
3476 xp = srcWidth / scaledWidth;
3477 xq = srcWidth % scaledWidth;
3479 // allocate buffers
3480 lineBuf = (Guchar *)gmalloc(srcWidth);
3482 // init y scale Bresenham
3483 yt = 0;
3485 for (y = 0; y < srcHeight; ++y) {
3487 // y scale Bresenham
3488 if ((yt += yq) >= srcHeight) {
3489 yt -= srcHeight;
3490 yStep = yp + 1;
3491 } else {
3492 yStep = yp;
3495 // read row from image
3496 (*src)(srcData, lineBuf);
3498 // init x scale Bresenham
3499 xt = 0;
3500 d0 = (255 << 23) / xp;
3501 d1 = (255 << 23) / (xp + 1);
3503 xx = 0;
3504 for (x = 0; x < scaledWidth; ++x) {
3506 // x scale Bresenham
3507 if ((xt += xq) >= scaledWidth) {
3508 xt -= scaledWidth;
3509 xStep = xp + 1;
3510 d = d1;
3511 } else {
3512 xStep = xp;
3513 d = d0;
3516 // compute the final pixel
3517 pix = 0;
3518 for (i = 0; i < xStep; ++i) {
3519 pix += lineBuf[xx++];
3521 // (255 * pix) / xStep
3522 pix = (pix * d) >> 23;
3524 // store the pixel
3525 for (i = 0; i < yStep; ++i) {
3526 destPtr = destPtr0 + i * scaledWidth + x;
3527 *destPtr = (Guchar)pix;
3531 destPtr0 += yStep * scaledWidth;
3534 gfree(lineBuf);
3537 void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
3538 int srcWidth, int srcHeight,
3539 int scaledWidth, int scaledHeight,
3540 SplashBitmap *dest) {
3541 Guchar *lineBuf;
3542 Guint pix;
3543 Guchar *destPtr0, *destPtr;
3544 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
3545 int i, j;
3547 destPtr0 = dest->data;
3548 if (destPtr0 == NULL) {
3549 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXu");
3550 return;
3553 // Bresenham parameters for y scale
3554 yp = scaledHeight / srcHeight;
3555 yq = scaledHeight % srcHeight;
3557 // Bresenham parameters for x scale
3558 xp = scaledWidth / srcWidth;
3559 xq = scaledWidth % srcWidth;
3561 // allocate buffers
3562 lineBuf = (Guchar *)gmalloc(srcWidth);
3564 // init y scale Bresenham
3565 yt = 0;
3567 for (y = 0; y < srcHeight; ++y) {
3569 // y scale Bresenham
3570 if ((yt += yq) >= srcHeight) {
3571 yt -= srcHeight;
3572 yStep = yp + 1;
3573 } else {
3574 yStep = yp;
3577 // read row from image
3578 (*src)(srcData, lineBuf);
3580 // init x scale Bresenham
3581 xt = 0;
3583 xx = 0;
3584 for (x = 0; x < srcWidth; ++x) {
3586 // x scale Bresenham
3587 if ((xt += xq) >= srcWidth) {
3588 xt -= srcWidth;
3589 xStep = xp + 1;
3590 } else {
3591 xStep = xp;
3594 // compute the final pixel
3595 pix = lineBuf[x] ? 255 : 0;
3597 // store the pixel
3598 for (i = 0; i < yStep; ++i) {
3599 for (j = 0; j < xStep; ++j) {
3600 destPtr = destPtr0 + i * scaledWidth + xx + j;
3601 *destPtr++ = (Guchar)pix;
3605 xx += xStep;
3608 destPtr0 += yStep * scaledWidth;
3611 gfree(lineBuf);
3614 void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
3615 SplashClipResult clipRes) {
3616 SplashPipe pipe;
3617 Guchar *p;
3618 int w, h, x, y;
3620 w = src->getWidth();
3621 h = src->getHeight();
3622 p = src->getDataPtr();
3623 if (p == NULL) {
3624 error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask");
3625 return;
3627 if (vectorAntialias && clipRes != splashClipAllInside) {
3628 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3629 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3630 drawAAPixelInit();
3631 for (y = 0; y < h; ++y) {
3632 for (x = 0; x < w; ++x) {
3633 pipe.shape = *p++;
3634 drawAAPixel(&pipe, xDest + x, yDest + y);
3637 } else {
3638 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3639 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3640 if (clipRes == splashClipAllInside) {
3641 for (y = 0; y < h; ++y) {
3642 pipeSetXY(&pipe, xDest, yDest + y);
3643 for (x = 0; x < w; ++x) {
3644 if (*p) {
3645 pipe.shape = *p;
3646 (this->*pipe.run)(&pipe);
3647 } else {
3648 pipeIncX(&pipe);
3650 ++p;
3653 updateModX(xDest);
3654 updateModX(xDest + w - 1);
3655 updateModY(yDest);
3656 updateModY(yDest + h - 1);
3657 } else {
3658 for (y = 0; y < h; ++y) {
3659 pipeSetXY(&pipe, xDest, yDest + y);
3660 for (x = 0; x < w; ++x) {
3661 if (*p && state->clip->test(xDest + x, yDest + y)) {
3662 pipe.shape = *p;
3663 (this->*pipe.run)(&pipe);
3664 updateModX(xDest + x);
3665 updateModY(yDest + y);
3666 } else {
3667 pipeIncX(&pipe);
3669 ++p;
3676 SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3677 SplashColorMode srcMode, GBool srcAlpha,
3678 int w, int h, SplashCoord *mat, GBool interpolate,
3679 GBool tilingPattern) {
3680 GBool ok;
3681 SplashBitmap *scaledImg;
3682 SplashClipResult clipRes;
3683 GBool minorAxisZero;
3684 int x0, y0, x1, y1, scaledWidth, scaledHeight;
3685 int nComps;
3686 int yp;
3688 if (debugMode) {
3689 printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
3690 srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
3691 (double)mat[3], (double)mat[4], (double)mat[5]);
3694 // check color modes
3695 ok = gFalse; // make gcc happy
3696 nComps = 0; // make gcc happy
3697 switch (bitmap->mode) {
3698 case splashModeMono1:
3699 case splashModeMono8:
3700 ok = srcMode == splashModeMono8;
3701 nComps = 1;
3702 break;
3703 case splashModeRGB8:
3704 ok = srcMode == splashModeRGB8;
3705 nComps = 3;
3706 break;
3707 case splashModeXBGR8:
3708 ok = srcMode == splashModeXBGR8;
3709 nComps = 4;
3710 break;
3711 case splashModeBGR8:
3712 ok = srcMode == splashModeBGR8;
3713 nComps = 3;
3714 break;
3715 #if SPLASH_CMYK
3716 case splashModeCMYK8:
3717 ok = srcMode == splashModeCMYK8;
3718 nComps = 4;
3719 break;
3720 case splashModeDeviceN8:
3721 ok = srcMode == splashModeDeviceN8;
3722 nComps = SPOT_NCOMPS+4;
3723 break;
3724 #endif
3725 default:
3726 ok = gFalse;
3727 break;
3729 if (!ok) {
3730 return splashErrModeMismatch;
3733 // check for singular matrix
3734 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
3735 return splashErrSingularMatrix;
3738 minorAxisZero = mat[1] == 0 && mat[2] == 0;
3740 // scaling only
3741 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
3742 x0 = imgCoordMungeLower(mat[4]);
3743 y0 = imgCoordMungeLower(mat[5]);
3744 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3745 y1 = imgCoordMungeUpper(mat[3] + mat[5]);
3746 // make sure narrow images cover at least one pixel
3747 if (x0 == x1) {
3748 ++x1;
3750 if (y0 == y1) {
3751 ++y1;
3753 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3754 opClipRes = clipRes;
3755 if (clipRes != splashClipAllOutside) {
3756 scaledWidth = x1 - x0;
3757 scaledHeight = y1 - y0;
3758 yp = h / scaledHeight;
3759 if (yp < 0 || yp > INT_MAX - 1) {
3760 return splashErrBadArg;
3762 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3763 scaledWidth, scaledHeight, interpolate, tilingPattern);
3764 if (scaledImg == NULL) {
3765 return splashErrBadArg;
3767 if (tf != NULL) {
3768 (*tf)(srcData, scaledImg);
3770 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3771 delete scaledImg;
3774 // scaling plus vertical flip
3775 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
3776 x0 = imgCoordMungeLower(mat[4]);
3777 y0 = imgCoordMungeLower(mat[3] + mat[5]);
3778 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3779 y1 = imgCoordMungeUpper(mat[5]);
3780 if (x0 == x1) {
3781 if (mat[4] + mat[0] * 0.5 < x0) {
3782 --x0;
3783 } else {
3784 ++x1;
3787 if (y0 == y1) {
3788 if (mat[5] + mat[1] * 0.5 < y0) {
3789 --y0;
3790 } else {
3791 ++y1;
3794 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3795 opClipRes = clipRes;
3796 if (clipRes != splashClipAllOutside) {
3797 scaledWidth = x1 - x0;
3798 scaledHeight = y1 - y0;
3799 yp = h / scaledHeight;
3800 if (yp < 0 || yp > INT_MAX - 1) {
3801 return splashErrBadArg;
3803 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3804 scaledWidth, scaledHeight, interpolate, tilingPattern);
3805 if (scaledImg == NULL) {
3806 return splashErrBadArg;
3808 if (tf != NULL) {
3809 (*tf)(srcData, scaledImg);
3811 vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
3812 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3813 delete scaledImg;
3816 // all other cases
3817 } else {
3818 return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha,
3819 w, h, mat, interpolate, tilingPattern);
3822 return splashOk;
3825 SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3826 SplashColorMode srcMode, int nComps,
3827 GBool srcAlpha,
3828 int srcWidth, int srcHeight,
3829 SplashCoord *mat, GBool interpolate,
3830 GBool tilingPattern) {
3831 SplashBitmap *scaledImg;
3832 SplashClipResult clipRes, clipRes2;
3833 SplashPipe pipe;
3834 SplashColor pixel;
3835 int scaledWidth, scaledHeight, t0, t1, th;
3836 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3837 SplashCoord vx[4], vy[4];
3838 int xMin, yMin, xMax, yMax;
3839 ImageSection section[3];
3840 int nSections;
3841 int y, xa, xb, x, i, xx, yy, yp;
3843 // compute the four vertices of the target quadrilateral
3844 vx[0] = mat[4]; vy[0] = mat[5];
3845 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3846 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3847 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3849 // clipping
3850 xMin = imgCoordMungeLower(vx[0]);
3851 xMax = imgCoordMungeUpper(vx[0]);
3852 yMin = imgCoordMungeLower(vy[0]);
3853 yMax = imgCoordMungeUpper(vy[0]);
3854 for (i = 1; i < 4; ++i) {
3855 t0 = imgCoordMungeLower(vx[i]);
3856 if (t0 < xMin) {
3857 xMin = t0;
3859 t0 = imgCoordMungeUpper(vx[i]);
3860 if (t0 > xMax) {
3861 xMax = t0;
3863 t1 = imgCoordMungeLower(vy[i]);
3864 if (t1 < yMin) {
3865 yMin = t1;
3867 t1 = imgCoordMungeUpper(vy[i]);
3868 if (t1 > yMax) {
3869 yMax = t1;
3872 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
3873 opClipRes = clipRes;
3874 if (clipRes == splashClipAllOutside) {
3875 return splashOk;
3878 // compute the scale factors
3879 if (splashAbs(mat[0]) >= splashAbs(mat[1])) {
3880 scaledWidth = xMax - xMin;
3881 scaledHeight = yMax - yMin;
3882 } else {
3883 scaledWidth = yMax - yMin;
3884 scaledHeight = xMax - xMin;
3886 if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) {
3887 if (mat[0] >= 0) {
3888 t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
3889 } else {
3890 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
3892 if (mat[1] >= 0) {
3893 t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
3894 } else {
3895 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
3897 scaledWidth = t0 > t1 ? t0 : t1;
3898 if (mat[2] >= 0) {
3899 t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
3900 if (splashAbs(mat[1]) >= 1) {
3901 th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]);
3902 if (th > t0) t0 = th;
3904 } else {
3905 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
3906 if (splashAbs(mat[1]) >= 1) {
3907 th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]);
3908 if (th > t0) t0 = th;
3911 if (mat[3] >= 0) {
3912 t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
3913 if (splashAbs(mat[0]) >= 1) {
3914 th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]);
3915 if (th > t1) t1 = th;
3917 } else {
3918 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
3919 if (splashAbs(mat[0]) >= 1) {
3920 th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]);
3921 if (th > t1) t1 = th;
3924 scaledHeight = t0 > t1 ? t0 : t1;
3926 if (scaledWidth == 0) {
3927 scaledWidth = 1;
3929 if (scaledHeight == 0) {
3930 scaledHeight = 1;
3933 // compute the inverse transform (after scaling) matrix
3934 r00 = mat[0] / scaledWidth;
3935 r01 = mat[1] / scaledWidth;
3936 r10 = mat[2] / scaledHeight;
3937 r11 = mat[3] / scaledHeight;
3938 det = r00 * r11 - r01 * r10;
3939 if (splashAbs(det) < 1e-6) {
3940 // this should be caught by the singular matrix check in drawImage
3941 return splashErrBadArg;
3943 ir00 = r11 / det;
3944 ir01 = -r01 / det;
3945 ir10 = -r10 / det;
3946 ir11 = r00 / det;
3948 // scale the input image
3949 yp = srcHeight / scaledHeight;
3950 if (yp < 0 || yp > INT_MAX - 1) {
3951 return splashErrBadArg;
3953 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
3954 srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
3956 if (scaledImg == NULL) {
3957 return splashErrBadArg;
3960 if (tf != NULL) {
3961 (*tf)(srcData, scaledImg);
3963 // construct the three sections
3964 i = 0;
3965 if (vy[1] < vy[i]) {
3966 i = 1;
3968 if (vy[2] < vy[i]) {
3969 i = 2;
3971 if (vy[3] < vy[i]) {
3972 i = 3;
3974 // NB: if using fixed point, 0.000001 will be truncated to zero,
3975 // so these two comparisons must be <=, not <
3976 if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
3977 vy[(i-1) & 3] < vy[(i+1) & 3]) {
3978 i = (i-1) & 3;
3980 if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
3981 section[0].y0 = imgCoordMungeLower(vy[i]);
3982 section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
3983 if (vx[i] < vx[(i+1) & 3]) {
3984 section[0].ia0 = i;
3985 section[0].ia1 = (i+3) & 3;
3986 section[0].ib0 = (i+1) & 3;
3987 section[0].ib1 = (i+2) & 3;
3988 } else {
3989 section[0].ia0 = (i+1) & 3;
3990 section[0].ia1 = (i+2) & 3;
3991 section[0].ib0 = i;
3992 section[0].ib1 = (i+3) & 3;
3994 nSections = 1;
3995 } else {
3996 section[0].y0 = imgCoordMungeLower(vy[i]);
3997 section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
3998 section[0].ia0 = section[0].ib0 = i;
3999 section[2].ia1 = section[2].ib1 = (i+2) & 3;
4000 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4001 section[0].ia1 = section[2].ia0 = (i+1) & 3;
4002 section[0].ib1 = section[2].ib0 = (i+3) & 3;
4003 } else {
4004 section[0].ia1 = section[2].ia0 = (i+3) & 3;
4005 section[0].ib1 = section[2].ib0 = (i+1) & 3;
4007 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
4008 section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]);
4009 section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]);
4010 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4011 section[1].ia0 = (i+1) & 3;
4012 section[1].ia1 = (i+2) & 3;
4013 section[1].ib0 = i;
4014 section[1].ib1 = (i+3) & 3;
4015 } else {
4016 section[1].ia0 = i;
4017 section[1].ia1 = (i+3) & 3;
4018 section[1].ib0 = (i+1) & 3;
4019 section[1].ib1 = (i+2) & 3;
4021 } else {
4022 section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]);
4023 section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]);
4024 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4025 section[1].ia0 = i;
4026 section[1].ia1 = (i+1) & 3;
4027 section[1].ib0 = (i+3) & 3;
4028 section[1].ib1 = (i+2) & 3;
4029 } else {
4030 section[1].ia0 = (i+3) & 3;
4031 section[1].ia1 = (i+2) & 3;
4032 section[1].ib0 = i;
4033 section[1].ib1 = (i+1) & 3;
4036 section[0].y1 = section[1].y0 - 1;
4037 section[1].y1 = section[2].y0 - 1;
4038 nSections = 3;
4040 for (i = 0; i < nSections; ++i) {
4041 section[i].xa0 = vx[section[i].ia0];
4042 section[i].ya0 = vy[section[i].ia0];
4043 section[i].xa1 = vx[section[i].ia1];
4044 section[i].ya1 = vy[section[i].ia1];
4045 section[i].xb0 = vx[section[i].ib0];
4046 section[i].yb0 = vy[section[i].ib0];
4047 section[i].xb1 = vx[section[i].ib1];
4048 section[i].yb1 = vy[section[i].ib1];
4049 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
4050 (section[i].ya1 - section[i].ya0);
4051 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
4052 (section[i].yb1 - section[i].yb0);
4055 // initialize the pixel pipe
4056 pipeInit(&pipe, 0, 0, NULL, pixel,
4057 (Guchar)splashRound(state->fillAlpha * 255),
4058 srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
4059 gFalse);
4060 if (vectorAntialias) {
4061 drawAAPixelInit();
4064 // make sure narrow images cover at least one pixel
4065 if (nSections == 1) {
4066 if (section[0].y0 == section[0].y1) {
4067 ++section[0].y1;
4068 clipRes = opClipRes = splashClipPartial;
4070 } else {
4071 if (section[0].y0 == section[2].y1) {
4072 ++section[1].y1;
4073 clipRes = opClipRes = splashClipPartial;
4077 // scan all pixels inside the target region
4078 for (i = 0; i < nSections; ++i) {
4079 for (y = section[i].y0; y <= section[i].y1; ++y) {
4080 xa = imgCoordMungeLower(section[i].xa0 +
4081 ((SplashCoord)y + 0.5 - section[i].ya0) *
4082 section[i].dxdya);
4083 if (unlikely(xa < 0))
4084 xa = 0;
4085 xb = imgCoordMungeUpper(section[i].xb0 +
4086 ((SplashCoord)y + 0.5 - section[i].yb0) *
4087 section[i].dxdyb);
4088 // make sure narrow images cover at least one pixel
4089 if (xa == xb) {
4090 ++xb;
4092 if (clipRes != splashClipAllInside) {
4093 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
4094 } else {
4095 clipRes2 = clipRes;
4097 for (x = xa; x < xb; ++x) {
4098 // map (x+0.5, y+0.5) back to the scaled image
4099 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
4100 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
4101 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
4102 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
4103 // xx should always be within bounds, but floating point
4104 // inaccuracy can cause problems
4105 if (xx < 0) {
4106 xx = 0;
4107 } else if (xx >= scaledWidth) {
4108 xx = scaledWidth - 1;
4110 if (yy < 0) {
4111 yy = 0;
4112 } else if (yy >= scaledHeight) {
4113 yy = scaledHeight - 1;
4115 scaledImg->getPixel(xx, yy, pixel);
4116 if (srcAlpha) {
4117 pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
4118 } else {
4119 pipe.shape = 255;
4121 if (vectorAntialias && clipRes2 != splashClipAllInside) {
4122 drawAAPixel(&pipe, x, y);
4123 } else {
4124 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
4130 delete scaledImg;
4131 return splashOk;
4134 // determine if a scaled image requires interpolation based on the scale and
4135 // the interpolate flag from the image dictionary
4136 static GBool isImageInterpolationRequired(int srcWidth, int srcHeight,
4137 int scaledWidth, int scaledHeight,
4138 GBool interpolate) {
4139 if (interpolate)
4140 return gTrue;
4142 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
4143 if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4)
4144 return gFalse;
4146 return gTrue;
4149 // Scale an image into a SplashBitmap.
4150 SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
4151 SplashColorMode srcMode, int nComps,
4152 GBool srcAlpha, int srcWidth, int srcHeight,
4153 int scaledWidth, int scaledHeight, GBool interpolate, GBool tilingPattern) {
4154 SplashBitmap *dest;
4156 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList());
4157 if (dest->getDataPtr() != NULL) {
4158 if (scaledHeight < srcHeight) {
4159 if (scaledWidth < srcWidth) {
4160 scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha,
4161 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4162 } else {
4163 scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha,
4164 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4166 } else {
4167 if (scaledWidth < srcWidth) {
4168 scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
4169 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4170 } else {
4171 if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
4172 scaleImageYuXuBilinear(src, srcData, srcMode, nComps, srcAlpha,
4173 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4174 } else {
4175 scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
4176 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4180 } else {
4181 delete dest;
4182 dest = NULL;
4184 return dest;
4187 void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
4188 SplashColorMode srcMode, int nComps,
4189 GBool srcAlpha, int srcWidth, int srcHeight,
4190 int scaledWidth, int scaledHeight,
4191 SplashBitmap *dest) {
4192 Guchar *lineBuf, *alphaLineBuf;
4193 Guint *pixBuf, *alphaPixBuf;
4194 Guint pix0, pix1, pix2;
4195 #if SPLASH_CMYK
4196 Guint pix3;
4197 Guint pix[SPOT_NCOMPS+4], cp;
4198 #endif
4199 Guint alpha;
4200 Guchar *destPtr, *destAlphaPtr;
4201 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4202 int i, j;
4204 // Bresenham parameters for y scale
4205 yp = srcHeight / scaledHeight;
4206 yq = srcHeight % scaledHeight;
4208 // Bresenham parameters for x scale
4209 xp = srcWidth / scaledWidth;
4210 xq = srcWidth % scaledWidth;
4212 // allocate buffers
4213 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4214 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4215 if (srcAlpha) {
4216 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4217 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4218 } else {
4219 alphaLineBuf = NULL;
4220 alphaPixBuf = NULL;
4223 // init y scale Bresenham
4224 yt = 0;
4226 destPtr = dest->data;
4227 destAlphaPtr = dest->alpha;
4228 for (y = 0; y < scaledHeight; ++y) {
4230 // y scale Bresenham
4231 if ((yt += yq) >= scaledHeight) {
4232 yt -= scaledHeight;
4233 yStep = yp + 1;
4234 } else {
4235 yStep = yp;
4238 // read rows from image
4239 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4240 if (srcAlpha) {
4241 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4243 for (i = 0; i < yStep; ++i) {
4244 (*src)(srcData, lineBuf, alphaLineBuf);
4245 for (j = 0; j < srcWidth * nComps; ++j) {
4246 pixBuf[j] += lineBuf[j];
4248 if (srcAlpha) {
4249 for (j = 0; j < srcWidth; ++j) {
4250 alphaPixBuf[j] += alphaLineBuf[j];
4255 // init x scale Bresenham
4256 xt = 0;
4257 d0 = (1 << 23) / (yStep * xp);
4258 d1 = (1 << 23) / (yStep * (xp + 1));
4260 xx = xxa = 0;
4261 for (x = 0; x < scaledWidth; ++x) {
4263 // x scale Bresenham
4264 if ((xt += xq) >= scaledWidth) {
4265 xt -= scaledWidth;
4266 xStep = xp + 1;
4267 d = d1;
4268 } else {
4269 xStep = xp;
4270 d = d0;
4273 switch (srcMode) {
4275 case splashModeMono8:
4277 // compute the final pixel
4278 pix0 = 0;
4279 for (i = 0; i < xStep; ++i) {
4280 pix0 += pixBuf[xx++];
4282 // pix / xStep * yStep
4283 pix0 = (pix0 * d) >> 23;
4285 // store the pixel
4286 *destPtr++ = (Guchar)pix0;
4287 break;
4289 case splashModeRGB8:
4291 // compute the final pixel
4292 pix0 = pix1 = pix2 = 0;
4293 for (i = 0; i < xStep; ++i) {
4294 pix0 += pixBuf[xx];
4295 pix1 += pixBuf[xx+1];
4296 pix2 += pixBuf[xx+2];
4297 xx += 3;
4299 // pix / xStep * yStep
4300 pix0 = (pix0 * d) >> 23;
4301 pix1 = (pix1 * d) >> 23;
4302 pix2 = (pix2 * d) >> 23;
4304 // store the pixel
4305 *destPtr++ = (Guchar)pix0;
4306 *destPtr++ = (Guchar)pix1;
4307 *destPtr++ = (Guchar)pix2;
4308 break;
4310 case splashModeXBGR8:
4312 // compute the final pixel
4313 pix0 = pix1 = pix2 = 0;
4314 for (i = 0; i < xStep; ++i) {
4315 pix0 += pixBuf[xx];
4316 pix1 += pixBuf[xx+1];
4317 pix2 += pixBuf[xx+2];
4318 xx += 4;
4320 // pix / xStep * yStep
4321 pix0 = (pix0 * d) >> 23;
4322 pix1 = (pix1 * d) >> 23;
4323 pix2 = (pix2 * d) >> 23;
4325 // store the pixel
4326 *destPtr++ = (Guchar)pix2;
4327 *destPtr++ = (Guchar)pix1;
4328 *destPtr++ = (Guchar)pix0;
4329 *destPtr++ = (Guchar)255;
4330 break;
4332 case splashModeBGR8:
4334 // compute the final pixel
4335 pix0 = pix1 = pix2 = 0;
4336 for (i = 0; i < xStep; ++i) {
4337 pix0 += pixBuf[xx];
4338 pix1 += pixBuf[xx+1];
4339 pix2 += pixBuf[xx+2];
4340 xx += 3;
4342 // pix / xStep * yStep
4343 pix0 = (pix0 * d) >> 23;
4344 pix1 = (pix1 * d) >> 23;
4345 pix2 = (pix2 * d) >> 23;
4347 // store the pixel
4348 *destPtr++ = (Guchar)pix2;
4349 *destPtr++ = (Guchar)pix1;
4350 *destPtr++ = (Guchar)pix0;
4351 break;
4353 #if SPLASH_CMYK
4354 case splashModeCMYK8:
4356 // compute the final pixel
4357 pix0 = pix1 = pix2 = pix3 = 0;
4358 for (i = 0; i < xStep; ++i) {
4359 pix0 += pixBuf[xx];
4360 pix1 += pixBuf[xx+1];
4361 pix2 += pixBuf[xx+2];
4362 pix3 += pixBuf[xx+3];
4363 xx += 4;
4365 // pix / xStep * yStep
4366 pix0 = (pix0 * d) >> 23;
4367 pix1 = (pix1 * d) >> 23;
4368 pix2 = (pix2 * d) >> 23;
4369 pix3 = (pix3 * d) >> 23;
4371 // store the pixel
4372 *destPtr++ = (Guchar)pix0;
4373 *destPtr++ = (Guchar)pix1;
4374 *destPtr++ = (Guchar)pix2;
4375 *destPtr++ = (Guchar)pix3;
4376 break;
4377 case splashModeDeviceN8:
4379 // compute the final pixel
4380 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4381 pix[cp] = 0;
4382 for (i = 0; i < xStep; ++i) {
4383 for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
4384 pix[cp] += pixBuf[xx + cp];
4386 xx += (SPOT_NCOMPS+4);
4388 // pix / xStep * yStep
4389 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4390 pix[cp] = (pix[cp] * d) >> 23;
4392 // store the pixel
4393 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4394 *destPtr++ = (Guchar)pix[cp];
4395 break;
4396 #endif
4399 case splashModeMono1: // mono1 is not allowed
4400 default:
4401 break;
4404 // process alpha
4405 if (srcAlpha) {
4406 alpha = 0;
4407 for (i = 0; i < xStep; ++i, ++xxa) {
4408 alpha += alphaPixBuf[xxa];
4410 // alpha / xStep * yStep
4411 alpha = (alpha * d) >> 23;
4412 *destAlphaPtr++ = (Guchar)alpha;
4417 gfree(alphaPixBuf);
4418 gfree(alphaLineBuf);
4419 gfree(pixBuf);
4420 gfree(lineBuf);
4423 void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
4424 SplashColorMode srcMode, int nComps,
4425 GBool srcAlpha, int srcWidth, int srcHeight,
4426 int scaledWidth, int scaledHeight,
4427 SplashBitmap *dest) {
4428 Guchar *lineBuf, *alphaLineBuf;
4429 Guint *pixBuf, *alphaPixBuf;
4430 Guint pix[splashMaxColorComps];
4431 Guint alpha;
4432 Guchar *destPtr, *destAlphaPtr;
4433 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
4434 int i, j;
4436 // Bresenham parameters for y scale
4437 yp = srcHeight / scaledHeight;
4438 yq = srcHeight % scaledHeight;
4440 // Bresenham parameters for x scale
4441 xp = scaledWidth / srcWidth;
4442 xq = scaledWidth % srcWidth;
4444 // allocate buffers
4445 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4446 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4447 if (srcAlpha) {
4448 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4449 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4450 } else {
4451 alphaLineBuf = NULL;
4452 alphaPixBuf = NULL;
4455 // init y scale Bresenham
4456 yt = 0;
4458 destPtr = dest->data;
4459 destAlphaPtr = dest->alpha;
4460 for (y = 0; y < scaledHeight; ++y) {
4462 // y scale Bresenham
4463 if ((yt += yq) >= scaledHeight) {
4464 yt -= scaledHeight;
4465 yStep = yp + 1;
4466 } else {
4467 yStep = yp;
4470 // read rows from image
4471 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4472 if (srcAlpha) {
4473 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4475 for (i = 0; i < yStep; ++i) {
4476 (*src)(srcData, lineBuf, alphaLineBuf);
4477 for (j = 0; j < srcWidth * nComps; ++j) {
4478 pixBuf[j] += lineBuf[j];
4480 if (srcAlpha) {
4481 for (j = 0; j < srcWidth; ++j) {
4482 alphaPixBuf[j] += alphaLineBuf[j];
4487 // init x scale Bresenham
4488 xt = 0;
4489 d = (1 << 23) / yStep;
4491 for (x = 0; x < srcWidth; ++x) {
4493 // x scale Bresenham
4494 if ((xt += xq) >= srcWidth) {
4495 xt -= srcWidth;
4496 xStep = xp + 1;
4497 } else {
4498 xStep = xp;
4501 // compute the final pixel
4502 for (i = 0; i < nComps; ++i) {
4503 // pixBuf[] / yStep
4504 pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
4507 // store the pixel
4508 switch (srcMode) {
4509 case splashModeMono1: // mono1 is not allowed
4510 break;
4511 case splashModeMono8:
4512 for (i = 0; i < xStep; ++i) {
4513 *destPtr++ = (Guchar)pix[0];
4515 break;
4516 case splashModeRGB8:
4517 for (i = 0; i < xStep; ++i) {
4518 *destPtr++ = (Guchar)pix[0];
4519 *destPtr++ = (Guchar)pix[1];
4520 *destPtr++ = (Guchar)pix[2];
4522 break;
4523 case splashModeXBGR8:
4524 for (i = 0; i < xStep; ++i) {
4525 *destPtr++ = (Guchar)pix[2];
4526 *destPtr++ = (Guchar)pix[1];
4527 *destPtr++ = (Guchar)pix[0];
4528 *destPtr++ = (Guchar)255;
4530 break;
4531 case splashModeBGR8:
4532 for (i = 0; i < xStep; ++i) {
4533 *destPtr++ = (Guchar)pix[2];
4534 *destPtr++ = (Guchar)pix[1];
4535 *destPtr++ = (Guchar)pix[0];
4537 break;
4538 #if SPLASH_CMYK
4539 case splashModeCMYK8:
4540 for (i = 0; i < xStep; ++i) {
4541 *destPtr++ = (Guchar)pix[0];
4542 *destPtr++ = (Guchar)pix[1];
4543 *destPtr++ = (Guchar)pix[2];
4544 *destPtr++ = (Guchar)pix[3];
4546 break;
4547 case splashModeDeviceN8:
4548 for (i = 0; i < xStep; ++i) {
4549 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4550 *destPtr++ = (Guchar)pix[cp];
4552 break;
4553 #endif
4556 // process alpha
4557 if (srcAlpha) {
4558 // alphaPixBuf[] / yStep
4559 alpha = (alphaPixBuf[x] * d) >> 23;
4560 for (i = 0; i < xStep; ++i) {
4561 *destAlphaPtr++ = (Guchar)alpha;
4567 gfree(alphaPixBuf);
4568 gfree(alphaLineBuf);
4569 gfree(pixBuf);
4570 gfree(lineBuf);
4573 void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
4574 SplashColorMode srcMode, int nComps,
4575 GBool srcAlpha, int srcWidth, int srcHeight,
4576 int scaledWidth, int scaledHeight,
4577 SplashBitmap *dest) {
4578 Guchar *lineBuf, *alphaLineBuf;
4579 Guint pix[splashMaxColorComps];
4580 Guint alpha;
4581 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4582 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4583 int i, j;
4585 // Bresenham parameters for y scale
4586 yp = scaledHeight / srcHeight;
4587 yq = scaledHeight % srcHeight;
4589 // Bresenham parameters for x scale
4590 xp = srcWidth / scaledWidth;
4591 xq = srcWidth % scaledWidth;
4593 // allocate buffers
4594 lineBuf = (Guchar *)gmallocn_checkoverflow(srcWidth, nComps);
4595 if (unlikely(!lineBuf))
4596 return;
4597 if (srcAlpha) {
4598 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4599 } else {
4600 alphaLineBuf = NULL;
4603 // init y scale Bresenham
4604 yt = 0;
4606 destPtr0 = dest->data;
4607 destAlphaPtr0 = dest->alpha;
4608 for (y = 0; y < srcHeight; ++y) {
4610 // y scale Bresenham
4611 if ((yt += yq) >= srcHeight) {
4612 yt -= srcHeight;
4613 yStep = yp + 1;
4614 } else {
4615 yStep = yp;
4618 // read row from image
4619 (*src)(srcData, lineBuf, alphaLineBuf);
4621 // init x scale Bresenham
4622 xt = 0;
4623 d0 = (1 << 23) / xp;
4624 d1 = (1 << 23) / (xp + 1);
4626 xx = xxa = 0;
4627 for (x = 0; x < scaledWidth; ++x) {
4629 // x scale Bresenham
4630 if ((xt += xq) >= scaledWidth) {
4631 xt -= scaledWidth;
4632 xStep = xp + 1;
4633 d = d1;
4634 } else {
4635 xStep = xp;
4636 d = d0;
4639 // compute the final pixel
4640 for (i = 0; i < nComps; ++i) {
4641 pix[i] = 0;
4643 for (i = 0; i < xStep; ++i) {
4644 for (j = 0; j < nComps; ++j, ++xx) {
4645 pix[j] += lineBuf[xx];
4648 for (i = 0; i < nComps; ++i) {
4649 // pix[] / xStep
4650 pix[i] = (pix[i] * d) >> 23;
4653 // store the pixel
4654 switch (srcMode) {
4655 case splashModeMono1: // mono1 is not allowed
4656 break;
4657 case splashModeMono8:
4658 for (i = 0; i < yStep; ++i) {
4659 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4660 *destPtr++ = (Guchar)pix[0];
4662 break;
4663 case splashModeRGB8:
4664 for (i = 0; i < yStep; ++i) {
4665 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4666 *destPtr++ = (Guchar)pix[0];
4667 *destPtr++ = (Guchar)pix[1];
4668 *destPtr++ = (Guchar)pix[2];
4670 break;
4671 case splashModeXBGR8:
4672 for (i = 0; i < yStep; ++i) {
4673 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4674 *destPtr++ = (Guchar)pix[2];
4675 *destPtr++ = (Guchar)pix[1];
4676 *destPtr++ = (Guchar)pix[0];
4677 *destPtr++ = (Guchar)255;
4679 break;
4680 case splashModeBGR8:
4681 for (i = 0; i < yStep; ++i) {
4682 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4683 *destPtr++ = (Guchar)pix[2];
4684 *destPtr++ = (Guchar)pix[1];
4685 *destPtr++ = (Guchar)pix[0];
4687 break;
4688 #if SPLASH_CMYK
4689 case splashModeCMYK8:
4690 for (i = 0; i < yStep; ++i) {
4691 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4692 *destPtr++ = (Guchar)pix[0];
4693 *destPtr++ = (Guchar)pix[1];
4694 *destPtr++ = (Guchar)pix[2];
4695 *destPtr++ = (Guchar)pix[3];
4697 break;
4698 case splashModeDeviceN8:
4699 for (i = 0; i < yStep; ++i) {
4700 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4701 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4702 *destPtr++ = (Guchar)pix[cp];
4704 break;
4705 #endif
4708 // process alpha
4709 if (srcAlpha) {
4710 alpha = 0;
4711 for (i = 0; i < xStep; ++i, ++xxa) {
4712 alpha += alphaLineBuf[xxa];
4714 // alpha / xStep
4715 alpha = (alpha * d) >> 23;
4716 for (i = 0; i < yStep; ++i) {
4717 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
4718 *destAlphaPtr = (Guchar)alpha;
4723 destPtr0 += yStep * scaledWidth * nComps;
4724 if (srcAlpha) {
4725 destAlphaPtr0 += yStep * scaledWidth;
4729 gfree(alphaLineBuf);
4730 gfree(lineBuf);
4733 void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
4734 SplashColorMode srcMode, int nComps,
4735 GBool srcAlpha, int srcWidth, int srcHeight,
4736 int scaledWidth, int scaledHeight,
4737 SplashBitmap *dest) {
4738 Guchar *lineBuf, *alphaLineBuf;
4739 Guint pix[splashMaxColorComps];
4740 Guint alpha;
4741 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4742 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
4743 int i, j;
4745 // Bresenham parameters for y scale
4746 yp = scaledHeight / srcHeight;
4747 yq = scaledHeight % srcHeight;
4749 // Bresenham parameters for x scale
4750 xp = scaledWidth / srcWidth;
4751 xq = scaledWidth % srcWidth;
4753 // allocate buffers
4754 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4755 if (srcAlpha) {
4756 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4757 } else {
4758 alphaLineBuf = NULL;
4761 // init y scale Bresenham
4762 yt = 0;
4764 destPtr0 = dest->data;
4765 destAlphaPtr0 = dest->alpha;
4766 for (y = 0; y < srcHeight; ++y) {
4768 // y scale Bresenham
4769 if ((yt += yq) >= srcHeight) {
4770 yt -= srcHeight;
4771 yStep = yp + 1;
4772 } else {
4773 yStep = yp;
4776 // read row from image
4777 (*src)(srcData, lineBuf, alphaLineBuf);
4779 // init x scale Bresenham
4780 xt = 0;
4782 xx = 0;
4783 for (x = 0; x < srcWidth; ++x) {
4785 // x scale Bresenham
4786 if ((xt += xq) >= srcWidth) {
4787 xt -= srcWidth;
4788 xStep = xp + 1;
4789 } else {
4790 xStep = xp;
4793 // compute the final pixel
4794 for (i = 0; i < nComps; ++i) {
4795 pix[i] = lineBuf[x * nComps + i];
4798 // store the pixel
4799 switch (srcMode) {
4800 case splashModeMono1: // mono1 is not allowed
4801 break;
4802 case splashModeMono8:
4803 for (i = 0; i < yStep; ++i) {
4804 for (j = 0; j < xStep; ++j) {
4805 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4806 *destPtr++ = (Guchar)pix[0];
4809 break;
4810 case splashModeRGB8:
4811 for (i = 0; i < yStep; ++i) {
4812 for (j = 0; j < xStep; ++j) {
4813 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4814 *destPtr++ = (Guchar)pix[0];
4815 *destPtr++ = (Guchar)pix[1];
4816 *destPtr++ = (Guchar)pix[2];
4819 break;
4820 case splashModeXBGR8:
4821 for (i = 0; i < yStep; ++i) {
4822 for (j = 0; j < xStep; ++j) {
4823 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4824 *destPtr++ = (Guchar)pix[2];
4825 *destPtr++ = (Guchar)pix[1];
4826 *destPtr++ = (Guchar)pix[0];
4827 *destPtr++ = (Guchar)255;
4830 break;
4831 case splashModeBGR8:
4832 for (i = 0; i < yStep; ++i) {
4833 for (j = 0; j < xStep; ++j) {
4834 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4835 *destPtr++ = (Guchar)pix[2];
4836 *destPtr++ = (Guchar)pix[1];
4837 *destPtr++ = (Guchar)pix[0];
4840 break;
4841 #if SPLASH_CMYK
4842 case splashModeCMYK8:
4843 for (i = 0; i < yStep; ++i) {
4844 for (j = 0; j < xStep; ++j) {
4845 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4846 *destPtr++ = (Guchar)pix[0];
4847 *destPtr++ = (Guchar)pix[1];
4848 *destPtr++ = (Guchar)pix[2];
4849 *destPtr++ = (Guchar)pix[3];
4852 break;
4853 case splashModeDeviceN8:
4854 for (i = 0; i < yStep; ++i) {
4855 for (j = 0; j < xStep; ++j) {
4856 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4857 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4858 *destPtr++ = (Guchar)pix[cp];
4861 break;
4862 #endif
4865 // process alpha
4866 if (srcAlpha) {
4867 alpha = alphaLineBuf[x];
4868 for (i = 0; i < yStep; ++i) {
4869 for (j = 0; j < xStep; ++j) {
4870 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j;
4871 *destAlphaPtr = (Guchar)alpha;
4876 xx += xStep;
4879 destPtr0 += yStep * scaledWidth * nComps;
4880 if (srcAlpha) {
4881 destAlphaPtr0 += yStep * scaledWidth;
4885 gfree(alphaLineBuf);
4886 gfree(lineBuf);
4889 // expand source row to scaledWidth using linear interpolation
4890 static void expandRow(Guchar *srcBuf, Guchar *dstBuf, int srcWidth, int scaledWidth, int nComps)
4892 double xStep = (double)srcWidth/scaledWidth;
4893 double xSrc = 0.0;
4894 double xFrac, xInt;
4895 int p;
4897 // pad the source with an extra pixel equal to the last pixel
4898 // so that when xStep is inside the last pixel we still have two
4899 // pixels to interpolate between.
4900 for (int i = 0; i < nComps; i++)
4901 srcBuf[srcWidth*nComps + i] = srcBuf[(srcWidth-1)*nComps + i];
4903 for (int x = 0; x < scaledWidth; x++) {
4904 xFrac = modf(xSrc, &xInt);
4905 p = (int)xInt;
4906 for (int c = 0; c < nComps; c++) {
4907 dstBuf[nComps*x + c] = srcBuf[nComps*p + c]*(1.0 - xFrac) + srcBuf[nComps*(p+1) + c]*xFrac;
4909 xSrc += xStep;
4913 // Scale up image using bilinear interpolation
4914 void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData,
4915 SplashColorMode srcMode, int nComps,
4916 GBool srcAlpha, int srcWidth, int srcHeight,
4917 int scaledWidth, int scaledHeight,
4918 SplashBitmap *dest) {
4919 Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2;
4920 Guint pix[splashMaxColorComps];
4921 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4922 int i;
4924 if (srcWidth < 1 || srcHeight < 1)
4925 return;
4927 // allocate buffers
4928 srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding
4929 lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
4930 lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps);
4931 if (srcAlpha) {
4932 alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding
4933 alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
4934 alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth);
4935 } else {
4936 alphaSrcBuf = NULL;
4937 alphaLineBuf1 = NULL;
4938 alphaLineBuf2 = NULL;
4941 double ySrc = 0.0;
4942 double yStep = (double)srcHeight/scaledHeight;
4943 double yFrac, yInt;
4944 int currentSrcRow = -1;
4945 (*src)(srcData, srcBuf, alphaSrcBuf);
4946 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4947 if (srcAlpha)
4948 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4950 destPtr0 = dest->data;
4951 destAlphaPtr0 = dest->alpha;
4952 for (int y = 0; y < scaledHeight; y++) {
4953 yFrac = modf(ySrc, &yInt);
4954 if ((int)yInt > currentSrcRow) {
4955 currentSrcRow++;
4956 // Copy line2 data to line1 and get next line2 data.
4957 // If line2 already contains the last source row we don't touch it.
4958 // This effectively adds an extra row of padding for interpolating the
4959 // last source row with.
4960 memcpy(lineBuf1, lineBuf2, scaledWidth * nComps);
4961 if (srcAlpha)
4962 memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth);
4963 if (currentSrcRow < srcHeight) {
4964 (*src)(srcData, srcBuf, alphaSrcBuf);
4965 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4966 if (srcAlpha)
4967 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4971 // write row y using linear interpolation on lineBuf1 and lineBuf2
4972 for (int x = 0; x < scaledWidth; ++x) {
4973 // compute the final pixel
4974 for (i = 0; i < nComps; ++i) {
4975 pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac;
4978 // store the pixel
4979 destPtr = destPtr0 + (y * scaledWidth + x) * nComps;
4980 switch (srcMode) {
4981 case splashModeMono1: // mono1 is not allowed
4982 break;
4983 case splashModeMono8:
4984 *destPtr++ = (Guchar)pix[0];
4985 break;
4986 case splashModeRGB8:
4987 *destPtr++ = (Guchar)pix[0];
4988 *destPtr++ = (Guchar)pix[1];
4989 *destPtr++ = (Guchar)pix[2];
4990 break;
4991 case splashModeXBGR8:
4992 *destPtr++ = (Guchar)pix[2];
4993 *destPtr++ = (Guchar)pix[1];
4994 *destPtr++ = (Guchar)pix[0];
4995 *destPtr++ = (Guchar)255;
4996 break;
4997 case splashModeBGR8:
4998 *destPtr++ = (Guchar)pix[2];
4999 *destPtr++ = (Guchar)pix[1];
5000 *destPtr++ = (Guchar)pix[0];
5001 break;
5002 #if SPLASH_CMYK
5003 case splashModeCMYK8:
5004 *destPtr++ = (Guchar)pix[0];
5005 *destPtr++ = (Guchar)pix[1];
5006 *destPtr++ = (Guchar)pix[2];
5007 *destPtr++ = (Guchar)pix[3];
5008 break;
5009 case splashModeDeviceN8:
5010 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
5011 *destPtr++ = (Guchar)pix[cp];
5012 break;
5013 #endif
5016 // process alpha
5017 if (srcAlpha) {
5018 destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x;
5019 *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac;
5023 ySrc += yStep;
5026 gfree(alphaSrcBuf);
5027 gfree(alphaLineBuf1);
5028 gfree(alphaLineBuf2);
5029 gfree(srcBuf);
5030 gfree(lineBuf1);
5031 gfree(lineBuf2);
5034 void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
5035 int nComps) {
5036 Guchar *lineBuf;
5037 Guchar *p0, *p1;
5038 int w;
5040 if (unlikely(img->data == NULL)) {
5041 error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage");
5042 return;
5045 w = width * nComps;
5046 lineBuf = (Guchar *)gmalloc(w);
5047 for (p0 = img->data, p1 = img->data + (height - 1) * w;
5048 p0 < p1;
5049 p0 += w, p1 -= w) {
5050 memcpy(lineBuf, p0, w);
5051 memcpy(p0, p1, w);
5052 memcpy(p1, lineBuf, w);
5054 if (img->alpha) {
5055 for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width;
5056 p0 < p1;
5057 p0 += width, p1 -= width) {
5058 memcpy(lineBuf, p0, width);
5059 memcpy(p0, p1, width);
5060 memcpy(p1, lineBuf, width);
5063 gfree(lineBuf);
5066 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest) {
5067 SplashClipResult clipRes = state->clip->testRect(xDest, yDest, xDest + src->getWidth() - 1, yDest + src->getHeight() - 1);
5068 if (clipRes != splashClipAllOutside) {
5069 blitImage(src, srcAlpha, xDest, yDest, clipRes);
5073 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
5074 SplashClipResult clipRes) {
5075 SplashPipe pipe;
5076 SplashColor pixel;
5077 Guchar *ap;
5078 int w, h, x0, y0, x1, y1, x, y;
5080 // split the image into clipped and unclipped regions
5081 w = src->getWidth();
5082 h = src->getHeight();
5083 if (clipRes == splashClipAllInside) {
5084 x0 = 0;
5085 y0 = 0;
5086 x1 = w;
5087 y1 = h;
5088 } else {
5089 if (state->clip->getNumPaths()) {
5090 x0 = x1 = w;
5091 y0 = y1 = h;
5092 } else {
5093 if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) {
5094 x0 = 0;
5096 if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) {
5097 y0 = 0;
5099 if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) {
5100 x1 = w;
5102 if (x1 < x0) {
5103 x1 = x0;
5105 if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
5106 y1 = h;
5108 if (y1 < y0) {
5109 y1 = y0;
5114 // draw the unclipped region
5115 if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
5116 pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
5117 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5118 if (srcAlpha) {
5119 for (y = y0; y < y1; ++y) {
5120 pipeSetXY(&pipe, xDest + x0, yDest + y);
5121 ap = src->getAlphaPtr() + y * w + x0;
5122 for (x = x0; x < x1; ++x) {
5123 src->getPixel(x, y, pixel);
5124 pipe.shape = *ap++;
5125 (this->*pipe.run)(&pipe);
5128 } else {
5129 for (y = y0; y < y1; ++y) {
5130 pipeSetXY(&pipe, xDest + x0, yDest + y);
5131 for (x = x0; x < x1; ++x) {
5132 src->getPixel(x, y, pixel);
5133 (this->*pipe.run)(&pipe);
5137 updateModX(xDest + x0);
5138 updateModX(xDest + x1 - 1);
5139 updateModY(yDest + y0);
5140 updateModY(yDest + y1 - 1);
5143 // draw the clipped regions
5144 if (y0 > 0) {
5145 blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0);
5147 if (y1 < h) {
5148 blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1);
5150 if (x0 > 0 && y0 < y1) {
5151 blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0);
5153 if (x1 < w && y0 < y1) {
5154 blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0,
5155 w - x1, y1 - y0);
5159 void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
5160 int xSrc, int ySrc, int xDest, int yDest,
5161 int w, int h) {
5162 SplashPipe pipe;
5163 SplashColor pixel;
5164 Guchar *ap;
5165 int x, y;
5167 if (vectorAntialias) {
5168 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5169 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
5170 drawAAPixelInit();
5171 if (srcAlpha) {
5172 for (y = 0; y < h; ++y) {
5173 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5174 for (x = 0; x < w; ++x) {
5175 src->getPixel(xSrc + x, ySrc + y, pixel);
5176 pipe.shape = *ap++;
5177 drawAAPixel(&pipe, xDest + x, yDest + y);
5180 } else {
5181 for (y = 0; y < h; ++y) {
5182 for (x = 0; x < w; ++x) {
5183 src->getPixel(xSrc + x, ySrc + y, pixel);
5184 pipe.shape = 255;
5185 drawAAPixel(&pipe, xDest + x, yDest + y);
5189 } else {
5190 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5191 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5192 if (srcAlpha) {
5193 for (y = 0; y < h; ++y) {
5194 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5195 pipeSetXY(&pipe, xDest, yDest + y);
5196 for (x = 0; x < w; ++x) {
5197 if (state->clip->test(xDest + x, yDest + y)) {
5198 src->getPixel(xSrc + x, ySrc + y, pixel);
5199 pipe.shape = *ap++;
5200 (this->*pipe.run)(&pipe);
5201 updateModX(xDest + x);
5202 updateModY(yDest + y);
5203 } else {
5204 pipeIncX(&pipe);
5205 ++ap;
5209 } else {
5210 for (y = 0; y < h; ++y) {
5211 pipeSetXY(&pipe, xDest, yDest + y);
5212 for (x = 0; x < w; ++x) {
5213 if (state->clip->test(xDest + x, yDest + y)) {
5214 src->getPixel(xSrc + x, ySrc + y, pixel);
5215 (this->*pipe.run)(&pipe);
5216 updateModX(xDest + x);
5217 updateModY(yDest + y);
5218 } else {
5219 pipeIncX(&pipe);
5227 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
5228 int xDest, int yDest, int w, int h,
5229 GBool noClip, GBool nonIsolated,
5230 GBool knockout, SplashCoord knockoutOpacity) {
5231 SplashPipe pipe;
5232 SplashColor pixel;
5233 Guchar alpha;
5234 Guchar *ap;
5235 int x, y;
5237 if (src->mode != bitmap->mode) {
5238 return splashErrModeMismatch;
5241 if (unlikely(!bitmap->data)) {
5242 return splashErrZeroImage;
5245 if(src->getSeparationList()->getLength() > bitmap->getSeparationList()->getLength()) {
5246 for (x = bitmap->getSeparationList()->getLength(); x < src->getSeparationList()->getLength(); x++)
5247 bitmap->getSeparationList()->append(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy());
5249 if (src->alpha) {
5250 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5251 (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated,
5252 knockout, (Guchar)splashRound(knockoutOpacity * 255));
5253 if (noClip) {
5254 for (y = 0; y < h; ++y) {
5255 pipeSetXY(&pipe, xDest, yDest + y);
5256 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5257 for (x = 0; x < w; ++x) {
5258 src->getPixel(xSrc + x, ySrc + y, pixel);
5259 alpha = *ap++;
5260 // this uses shape instead of alpha, which isn't technically
5261 // correct, but works out the same
5262 pipe.shape = alpha;
5263 (this->*pipe.run)(&pipe);
5266 updateModX(xDest);
5267 updateModX(xDest + w - 1);
5268 updateModY(yDest);
5269 updateModY(yDest + h - 1);
5270 } else {
5271 for (y = 0; y < h; ++y) {
5272 pipeSetXY(&pipe, xDest, yDest + y);
5273 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5274 for (x = 0; x < w; ++x) {
5275 src->getPixel(xSrc + x, ySrc + y, pixel);
5276 alpha = *ap++;
5277 if (state->clip->test(xDest + x, yDest + y)) {
5278 // this uses shape instead of alpha, which isn't technically
5279 // correct, but works out the same
5280 pipe.shape = alpha;
5281 (this->*pipe.run)(&pipe);
5282 updateModX(xDest + x);
5283 updateModY(yDest + y);
5284 } else {
5285 pipeIncX(&pipe);
5290 } else {
5291 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5292 (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated);
5293 if (noClip) {
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 (this->*pipe.run)(&pipe);
5301 updateModX(xDest);
5302 updateModX(xDest + w - 1);
5303 updateModY(yDest);
5304 updateModY(yDest + h - 1);
5305 } else {
5306 for (y = 0; y < h; ++y) {
5307 pipeSetXY(&pipe, xDest, yDest + y);
5308 for (x = 0; x < w; ++x) {
5309 src->getPixel(xSrc + x, ySrc + y, pixel);
5310 if (state->clip->test(xDest + x, yDest + y)) {
5311 (this->*pipe.run)(&pipe);
5312 updateModX(xDest + x);
5313 updateModY(yDest + y);
5314 } else {
5315 pipeIncX(&pipe);
5322 return splashOk;
5325 void Splash::compositeBackground(SplashColorPtr color) {
5326 SplashColorPtr p;
5327 Guchar *q;
5328 Guchar alpha, alpha1, c, color0, color1, color2;
5329 #if SPLASH_CMYK
5330 Guchar color3;
5331 Guchar colorsp[SPOT_NCOMPS+4], cp;
5332 #endif
5333 int x, y, mask;
5335 if (unlikely(bitmap->alpha == NULL)) {
5336 error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground");
5337 return;
5340 switch (bitmap->mode) {
5341 case splashModeMono1:
5342 color0 = color[0];
5343 for (y = 0; y < bitmap->height; ++y) {
5344 p = &bitmap->data[y * bitmap->rowSize];
5345 q = &bitmap->alpha[y * bitmap->width];
5346 mask = 0x80;
5347 for (x = 0; x < bitmap->width; ++x) {
5348 alpha = *q++;
5349 alpha1 = 255 - alpha;
5350 c = (*p & mask) ? 0xff : 0x00;
5351 c = div255(alpha1 * color0 + alpha * c);
5352 if (c & 0x80) {
5353 *p |= mask;
5354 } else {
5355 *p &= ~mask;
5357 if (!(mask >>= 1)) {
5358 mask = 0x80;
5359 ++p;
5363 break;
5364 case splashModeMono8:
5365 color0 = color[0];
5366 for (y = 0; y < bitmap->height; ++y) {
5367 p = &bitmap->data[y * bitmap->rowSize];
5368 q = &bitmap->alpha[y * bitmap->width];
5369 for (x = 0; x < bitmap->width; ++x) {
5370 alpha = *q++;
5371 alpha1 = 255 - alpha;
5372 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5373 ++p;
5376 break;
5377 case splashModeRGB8:
5378 case splashModeBGR8:
5379 color0 = color[0];
5380 color1 = color[1];
5381 color2 = color[2];
5382 for (y = 0; y < bitmap->height; ++y) {
5383 p = &bitmap->data[y * bitmap->rowSize];
5384 q = &bitmap->alpha[y * bitmap->width];
5385 for (x = 0; x < bitmap->width; ++x) {
5386 alpha = *q++;
5387 if (alpha == 0)
5389 p[0] = color0;
5390 p[1] = color1;
5391 p[2] = color2;
5393 else if (alpha != 255)
5395 alpha1 = 255 - alpha;
5396 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5397 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5398 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5400 p += 3;
5403 break;
5404 case splashModeXBGR8:
5405 color0 = color[0];
5406 color1 = color[1];
5407 color2 = color[2];
5408 for (y = 0; y < bitmap->height; ++y) {
5409 p = &bitmap->data[y * bitmap->rowSize];
5410 q = &bitmap->alpha[y * bitmap->width];
5411 for (x = 0; x < bitmap->width; ++x) {
5412 alpha = *q++;
5413 if (alpha == 0)
5415 p[0] = color0;
5416 p[1] = color1;
5417 p[2] = color2;
5419 else if (alpha != 255)
5421 alpha1 = 255 - alpha;
5422 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5423 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5424 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5426 p[3] = 255;
5427 p += 4;
5430 break;
5431 #if SPLASH_CMYK
5432 case splashModeCMYK8:
5433 color0 = color[0];
5434 color1 = color[1];
5435 color2 = color[2];
5436 color3 = color[3];
5437 for (y = 0; y < bitmap->height; ++y) {
5438 p = &bitmap->data[y * bitmap->rowSize];
5439 q = &bitmap->alpha[y * bitmap->width];
5440 for (x = 0; x < bitmap->width; ++x) {
5441 alpha = *q++;
5442 if (alpha == 0)
5444 p[0] = color0;
5445 p[1] = color1;
5446 p[2] = color2;
5447 p[3] = color3;
5449 else if (alpha != 255)
5451 alpha1 = 255 - alpha;
5452 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5453 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5454 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5455 p[3] = div255(alpha1 * color3 + alpha * p[3]);
5457 p += 4;
5460 break;
5461 case splashModeDeviceN8:
5462 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5463 colorsp[cp] = color[cp];
5464 for (y = 0; y < bitmap->height; ++y) {
5465 p = &bitmap->data[y * bitmap->rowSize];
5466 q = &bitmap->alpha[y * bitmap->width];
5467 for (x = 0; x < bitmap->width; ++x) {
5468 alpha = *q++;
5469 if (alpha == 0)
5471 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5472 p[cp] = colorsp[cp];
5474 else if (alpha != 255)
5476 alpha1 = 255 - alpha;
5477 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5478 p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]);
5480 p += (SPOT_NCOMPS+4);
5483 break;
5484 #endif
5486 memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
5489 GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
5491 double xdbl[3] = {0., 0., 0.};
5492 double ydbl[3] = {0., 0., 0.};
5493 int x[3] = {0, 0, 0};
5494 int y[3] = {0, 0, 0};
5495 double xt=0., xa=0., yt=0.;
5496 double ca=0., ct=0.;
5498 // triangle interpolation:
5500 double scanLimitMapL[2] = {0., 0.};
5501 double scanLimitMapR[2] = {0., 0.};
5502 double scanColorMapL[2] = {0., 0.};
5503 double scanColorMapR[2] = {0., 0.};
5504 double scanColorMap[2] = {0., 0.};
5505 int scanEdgeL[2] = { 0, 0 };
5506 int scanEdgeR[2] = { 0, 0 };
5507 GBool hasFurtherSegment = gFalse;
5509 int scanLineOff = 0;
5510 int bitmapOff = 0;
5511 int scanLimitR = 0, scanLimitL = 0;
5513 int bitmapWidth = bitmap->getWidth();
5514 SplashClip* clip = getClip();
5515 SplashBitmap *blitTarget = bitmap;
5516 SplashColorPtr bitmapData = bitmap->getDataPtr();
5517 int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
5518 SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
5519 SplashColorPtr cur = NULL;
5520 SplashCoord* userToCanvasMatrix = getMatrix();
5521 SplashColorMode bitmapMode = bitmap->getMode();
5522 GBool hasAlpha = (bitmapAlpha != NULL);
5523 int rowSize = bitmap->getRowSize();
5524 int colorComps = 0;
5525 switch (bitmapMode) {
5526 case splashModeMono1:
5527 break;
5528 case splashModeMono8:
5529 colorComps=1;
5530 break;
5531 case splashModeRGB8:
5532 colorComps=3;
5533 break;
5534 case splashModeBGR8:
5535 colorComps=3;
5536 break;
5537 case splashModeXBGR8:
5538 colorComps=4;
5539 break;
5540 #if SPLASH_CMYK
5541 case splashModeCMYK8:
5542 colorComps=4;
5543 break;
5544 case splashModeDeviceN8:
5545 colorComps=SPOT_NCOMPS+4;
5546 break;
5547 #endif
5550 SplashPipe pipe;
5551 SplashColor cSrcVal;
5553 pipeInit(&pipe, 0, 0, NULL, cSrcVal, (Guchar)splashRound(state->strokeAlpha * 255), gFalse, gFalse);
5555 if (vectorAntialias) {
5556 if (aaBuf == NULL)
5557 return gFalse; // fall back to old behaviour
5558 drawAAPixelInit();
5561 // idea:
5562 // 1. If pipe->noTransparency && !state->blendFunc
5563 // -> blit directly into the drawing surface!
5564 // -> disable alpha manually.
5565 // 2. Otherwise:
5566 // - blit also directly, but into an intermediate surface.
5567 // Afterwards, blit the intermediate surface using the drawing pipeline.
5568 // This is necessary because triangle elements can be on top of each
5569 // other, so the complete shading needs to be drawn before opacity is
5570 // applied.
5571 // - the final step, is performed using a SplashPipe:
5572 // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference
5573 // - invoke drawPixel(&pipe,X,Y,bNoClip);
5574 GBool bDirectBlit = vectorAntialias ? gFalse : pipe.noTransparency && !state->blendFunc;
5575 if (!bDirectBlit) {
5576 blitTarget = new SplashBitmap(bitmap->getWidth(),
5577 bitmap->getHeight(),
5578 bitmap->getRowPad(),
5579 bitmap->getMode(),
5580 gTrue,
5581 bitmap->getRowSize() >= 0);
5582 bitmapData = blitTarget->getDataPtr();
5583 bitmapAlpha = blitTarget->getAlphaPtr();
5585 // initialisation seems to be necessary:
5586 int S = bitmap->getWidth() * bitmap->getHeight();
5587 for (int i = 0; i < S; ++i)
5588 bitmapAlpha[i] = 0;
5589 hasAlpha = gTrue;
5592 if (shading->isParameterized()) {
5593 double color[3];
5594 double colorinterp;
5596 for (int i = 0; i < shading->getNTriangles(); ++i) {
5597 shading->getTriangle(i,
5598 xdbl + 0, ydbl + 0, color + 0,
5599 xdbl + 1, ydbl + 1, color + 1,
5600 xdbl + 2, ydbl + 2, color + 2);
5601 for (int m = 0; m < 3; ++m) {
5602 xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
5603 yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5];
5604 xdbl[m] = xt;
5605 ydbl[m] = yt;
5606 // we operate on scanlines which are integer offsets into the
5607 // raster image. The double offsets are of no use here.
5608 x[m] = splashRound(xt);
5609 y[m] = splashRound(yt);
5611 // sort according to y coordinate to simplify sweep through scanlines:
5612 // INSERTION SORT.
5613 if (y[0] > y[1]) {
5614 Guswap(x[0], x[1]);
5615 Guswap(y[0], y[1]);
5616 Guswap(color[0], color[1]);
5618 // first two are sorted.
5619 assert(y[0] <= y[1]);
5620 if (y[1] > y[2]) {
5621 int tmpX = x[2];
5622 int tmpY = y[2];
5623 double tmpC = color[2];
5624 x[2] = x[1]; y[2] = y[1]; color[2] = color[1];
5626 if (y[0] > tmpY) {
5627 x[1] = x[0]; y[1] = y[0]; color[1] = color[0];
5628 x[0] = tmpX; y[0] = tmpY; color[0] = tmpC;
5629 } else {
5630 x[1] = tmpX; y[1] = tmpY; color[1] = tmpC;
5633 // first three are sorted
5634 assert(y[0] <= y[1]);
5635 assert(y[1] <= y[2]);
5636 /////
5638 // this here is det( T ) == 0
5639 // where T is the matrix to map to barycentric coordinates.
5640 if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0)
5641 continue; // degenerate triangle.
5643 // this here initialises the scanline generation.
5644 // We start with low Y coordinates and sweep up to the large Y
5645 // coordinates.
5647 // scanEdgeL[m] in {0,1,2} m=0,1
5648 // scanEdgeR[m] in {0,1,2} m=0,1
5650 // are the two edges between which scanlines are (currently)
5651 // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
5652 // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
5654 scanEdgeL[0] = 0;
5655 scanEdgeR[0] = 0;
5656 if (y[0] == y[1]) {
5657 scanEdgeL[0] = 1;
5658 scanEdgeL[1] = scanEdgeR[1] = 2;
5660 } else {
5661 scanEdgeL[1] = 1; scanEdgeR[1] = 2;
5663 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5664 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5666 // Ok. Now prepare the linear maps which map the y coordinate of
5667 // the current scanline to the corresponding LEFT and RIGHT x
5668 // coordinate (which define the scanline).
5669 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5670 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5671 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5672 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5674 xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
5675 xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
5676 if (xa > xt) {
5677 // I have "left" is to the right of "right".
5678 // Exchange sides!
5679 Guswap(scanEdgeL[0], scanEdgeR[0]);
5680 Guswap(scanEdgeL[1], scanEdgeR[1]);
5681 Guswap(scanLimitMapL[0], scanLimitMapR[0]);
5682 Guswap(scanLimitMapL[1], scanLimitMapR[1]);
5683 // FIXME I'm sure there is a more efficient way to check this.
5686 // Same game: we can linearly interpolate the color based on the
5687 // current y coordinate (that's correct for triangle
5688 // interpolation due to linearity. We could also have done it in
5689 // barycentric coordinates, but that's slightly more involved)
5690 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5691 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5692 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5693 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5695 hasFurtherSegment = (y[1] < y[2]);
5696 scanLineOff = y[0] * rowSize;
5698 for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
5699 if (hasFurtherSegment && Y == y[1]) {
5700 // SWEEP EVENT: we encountered the next segment.
5702 // switch to next segment, either at left end or at right
5703 // end:
5704 if (scanEdgeL[1] == 1) {
5705 scanEdgeL[0] = 1;
5706 scanEdgeL[1] = 2;
5707 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5708 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5710 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5711 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5712 } else if (scanEdgeR[1] == 1) {
5713 scanEdgeR[0] = 1;
5714 scanEdgeR[1] = 2;
5715 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5716 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5718 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5719 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5721 assert( y[scanEdgeL[0]] < y[scanEdgeL[1]] );
5722 assert( y[scanEdgeR[0]] < y[scanEdgeR[1]] );
5723 hasFurtherSegment = gFalse;
5726 yt = Y;
5728 xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
5729 xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
5731 ca = yt * scanColorMapL[0] + scanColorMapL[1];
5732 ct = yt * scanColorMapR[0] + scanColorMapR[1];
5734 scanLimitL = splashRound(xa);
5735 scanLimitR = splashRound(xt);
5737 // Ok. Now: init the color interpolation depending on the X
5738 // coordinate inside of the current scanline:
5739 scanColorMap[0] = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL));
5740 scanColorMap[1] = ca - scanLimitL * scanColorMap[0];
5742 // handled by clipping:
5743 // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
5744 assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
5745 assert(scanLineOff == Y * rowSize);
5747 colorinterp = scanColorMap[0] * scanLimitL + scanColorMap[1];
5749 bitmapOff = scanLineOff + scanLimitL * colorComps;
5750 for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap[0], bitmapOff += colorComps) {
5751 // FIXME : standard rectangular clipping can be done for a
5752 // complete scanline which is faster
5753 // --> see SplashClip and its methods
5754 if (!clip->test(X, Y))
5755 continue;
5757 assert(fabs(colorinterp - (scanColorMap[0] * X + scanColorMap[1])) < 1e-10);
5758 assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
5760 shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]);
5762 // make the shading visible.
5763 // Note that opacity is handled by the bDirectBlit stuff, see
5764 // above for comments and below for implementation.
5765 if (hasAlpha)
5766 bitmapAlpha[Y * bitmapWidth + X] = 255;
5770 } else {
5771 return gFalse;
5774 if (!bDirectBlit) {
5775 // ok. Finalize the stuff by blitting the shading into the final
5776 // geometry, this time respecting the rendering pipe.
5777 int W = blitTarget->getWidth();
5778 int H = blitTarget->getHeight();
5779 cur = cSrcVal;
5781 for (int X = 0; X < W; ++X) {
5782 for (int Y = 0; Y < H; ++Y) {
5783 if (!bitmapAlpha[Y * bitmapWidth + X])
5784 continue; // draw only parts of the shading!
5785 bitmapOff = Y * rowSize + colorComps * X;
5787 for (int m = 0; m < colorComps; ++m)
5788 cur[m] = bitmapData[bitmapOff + m];
5789 if (vectorAntialias) {
5790 drawAAPixel(&pipe, X, Y);
5791 } else {
5792 drawPixel(&pipe, X, Y, gTrue); // no clipping - has already been done.
5797 delete blitTarget;
5798 blitTarget = NULL;
5801 return gTrue;
5804 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
5805 int xDest, int yDest, int w, int h) {
5806 SplashColorPtr p, sp;
5807 Guchar *q;
5808 int x, y, mask, srcMask;
5810 if (src->mode != bitmap->mode) {
5811 return splashErrModeMismatch;
5814 if (unlikely(!bitmap->data)) {
5815 return splashErrZeroImage;
5818 switch (bitmap->mode) {
5819 case splashModeMono1:
5820 for (y = 0; y < h; ++y) {
5821 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
5822 mask = 0x80 >> (xDest & 7);
5823 sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
5824 srcMask = 0x80 >> (xSrc & 7);
5825 for (x = 0; x < w; ++x) {
5826 if (*sp & srcMask) {
5827 *p |= mask;
5828 } else {
5829 *p &= ~mask;
5831 if (!(mask >>= 1)) {
5832 mask = 0x80;
5833 ++p;
5835 if (!(srcMask >>= 1)) {
5836 srcMask = 0x80;
5837 ++sp;
5841 break;
5842 case splashModeMono8:
5843 for (y = 0; y < h; ++y) {
5844 p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
5845 sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc];
5846 for (x = 0; x < w; ++x) {
5847 *p++ = *sp++;
5850 break;
5851 case splashModeRGB8:
5852 case splashModeBGR8:
5853 for (y = 0; y < h; ++y) {
5854 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
5855 sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
5856 for (x = 0; x < w; ++x) {
5857 *p++ = *sp++;
5858 *p++ = *sp++;
5859 *p++ = *sp++;
5862 break;
5863 case splashModeXBGR8:
5864 for (y = 0; y < h; ++y) {
5865 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5866 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5867 for (x = 0; x < w; ++x) {
5868 *p++ = *sp++;
5869 *p++ = *sp++;
5870 *p++ = *sp++;
5871 *p++ = 255;
5872 sp++;
5875 break;
5876 #if SPLASH_CMYK
5877 case splashModeCMYK8:
5878 for (y = 0; y < h; ++y) {
5879 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5880 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5881 for (x = 0; x < w; ++x) {
5882 *p++ = *sp++;
5883 *p++ = *sp++;
5884 *p++ = *sp++;
5885 *p++ = *sp++;
5888 break;
5889 case splashModeDeviceN8:
5890 for (y = 0; y < h; ++y) {
5891 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS+4) * xDest];
5892 sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS+4) * xSrc];
5893 for (x = 0; x < w; ++x) {
5894 for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
5895 *p++ = *sp++;
5898 break;
5899 #endif
5902 if (bitmap->alpha) {
5903 for (y = 0; y < h; ++y) {
5904 q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
5905 memset(q, 0x00, w);
5909 return splashOk;
5912 SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
5913 GBool flatten) {
5914 SplashPath *pathIn, *dashPath, *pathOut;
5915 SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
5916 SplashCoord crossprod, dotprod, miter, m;
5917 GBool first, last, closed;
5918 int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
5919 int left0, left1, left2, right0, right1, right2, join0, join1, join2;
5920 int leftFirst, rightFirst, firstPt;
5922 pathOut = new SplashPath();
5924 if (path->length == 0) {
5925 return pathOut;
5928 if (flatten) {
5929 pathIn = flattenPath(path, state->matrix, state->flatness);
5930 if (state->lineDashLength > 0) {
5931 dashPath = makeDashedPath(pathIn);
5932 delete pathIn;
5933 pathIn = dashPath;
5934 if (pathIn->length == 0) {
5935 delete pathIn;
5936 return pathOut;
5939 } else {
5940 pathIn = path;
5943 subpathStart0 = subpathStart1 = 0; // make gcc happy
5944 seg = 0; // make gcc happy
5945 closed = gFalse; // make gcc happy
5946 left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
5947 leftFirst = rightFirst = firstPt = 0; // make gcc happy
5949 i0 = 0;
5950 for (i1 = i0;
5951 !(pathIn->flags[i1] & splashPathLast) &&
5952 i1 + 1 < pathIn->length &&
5953 pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
5954 pathIn->pts[i1+1].y == pathIn->pts[i1].y;
5955 ++i1) ;
5957 while (i1 < pathIn->length) {
5958 if ((first = pathIn->flags[i0] & splashPathFirst)) {
5959 subpathStart0 = i0;
5960 subpathStart1 = i1;
5961 seg = 0;
5962 closed = pathIn->flags[i0] & splashPathClosed;
5964 j0 = i1 + 1;
5965 if (j0 < pathIn->length) {
5966 for (j1 = j0;
5967 !(pathIn->flags[j1] & splashPathLast) &&
5968 j1 + 1 < pathIn->length &&
5969 pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
5970 pathIn->pts[j1+1].y == pathIn->pts[j1].y;
5971 ++j1) ;
5972 } else {
5973 j1 = j0;
5975 if (pathIn->flags[i1] & splashPathLast) {
5976 if (first && state->lineCap == splashLineCapRound) {
5977 // special case: zero-length subpath with round line caps -->
5978 // draw a circle
5979 pathOut->moveTo(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->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5994 pathIn->pts[i0].y - bezierCircle2 * w,
5995 pathIn->pts[i0].x - bezierCircle2 * w,
5996 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
5997 pathIn->pts[i0].x,
5998 pathIn->pts[i0].y - (SplashCoord)0.5 * w);
5999 pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
6000 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
6001 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
6002 pathIn->pts[i0].y - bezierCircle2 * w,
6003 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
6004 pathIn->pts[i0].y);
6005 pathOut->close();
6007 i0 = j0;
6008 i1 = j1;
6009 continue;
6011 last = pathIn->flags[j1] & splashPathLast;
6012 if (last) {
6013 k0 = subpathStart1 + 1;
6014 } else {
6015 k0 = j1 + 1;
6017 for (k1 = k0;
6018 !(pathIn->flags[k1] & splashPathLast) &&
6019 k1 + 1 < pathIn->length &&
6020 pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
6021 pathIn->pts[k1+1].y == pathIn->pts[k1].y;
6022 ++k1) ;
6024 // compute the deltas for segment (i1, j0)
6025 #if USE_FIXEDPOINT
6026 // the 1/d value can be small, which introduces significant
6027 // inaccuracies in fixed point mode
6028 d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6029 pathIn->pts[j0].x, pathIn->pts[j0].y);
6030 dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
6031 dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
6032 #else
6033 d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6034 pathIn->pts[j0].x, pathIn->pts[j0].y);
6035 dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
6036 dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
6037 #endif
6038 wdx = (SplashCoord)0.5 * w * dx;
6039 wdy = (SplashCoord)0.5 * w * dy;
6041 // draw the start cap
6042 pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
6043 if (i0 == subpathStart0) {
6044 firstPt = pathOut->length - 1;
6046 if (first && !closed) {
6047 switch (state->lineCap) {
6048 case splashLineCapButt:
6049 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6050 break;
6051 case splashLineCapRound:
6052 pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
6053 pathIn->pts[i0].y + wdx - bezierCircle * wdy,
6054 pathIn->pts[i0].x - wdx - bezierCircle * wdy,
6055 pathIn->pts[i0].y - wdy + bezierCircle * wdx,
6056 pathIn->pts[i0].x - wdx,
6057 pathIn->pts[i0].y - wdy);
6058 pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
6059 pathIn->pts[i0].y - wdy - bezierCircle * wdx,
6060 pathIn->pts[i0].x + wdy - bezierCircle * wdx,
6061 pathIn->pts[i0].y - wdx - bezierCircle * wdy,
6062 pathIn->pts[i0].x + wdy,
6063 pathIn->pts[i0].y - wdx);
6064 break;
6065 case splashLineCapProjecting:
6066 pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy,
6067 pathIn->pts[i0].y + wdx - wdy);
6068 pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
6069 pathIn->pts[i0].y - wdx - wdy);
6070 pathOut->lineTo(pathIn->pts[i0].x + wdy,
6071 pathIn->pts[i0].y - wdx);
6072 break;
6074 } else {
6075 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6078 // draw the left side of the segment rectangle
6079 left2 = pathOut->length - 1;
6080 pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
6082 // draw the end cap
6083 if (last && !closed) {
6084 switch (state->lineCap) {
6085 case splashLineCapButt:
6086 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6087 break;
6088 case splashLineCapRound:
6089 pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
6090 pathIn->pts[j0].y - wdx + bezierCircle * wdy,
6091 pathIn->pts[j0].x + wdx + bezierCircle * wdy,
6092 pathIn->pts[j0].y + wdy - bezierCircle * wdx,
6093 pathIn->pts[j0].x + wdx,
6094 pathIn->pts[j0].y + wdy);
6095 pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
6096 pathIn->pts[j0].y + wdy + bezierCircle * wdx,
6097 pathIn->pts[j0].x - wdy + bezierCircle * wdx,
6098 pathIn->pts[j0].y + wdx + bezierCircle * wdy,
6099 pathIn->pts[j0].x - wdy,
6100 pathIn->pts[j0].y + wdx);
6101 break;
6102 case splashLineCapProjecting:
6103 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
6104 pathIn->pts[j0].y - wdx + wdy);
6105 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
6106 pathIn->pts[j0].y + wdx + wdy);
6107 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6108 pathIn->pts[j0].y + wdx);
6109 break;
6111 } else {
6112 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6115 // draw the right side of the segment rectangle
6116 // (NB: if stroke adjustment is enabled, the closepath operation MUST
6117 // add a segment because this segment is used for a hint)
6118 right2 = pathOut->length - 1;
6119 pathOut->close(state->strokeAdjust);
6121 // draw the join
6122 join2 = pathOut->length;
6123 if (!last || closed) {
6125 // compute the deltas for segment (j1, k0)
6126 #if USE_FIXEDPOINT
6127 // the 1/d value can be small, which introduces significant
6128 // inaccuracies in fixed point mode
6129 d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6130 pathIn->pts[k0].x, pathIn->pts[k0].y);
6131 dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
6132 dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
6133 #else
6134 d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6135 pathIn->pts[k0].x, pathIn->pts[k0].y);
6136 dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
6137 dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
6138 #endif
6139 wdxNext = (SplashCoord)0.5 * w * dxNext;
6140 wdyNext = (SplashCoord)0.5 * w * dyNext;
6142 // compute the join parameters
6143 crossprod = dx * dyNext - dy * dxNext;
6144 dotprod = -(dx * dxNext + dy * dyNext);
6145 if (dotprod > 0.9999) {
6146 // avoid a divide-by-zero -- set miter to something arbitrary
6147 // such that sqrt(miter) will exceed miterLimit (and m is never
6148 // used in that situation)
6149 // (note: the comparison value (0.9999) has to be less than
6150 // 1-epsilon, where epsilon is the smallest value
6151 // representable in the fixed point format)
6152 miter = (state->miterLimit + 1) * (state->miterLimit + 1);
6153 m = 0;
6154 } else {
6155 miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
6156 if (miter < 1) {
6157 // this can happen because of floating point inaccuracies
6158 miter = 1;
6160 m = splashSqrt(miter - 1);
6163 // round join
6164 if (state->lineJoin == splashLineJoinRound) {
6165 pathOut->moveTo(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);
6179 pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6180 pathIn->pts[j0].y - bezierCircle2 * w,
6181 pathIn->pts[j0].x - bezierCircle2 * w,
6182 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6183 pathIn->pts[j0].x,
6184 pathIn->pts[j0].y - (SplashCoord)0.5 * w);
6185 pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w,
6186 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6187 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6188 pathIn->pts[j0].y - bezierCircle2 * w,
6189 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6190 pathIn->pts[j0].y);
6192 } else {
6193 pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
6195 // angle < 180
6196 if (crossprod < 0) {
6197 pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
6198 pathIn->pts[j0].y + wdxNext);
6199 // miter join inside limit
6200 if (state->lineJoin == splashLineJoinMiter &&
6201 splashSqrt(miter) <= state->miterLimit) {
6202 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
6203 pathIn->pts[j0].y + wdx + wdy * m);
6204 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6205 pathIn->pts[j0].y + wdx);
6206 // bevel join or miter join outside limit
6207 } else {
6208 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6209 pathIn->pts[j0].y + wdx);
6212 // angle >= 180
6213 } else {
6214 pathOut->lineTo(pathIn->pts[j0].x + wdy,
6215 pathIn->pts[j0].y - wdx);
6216 // miter join inside limit
6217 if (state->lineJoin == splashLineJoinMiter &&
6218 splashSqrt(miter) <= state->miterLimit) {
6219 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
6220 pathIn->pts[j0].y - wdx + wdy * m);
6221 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6222 pathIn->pts[j0].y - wdxNext);
6223 // bevel join or miter join outside limit
6224 } else {
6225 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6226 pathIn->pts[j0].y - wdxNext);
6231 pathOut->close();
6234 // add stroke adjustment hints
6235 if (state->strokeAdjust) {
6236 if (seg == 0 && !closed) {
6237 if (state->lineCap == splashLineCapButt) {
6238 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6239 firstPt, firstPt + 1);
6240 if (last) {
6241 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6242 left2 + 1, left2 + 2);
6244 } else if (state->lineCap == splashLineCapProjecting) {
6245 if (last) {
6246 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6247 firstPt + 1, firstPt + 2);
6248 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6249 left2 + 2, left2 + 3);
6250 } else {
6251 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1,
6252 firstPt + 1, firstPt + 2);
6256 if (seg >= 1) {
6257 if (seg >= 2) {
6258 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6259 pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
6260 } else {
6261 pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
6263 pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
6265 left0 = left1;
6266 left1 = left2;
6267 right0 = right1;
6268 right1 = right2;
6269 join0 = join1;
6270 join1 = join2;
6271 if (seg == 0) {
6272 leftFirst = left2;
6273 rightFirst = right2;
6275 if (last) {
6276 if (seg >= 2) {
6277 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6278 pathOut->addStrokeAdjustHint(left1, right1,
6279 join0, pathOut->length - 1);
6280 } else {
6281 pathOut->addStrokeAdjustHint(left1, right1,
6282 firstPt, pathOut->length - 1);
6284 if (closed) {
6285 pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
6286 pathOut->addStrokeAdjustHint(left1, right1,
6287 rightFirst + 1, rightFirst + 1);
6288 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6289 left1 + 1, right1);
6290 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6291 join1, pathOut->length - 1);
6293 if (!closed && seg > 0) {
6294 if (state->lineCap == splashLineCapButt) {
6295 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1,
6296 left1 + 1, left1 + 2);
6297 } else if (state->lineCap == splashLineCapProjecting) {
6298 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2,
6299 left1 + 2, left1 + 3);
6305 i0 = j0;
6306 i1 = j1;
6307 ++seg;
6310 if (pathIn != path) {
6311 delete pathIn;
6314 return pathOut;
6317 void Splash::dumpPath(SplashPath *path) {
6318 int i;
6320 for (i = 0; i < path->length; ++i) {
6321 printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
6322 i, (double)path->pts[i].x, (double)path->pts[i].y,
6323 (path->flags[i] & splashPathFirst) ? " first" : "",
6324 (path->flags[i] & splashPathLast) ? " last" : "",
6325 (path->flags[i] & splashPathClosed) ? " closed" : "",
6326 (path->flags[i] & splashPathCurve) ? " curve" : "");
6330 void Splash::dumpXPath(SplashXPath *path) {
6331 int i;
6333 for (i = 0; i < path->length; ++i) {
6334 printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n",
6335 i, (double)path->segs[i].x0, (double)path->segs[i].y0,
6336 (double)path->segs[i].x1, (double)path->segs[i].y1,
6337 (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
6338 (path->segs[i].flags & splashXPathVert) ? "V" : " ",
6339 (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
6343 SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox,
6344 SplashPattern *pattern) {
6345 SplashPipe pipe;
6346 SplashXPath *xPath;
6347 SplashXPathScanner *scanner;
6348 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
6349 SplashClipResult clipRes;
6351 if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure
6352 return splashErrGeneric;
6354 if (path->length == 0) {
6355 return splashErrEmptyPath;
6357 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
6358 if (vectorAntialias) {
6359 xPath->aaScale();
6361 xPath->sort();
6362 yMinI = state->clip->getYMinI();
6363 yMaxI = state->clip->getYMaxI();
6364 if (vectorAntialias && !inShading) {
6365 yMinI = yMinI * splashAASize;
6366 yMaxI = (yMaxI + 1) * splashAASize - 1;
6368 scanner = new SplashXPathScanner(xPath, gFalse, yMinI, yMaxI);
6370 // get the min and max x and y values
6371 if (vectorAntialias) {
6372 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
6373 } else {
6374 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
6377 // check clipping
6378 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
6379 // limit the y range
6380 if (yMinI < state->clip->getYMinI()) {
6381 yMinI = state->clip->getYMinI();
6383 if (yMaxI > state->clip->getYMaxI()) {
6384 yMaxI = state->clip->getYMaxI();
6387 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, gFalse);
6389 // draw the spans
6390 if (vectorAntialias) {
6391 for (y = yMinI; y <= yMaxI; ++y) {
6392 scanner->renderAALine(aaBuf, &x0, &x1, y);
6393 if (clipRes != splashClipAllInside) {
6394 state->clip->clipAALine(aaBuf, &x0, &x1, y);
6396 #if splashAASize == 4
6397 if (!hasBBox && y > yMinI && y < yMaxI) {
6398 // correct shape on left side if clip is
6399 // vertical through the middle of shading:
6400 Guchar *p0, *p1, *p2, *p3;
6401 Guchar c1, c2, c3, c4;
6402 p0 = aaBuf->getDataPtr() + (x0 >> 1);
6403 p1 = p0 + aaBuf->getRowSize();
6404 p2 = p1 + aaBuf->getRowSize();
6405 p3 = p2 + aaBuf->getRowSize();
6406 if (x0 & 1) {
6407 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6408 } else {
6409 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6411 if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03
6412 && c1 == c2 && c2 == c3 && c3 == c4 &&
6413 pattern->testPosition(x0 - 1, y) )
6415 Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0;
6416 *p0 |= shapeCorrection;
6417 *p1 |= shapeCorrection;
6418 *p2 |= shapeCorrection;
6419 *p3 |= shapeCorrection;
6421 // correct shape on right side if clip is
6422 // through the middle of shading:
6423 p0 = aaBuf->getDataPtr() + (x1 >> 1);
6424 p1 = p0 + aaBuf->getRowSize();
6425 p2 = p1 + aaBuf->getRowSize();
6426 p3 = p2 + aaBuf->getRowSize();
6427 if (x1 & 1) {
6428 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6429 } else {
6430 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6433 if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c
6434 && c1 == c2 && c2 == c3 && c3 == c4 &&
6435 pattern->testPosition(x1 + 1, y) )
6437 Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0;
6438 *p0 |= shapeCorrection;
6439 *p1 |= shapeCorrection;
6440 *p2 |= shapeCorrection;
6441 *p3 |= shapeCorrection;
6444 #endif
6445 drawAALine(&pipe, x0, x1, y);
6447 } else {
6448 SplashClipResult clipRes2;
6449 for (y = yMinI; y <= yMaxI; ++y) {
6450 while (scanner->getNextSpan(y, &x0, &x1)) {
6451 if (clipRes == splashClipAllInside) {
6452 drawSpan(&pipe, x0, x1, y, gTrue);
6453 } else {
6454 // limit the x range
6455 if (x0 < state->clip->getXMinI()) {
6456 x0 = state->clip->getXMinI();
6458 if (x1 > state->clip->getXMaxI()) {
6459 x1 = state->clip->getXMaxI();
6461 clipRes2 = state->clip->testSpan(x0, x1, y);
6462 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
6468 opClipRes = clipRes;
6470 delete scanner;
6471 delete xPath;
6472 return splashOk;