1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
6
7 //========================================================================
8 //
9 // Modified under the Poppler project - http://poppler.freedesktop.org
10 //
11 // All changes made under the Poppler project to this file are licensed
12 // under GPL version 2 or later
13 //
14 // Copyright (C) 2005-2016 Albert Astals Cid <aacid@kde.org>
15 // Copyright (C) 2005 Marco Pesenti Gritti <mpg@redhat.com>
16 // Copyright (C) 2010-2016 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>
22 //
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
25 //
26 //========================================================================
27
28 #include <config.h>
29
30 #ifdef USE_GCC_PRAGMAS
31 #pragma implementation
32 #endif
33
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>
56
57 //------------------------------------------------------------------------
58
59 #define splashAAGamma 1.5
60
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))
65
66 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)67 static inline Guchar div255(int x) {
68 return (Guchar)((x + (x >> 8) + 0x80) >> 8);
69 }
70
71 // Clip x to lie in [0, 255].
clip255(int x)72 static inline Guchar clip255(int x) {
73 return x < 0 ? 0 : x > 255 ? 255 : x;
74 }
75
76 template<typename T>
Guswap(T & a,T & b)77 inline void Guswap( T&a, T&b ) { T tmp = a; a=b; b=tmp; }
78
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;
89 }
90 static inline int imgCoordMungeUpper(SplashCoord x) {
91 return splashCeil(x + 0.5) - 1;
92 }
93 #else
imgCoordMungeLower(SplashCoord x)94 static inline int imgCoordMungeLower(SplashCoord x) {
95 return splashFloor(x);
96 }
imgCoordMungeUpper(SplashCoord x)97 static inline int imgCoordMungeUpper(SplashCoord x) {
98 return splashFloor(x) + 1;
99 }
imgCoordMungeLowerC(SplashCoord x,GBool glyphMode)100 static inline int imgCoordMungeLowerC(SplashCoord x, GBool glyphMode) {
101 return glyphMode ? (splashCeil(x + 0.5) - 1) : splashFloor(x);
102 }
imgCoordMungeUpperC(SplashCoord x,GBool glyphMode)103 static inline int imgCoordMungeUpperC(SplashCoord x, GBool glyphMode) {
104 return glyphMode ? (splashCeil(x + 0.5) - 1) : (splashFloor(x) + 1);
105 }
106 #endif
107
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
118 };
119
120 //------------------------------------------------------------------------
121 // SplashPipe
122 //------------------------------------------------------------------------
123
124 #define splashPipeMaxStages 9
125
126 struct SplashPipe {
127 // pixel coordinates
128 int x, y;
129
130 // source pattern
131 SplashPattern *pattern;
132
133 // source alpha and color
134 Guchar aInput;
135 GBool usesShape;
136 SplashColorPtr cSrc;
137 SplashColor cSrcVal;
138
139 // non-isolated group alpha0
140 Guchar *alpha0Ptr;
141
142 // knockout groups
143 GBool knockout;
144 Guchar knockoutOpacity;
145
146 // soft mask
147 SplashColorPtr softMaskPtr;
148
149 // destination alpha and color
150 SplashColorPtr destColorPtr;
151 int destColorMask;
152 Guchar *destAlphaPtr;
153
154 // shape
155 Guchar shape;
156
157 // result alpha and color
158 GBool noTransparency;
159 SplashPipeResultColorCtrl resultColorCtrl;
160
161 // non-isolated group correction
162 GBool nonIsolatedGroup;
163
164 // the "run" function
165 void (Splash::*run)(SplashPipe *pipe);
166 };
167
168 SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
169 splashPipeResultColorNoAlphaBlendMono,
170 splashPipeResultColorNoAlphaBlendMono,
171 splashPipeResultColorNoAlphaBlendRGB,
172 splashPipeResultColorNoAlphaBlendRGB,
173 splashPipeResultColorNoAlphaBlendRGB
174 #if SPLASH_CMYK
175 ,
176 splashPipeResultColorNoAlphaBlendCMYK,
177 splashPipeResultColorNoAlphaBlendDeviceN
178 #endif
179 };
180
181 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
182 splashPipeResultColorAlphaNoBlendMono,
183 splashPipeResultColorAlphaNoBlendMono,
184 splashPipeResultColorAlphaNoBlendRGB,
185 splashPipeResultColorAlphaNoBlendRGB,
186 splashPipeResultColorAlphaNoBlendRGB
187 #if SPLASH_CMYK
188 ,
189 splashPipeResultColorAlphaNoBlendCMYK,
190 splashPipeResultColorAlphaNoBlendDeviceN
191 #endif
192 };
193
194 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
195 splashPipeResultColorAlphaBlendMono,
196 splashPipeResultColorAlphaBlendMono,
197 splashPipeResultColorAlphaBlendRGB,
198 splashPipeResultColorAlphaBlendRGB,
199 splashPipeResultColorAlphaBlendRGB
200 #if SPLASH_CMYK
201 ,
202 splashPipeResultColorAlphaBlendCMYK,
203 splashPipeResultColorAlphaBlendDeviceN
204 #endif
205 };
206
207 //------------------------------------------------------------------------
208
blendXor(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)209 static void blendXor(SplashColorPtr src, SplashColorPtr dest,
210 SplashColorPtr blend, SplashColorMode cm) {
211 int i;
212
213 for (i = 0; i < splashColorModeNComps[cm]; ++i) {
214 blend[i] = src[i] ^ dest[i];
215 }
216 }
217
218 //------------------------------------------------------------------------
219 // modified region
220 //------------------------------------------------------------------------
221
clearModRegion()222 void Splash::clearModRegion() {
223 modXMin = bitmap->getWidth();
224 modYMin = bitmap->getHeight();
225 modXMax = -1;
226 modYMax = -1;
227 }
228
updateModX(int x)229 inline void Splash::updateModX(int x) {
230 if (x < modXMin) {
231 modXMin = x;
232 }
233 if (x > modXMax) {
234 modXMax = x;
235 }
236 }
237
updateModY(int y)238 inline void Splash::updateModY(int y) {
239 if (y < modYMin) {
240 modYMin = y;
241 }
242 if (y > modYMax) {
243 modYMax = y;
244 }
245 }
246
247 //------------------------------------------------------------------------
248 // pipeline
249 //------------------------------------------------------------------------
250
pipeInit(SplashPipe * pipe,int x,int y,SplashPattern * pattern,SplashColorPtr cSrc,Guchar aInput,GBool usesShape,GBool nonIsolatedGroup,GBool knockout,Guchar knockoutOpacity)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;
258
259 // source color
260 if (pattern) {
261 if (pattern->isStatic()) {
262 pattern->getColor(x, y, pipe->cSrcVal);
263 } else {
264 pipe->pattern = pattern;
265 }
266 pipe->cSrc = pipe->cSrcVal;
267 } else {
268 pipe->cSrc = cSrc;
269 }
270
271 // source alpha
272 pipe->aInput = aInput;
273 pipe->usesShape = usesShape;
274 pipe->shape = 0;
275
276 // knockout
277 pipe->knockout = knockout;
278 pipe->knockoutOpacity = knockoutOpacity;
279
280 // result alpha
281 if (aInput == 255 && !state->softMask && !usesShape &&
282 !state->inNonIsolatedGroup && !nonIsolatedGroup) {
283 pipe->noTransparency = gTrue;
284 } else {
285 pipe->noTransparency = gFalse;
286 }
287
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];
296 }
297
298 // non-isolated group correction
299 pipe->nonIsolatedGroup = nonIsolatedGroup;
300
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
320 }
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
341 }
342 }
343 }
344
345 // general case
pipeRun(SplashPipe * pipe)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
356
357 //----- source color
358
359 // static pattern: handled in pipeInit
360 // fixed color: handled in pipeInit
361
362 // dynamic pattern
363 if (pipe->pattern) {
364 if (!pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal)) {
365 pipeIncX(pipe);
366 return;
367 }
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;
374 }
375 if (pipe->cSrcVal[1] == 0) {
376 mask &= ~2;
377 }
378 if (pipe->cSrcVal[2] == 0) {
379 mask &= ~4;
380 }
381 if (pipe->cSrcVal[3] == 0) {
382 mask &= ~8;
383 }
384 state->overprintMask = mask;
385 }
386 }
387 #endif
388 }
389
390 if (pipe->noTransparency && !state->blendFunc) {
391
392 //----- write destination pixel
393
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;
401 }
402 if (!(pipe->destColorMask >>= 1)) {
403 pipe->destColorMask = 0x80;
404 ++pipe->destColorPtr;
405 }
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]];
432 }
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]];
437 }
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]];
442 }
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]];
447 }
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]];
455 }
456 mask <<= 1;
457 }
458 pipe->destColorPtr += (SPOT_NCOMPS+4);
459 break;
460 #endif
461 }
462 if (pipe->destAlphaPtr) {
463 *pipe->destAlphaPtr++ = 255;
464 }
465
466 } else {
467
468 //----- read destination pixel
469
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
495 }
496 } else {
497 destColorPtr = pipe->destColorPtr;
498 }
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
534 }
535 if (pipe->destAlphaPtr) {
536 aDest = *pipe->destAlphaPtr;
537 } else {
538 aDest = 0xff;
539 }
540
541 //----- source alpha
542
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++);
549 }
550 } else if (pipe->usesShape) {
551 aSrc = div255(pipe->aInput * pipe->shape);
552 } else {
553 aSrc = pipe->aInput;
554 }
555
556 //----- non-isolated group correction
557
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 // fall through
584 case splashModeRGB8:
585 case splashModeBGR8:
586 cSrcNonIso[2] = clip255(pipe->cSrc[2] +
587 ((pipe->cSrc[2] - cDest[2]) * t) / 255);
588 cSrcNonIso[1] = clip255(pipe->cSrc[1] +
589 ((pipe->cSrc[1] - cDest[1]) * t) / 255);
590 // fall through
591 case splashModeMono1:
592 case splashModeMono8:
593 cSrcNonIso[0] = clip255(pipe->cSrc[0] +
594 ((pipe->cSrc[0] - cDest[0]) * t) / 255);
595 break;
596 }
597 cSrc = cSrcNonIso;
598 // knockout: remove backdrop color
599 if (pipe->knockout && pipe->shape >= pipe->knockoutOpacity) {
600 aDest = 0;
601 }
602 }
603 } else {
604 cSrc = pipe->cSrc;
605 }
606
607 //----- blend function
608
609 if (state->blendFunc) {
610 #if SPLASH_CMYK
611 if (bitmap->mode == splashModeDeviceN8) {
612 for (int k = 4; k < 4 + SPOT_NCOMPS; k++) {
613 cBlend[k] = 0;
614 }
615 }
616 #endif
617 (*state->blendFunc)(cSrc, cDest, cBlend, bitmap->mode);
618 }
619
620 //----- result alpha and non-isolated group element correction
621
622 if (pipe->noTransparency) {
623 alphaI = alphaIm1 = aResult = 255;
624 } else {
625 aResult = aSrc + aDest - div255(aSrc * aDest);
626
627 // alphaI = alpha_i
628 // alphaIm1 = alpha_(i-1)
629 if (pipe->alpha0Ptr) {
630 alpha0 = *pipe->alpha0Ptr++;
631 alphaI = aResult + alpha0 - div255(aResult * alpha0);
632 alphaIm1 = alpha0 + aDest - div255(alpha0 * aDest);
633 } else {
634 alphaI = aResult;
635 alphaIm1 = aDest;
636 }
637 }
638
639 //----- result color
640
641 cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
642
643 switch (pipe->resultColorCtrl) {
644
645 case splashPipeResultColorNoAlphaBlendMono:
646 cResult0 = state->grayTransfer[div255((255 - aDest) * cSrc[0] +
647 aDest * cBlend[0])];
648 break;
649 case splashPipeResultColorNoAlphaBlendRGB:
650 cResult0 = state->rgbTransferR[div255((255 - aDest) * cSrc[0] +
651 aDest * cBlend[0])];
652 cResult1 = state->rgbTransferG[div255((255 - aDest) * cSrc[1] +
653 aDest * cBlend[1])];
654 cResult2 = state->rgbTransferB[div255((255 - aDest) * cSrc[2] +
655 aDest * cBlend[2])];
656 break;
657 #if SPLASH_CMYK
658 case splashPipeResultColorNoAlphaBlendCMYK:
659 cResult0 = state->cmykTransferC[div255((255 - aDest) * cSrc[0] +
660 aDest * cBlend[0])];
661 cResult1 = state->cmykTransferM[div255((255 - aDest) * cSrc[1] +
662 aDest * cBlend[1])];
663 cResult2 = state->cmykTransferY[div255((255 - aDest) * cSrc[2] +
664 aDest * cBlend[2])];
665 cResult3 = state->cmykTransferK[div255((255 - aDest) * cSrc[3] +
666 aDest * cBlend[3])];
667 break;
668 case splashPipeResultColorNoAlphaBlendDeviceN:
669 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
670 cResult[cp] = state->deviceNTransfer[cp][div255((255 - aDest) * cSrc[cp] +
671 aDest * cBlend[cp])];
672 break;
673 #endif
674
675 case splashPipeResultColorAlphaNoBlendMono:
676 if (alphaI == 0) {
677 cResult0 = 0;
678 } else {
679 cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
680 aSrc * cSrc[0]) / alphaI];
681 }
682 break;
683 case splashPipeResultColorAlphaNoBlendRGB:
684 if (alphaI == 0) {
685 cResult0 = 0;
686 cResult1 = 0;
687 cResult2 = 0;
688 } else {
689 cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
690 aSrc * cSrc[0]) / alphaI];
691 cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
692 aSrc * cSrc[1]) / alphaI];
693 cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
694 aSrc * cSrc[2]) / alphaI];
695 }
696 break;
697 #if SPLASH_CMYK
698 case splashPipeResultColorAlphaNoBlendCMYK:
699 if (alphaI == 0) {
700 cResult0 = 0;
701 cResult1 = 0;
702 cResult2 = 0;
703 cResult3 = 0;
704 } else {
705 cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
706 aSrc * cSrc[0]) / alphaI];
707 cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
708 aSrc * cSrc[1]) / alphaI];
709 cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
710 aSrc * cSrc[2]) / alphaI];
711 cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
712 aSrc * cSrc[3]) / alphaI];
713 }
714 break;
715 case splashPipeResultColorAlphaNoBlendDeviceN:
716 if (alphaI == 0) {
717 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
718 cResult[cp] = 0;
719 } else {
720 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
721 cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
722 aSrc * cSrc[cp]) / alphaI];
723 }
724 break;
725 #endif
726
727 case splashPipeResultColorAlphaBlendMono:
728 if (alphaI == 0) {
729 cResult0 = 0;
730 } else {
731 cResult0 = state->grayTransfer[((alphaI - aSrc) * cDest[0] +
732 aSrc * ((255 - alphaIm1) * cSrc[0] +
733 alphaIm1 * cBlend[0]) / 255) /
734 alphaI];
735 }
736 break;
737 case splashPipeResultColorAlphaBlendRGB:
738 if (alphaI == 0) {
739 cResult0 = 0;
740 cResult1 = 0;
741 cResult2 = 0;
742 } else {
743 cResult0 = state->rgbTransferR[((alphaI - aSrc) * cDest[0] +
744 aSrc * ((255 - alphaIm1) * cSrc[0] +
745 alphaIm1 * cBlend[0]) / 255) /
746 alphaI];
747 cResult1 = state->rgbTransferG[((alphaI - aSrc) * cDest[1] +
748 aSrc * ((255 - alphaIm1) * cSrc[1] +
749 alphaIm1 * cBlend[1]) / 255) /
750 alphaI];
751 cResult2 = state->rgbTransferB[((alphaI - aSrc) * cDest[2] +
752 aSrc * ((255 - alphaIm1) * cSrc[2] +
753 alphaIm1 * cBlend[2]) / 255) /
754 alphaI];
755 }
756 break;
757 #if SPLASH_CMYK
758 case splashPipeResultColorAlphaBlendCMYK:
759 if (alphaI == 0) {
760 cResult0 = 0;
761 cResult1 = 0;
762 cResult2 = 0;
763 cResult3 = 0;
764 } else {
765 cResult0 = state->cmykTransferC[((alphaI - aSrc) * cDest[0] +
766 aSrc * ((255 - alphaIm1) * cSrc[0] +
767 alphaIm1 * cBlend[0]) / 255) /
768 alphaI];
769 cResult1 = state->cmykTransferM[((alphaI - aSrc) * cDest[1] +
770 aSrc * ((255 - alphaIm1) * cSrc[1] +
771 alphaIm1 * cBlend[1]) / 255) /
772 alphaI];
773 cResult2 = state->cmykTransferY[((alphaI - aSrc) * cDest[2] +
774 aSrc * ((255 - alphaIm1) * cSrc[2] +
775 alphaIm1 * cBlend[2]) / 255) /
776 alphaI];
777 cResult3 = state->cmykTransferK[((alphaI - aSrc) * cDest[3] +
778 aSrc * ((255 - alphaIm1) * cSrc[3] +
779 alphaIm1 * cBlend[3]) / 255) /
780 alphaI];
781 }
782 break;
783 case splashPipeResultColorAlphaBlendDeviceN:
784 if (alphaI == 0) {
785 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
786 cResult[cp] = 0;
787 } else {
788 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
789 cResult[cp] = state->deviceNTransfer[cp][((alphaI - aSrc) * cDest[cp] +
790 aSrc * ((255 - alphaIm1) * cSrc[cp] +
791 alphaIm1 * cBlend[cp]) / 255) /
792 alphaI];
793 }
794 break;
795 #endif
796 }
797
798 //----- write destination pixel
799
800 switch (bitmap->mode) {
801 case splashModeMono1:
802 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
803 *pipe->destColorPtr |= pipe->destColorMask;
804 } else {
805 *pipe->destColorPtr &= ~pipe->destColorMask;
806 }
807 if (!(pipe->destColorMask >>= 1)) {
808 pipe->destColorMask = 0x80;
809 ++pipe->destColorPtr;
810 }
811 break;
812 case splashModeMono8:
813 *pipe->destColorPtr++ = cResult0;
814 break;
815 case splashModeRGB8:
816 *pipe->destColorPtr++ = cResult0;
817 *pipe->destColorPtr++ = cResult1;
818 *pipe->destColorPtr++ = cResult2;
819 break;
820 case splashModeXBGR8:
821 *pipe->destColorPtr++ = cResult2;
822 *pipe->destColorPtr++ = cResult1;
823 *pipe->destColorPtr++ = cResult0;
824 *pipe->destColorPtr++ = 255;
825 break;
826 case splashModeBGR8:
827 *pipe->destColorPtr++ = cResult2;
828 *pipe->destColorPtr++ = cResult1;
829 *pipe->destColorPtr++ = cResult0;
830 break;
831 #if SPLASH_CMYK
832 case splashModeCMYK8:
833 if (state->overprintMask & 1) {
834 pipe->destColorPtr[0] = (state->overprintAdditive) ?
835 std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
836 cResult0;
837 }
838 if (state->overprintMask & 2) {
839 pipe->destColorPtr[1] = (state->overprintAdditive) ?
840 std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
841 cResult1;
842 }
843 if (state->overprintMask & 4) {
844 pipe->destColorPtr[2] = (state->overprintAdditive) ?
845 std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
846 cResult2;
847 }
848 if (state->overprintMask & 8) {
849 pipe->destColorPtr[3] = (state->overprintAdditive) ?
850 std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
851 cResult3;
852 }
853 pipe->destColorPtr += 4;
854 break;
855 case splashModeDeviceN8:
856 mask = 1;
857 for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
858 if (state->overprintMask & mask) {
859 pipe->destColorPtr[cp] = cResult[cp];
860 }
861 mask <<=1;
862 }
863 pipe->destColorPtr += (SPOT_NCOMPS+4);
864 break;
865 #endif
866 }
867 if (pipe->destAlphaPtr) {
868 *pipe->destAlphaPtr++ = aResult;
869 }
870
871 }
872
873 ++pipe->x;
874 }
875
876 // special case:
877 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
878 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr) {
pipeRunSimpleMono1(SplashPipe * pipe)879 void Splash::pipeRunSimpleMono1(SplashPipe *pipe) {
880 Guchar cResult0;
881
882 //----- write destination pixel
883 cResult0 = state->grayTransfer[pipe->cSrc[0]];
884 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
885 *pipe->destColorPtr |= pipe->destColorMask;
886 } else {
887 *pipe->destColorPtr &= ~pipe->destColorMask;
888 }
889 if (!(pipe->destColorMask >>= 1)) {
890 pipe->destColorMask = 0x80;
891 ++pipe->destColorPtr;
892 }
893
894 ++pipe->x;
895 }
896
897 // special case:
898 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
899 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr) {
pipeRunSimpleMono8(SplashPipe * pipe)900 void Splash::pipeRunSimpleMono8(SplashPipe *pipe) {
901 //----- write destination pixel
902 *pipe->destColorPtr++ = state->grayTransfer[pipe->cSrc[0]];
903 *pipe->destAlphaPtr++ = 255;
904
905 ++pipe->x;
906 }
907
908 // special case:
909 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
910 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr) {
pipeRunSimpleRGB8(SplashPipe * pipe)911 void Splash::pipeRunSimpleRGB8(SplashPipe *pipe) {
912 //----- write destination pixel
913 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
914 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
915 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
916 *pipe->destAlphaPtr++ = 255;
917
918 ++pipe->x;
919 }
920
921 // special case:
922 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
923 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr) {
pipeRunSimpleXBGR8(SplashPipe * pipe)924 void Splash::pipeRunSimpleXBGR8(SplashPipe *pipe) {
925 //----- write destination pixel
926 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
927 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
928 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
929 *pipe->destColorPtr++ = 255;
930 *pipe->destAlphaPtr++ = 255;
931
932 ++pipe->x;
933 }
934
935 // special case:
936 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
937 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr) {
pipeRunSimpleBGR8(SplashPipe * pipe)938 void Splash::pipeRunSimpleBGR8(SplashPipe *pipe) {
939 //----- write destination pixel
940 *pipe->destColorPtr++ = state->rgbTransferB[pipe->cSrc[2]];
941 *pipe->destColorPtr++ = state->rgbTransferG[pipe->cSrc[1]];
942 *pipe->destColorPtr++ = state->rgbTransferR[pipe->cSrc[0]];
943 *pipe->destAlphaPtr++ = 255;
944
945 ++pipe->x;
946 }
947
948 #if SPLASH_CMYK
949 // special case:
950 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
951 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr) {
pipeRunSimpleCMYK8(SplashPipe * pipe)952 void Splash::pipeRunSimpleCMYK8(SplashPipe *pipe) {
953 //----- write destination pixel
954 if (state->overprintMask & 1) {
955 pipe->destColorPtr[0] = (state->overprintAdditive) ?
956 std::min<int>(pipe->destColorPtr[0] + state->cmykTransferC[pipe->cSrc[0]], 255) :
957 state->cmykTransferC[pipe->cSrc[0]];
958 }
959 if (state->overprintMask & 2) {
960 pipe->destColorPtr[1] = (state->overprintAdditive) ?
961 std::min<int>(pipe->destColorPtr[1] + state->cmykTransferM[pipe->cSrc[1]], 255) :
962 state->cmykTransferM[pipe->cSrc[1]];
963 }
964 if (state->overprintMask & 4) {
965 pipe->destColorPtr[2] = (state->overprintAdditive) ?
966 std::min<int>(pipe->destColorPtr[2] + state->cmykTransferY[pipe->cSrc[2]], 255) :
967 state->cmykTransferY[pipe->cSrc[2]];
968 }
969 if (state->overprintMask & 8) {
970 pipe->destColorPtr[3] = (state->overprintAdditive) ?
971 std::min<int>(pipe->destColorPtr[3] + state->cmykTransferK[pipe->cSrc[3]], 255) :
972 state->cmykTransferK[pipe->cSrc[3]];
973 }
974 pipe->destColorPtr += 4;
975 *pipe->destAlphaPtr++ = 255;
976
977 ++pipe->x;
978 }
979
980 // special case:
981 // !pipe->pattern && pipe->noTransparency && !state->blendFunc &&
982 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr) {
pipeRunSimpleDeviceN8(SplashPipe * pipe)983 void Splash::pipeRunSimpleDeviceN8(SplashPipe *pipe) {
984 //----- write destination pixel
985 int mask = 1;
986 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++) {
987 if (state->overprintMask & mask) {
988 pipe->destColorPtr[cp] = state->deviceNTransfer[cp][pipe->cSrc[cp]];
989 }
990 mask <<=1;
991 }
992 pipe->destColorPtr += (SPOT_NCOMPS+4);
993 *pipe->destAlphaPtr++ = 255;
994
995 ++pipe->x;
996 }
997 #endif
998
999 // special case:
1000 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1001 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1002 // !pipe->nonIsolatedGroup &&
1003 // bitmap->mode == splashModeMono1 && !pipe->destAlphaPtr
pipeRunAAMono1(SplashPipe * pipe)1004 void Splash::pipeRunAAMono1(SplashPipe *pipe) {
1005 Guchar aSrc;
1006 SplashColor cDest;
1007 Guchar cResult0;
1008
1009 //----- read destination pixel
1010 cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
1011
1012 //----- source alpha
1013 aSrc = div255(pipe->aInput * pipe->shape);
1014
1015 //----- result color
1016 // note: aDest = alpha2 = aResult = 0xff
1017 cResult0 = state->grayTransfer[(Guchar)div255((0xff - aSrc) * cDest[0] +
1018 aSrc * pipe->cSrc[0])];
1019
1020 //----- write destination pixel
1021 if (state->screen->test(pipe->x, pipe->y, cResult0)) {
1022 *pipe->destColorPtr |= pipe->destColorMask;
1023 } else {
1024 *pipe->destColorPtr &= ~pipe->destColorMask;
1025 }
1026 if (!(pipe->destColorMask >>= 1)) {
1027 pipe->destColorMask = 0x80;
1028 ++pipe->destColorPtr;
1029 }
1030
1031 ++pipe->x;
1032 }
1033
1034 // special case:
1035 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1036 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1037 // !pipe->nonIsolatedGroup &&
1038 // bitmap->mode == splashModeMono8 && pipe->destAlphaPtr
pipeRunAAMono8(SplashPipe * pipe)1039 void Splash::pipeRunAAMono8(SplashPipe *pipe) {
1040 Guchar aSrc, aDest, alpha2, aResult;
1041 SplashColor cDest;
1042 Guchar cResult0;
1043
1044 //----- read destination pixel
1045 cDest[0] = *pipe->destColorPtr;
1046 aDest = *pipe->destAlphaPtr;
1047
1048 //----- source alpha
1049 aSrc = div255(pipe->aInput * pipe->shape);
1050
1051 //----- result alpha and non-isolated group element correction
1052 aResult = aSrc + aDest - div255(aSrc * aDest);
1053 alpha2 = aResult;
1054
1055 //----- result color
1056 if (alpha2 == 0) {
1057 cResult0 = 0;
1058 } else {
1059 cResult0 = state->grayTransfer[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1060 aSrc * pipe->cSrc[0]) / alpha2)];
1061 }
1062
1063 //----- write destination pixel
1064 *pipe->destColorPtr++ = cResult0;
1065 *pipe->destAlphaPtr++ = aResult;
1066
1067 ++pipe->x;
1068 }
1069
1070 // special case:
1071 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1072 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1073 // !pipe->nonIsolatedGroup &&
1074 // bitmap->mode == splashModeRGB8 && pipe->destAlphaPtr
pipeRunAARGB8(SplashPipe * pipe)1075 void Splash::pipeRunAARGB8(SplashPipe *pipe) {
1076 Guchar aSrc, aDest, alpha2, aResult;
1077 SplashColor cDest;
1078 Guchar cResult0, cResult1, cResult2;
1079
1080 //----- read destination pixel
1081 cDest[0] = pipe->destColorPtr[0];
1082 cDest[1] = pipe->destColorPtr[1];
1083 cDest[2] = pipe->destColorPtr[2];
1084 aDest = *pipe->destAlphaPtr;
1085
1086 //----- source alpha
1087 aSrc = div255(pipe->aInput * pipe->shape);
1088
1089 //----- result alpha and non-isolated group element correction
1090 aResult = aSrc + aDest - div255(aSrc * aDest);
1091 alpha2 = aResult;
1092
1093 //----- result color
1094 if (alpha2 == 0) {
1095 cResult0 = 0;
1096 cResult1 = 0;
1097 cResult2 = 0;
1098 } else {
1099 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1100 aSrc * pipe->cSrc[0]) / alpha2)];
1101 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1102 aSrc * pipe->cSrc[1]) / alpha2)];
1103 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1104 aSrc * pipe->cSrc[2]) / alpha2)];
1105 }
1106
1107 //----- write destination pixel
1108 *pipe->destColorPtr++ = cResult0;
1109 *pipe->destColorPtr++ = cResult1;
1110 *pipe->destColorPtr++ = cResult2;
1111 *pipe->destAlphaPtr++ = aResult;
1112
1113 ++pipe->x;
1114 }
1115
1116 // special case:
1117 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1118 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1119 // !pipe->nonIsolatedGroup &&
1120 // bitmap->mode == splashModeXBGR8 && pipe->destAlphaPtr
pipeRunAAXBGR8(SplashPipe * pipe)1121 void Splash::pipeRunAAXBGR8(SplashPipe *pipe) {
1122 Guchar aSrc, aDest, alpha2, aResult;
1123 SplashColor cDest;
1124 Guchar cResult0, cResult1, cResult2;
1125
1126 //----- read destination pixel
1127 cDest[0] = pipe->destColorPtr[2];
1128 cDest[1] = pipe->destColorPtr[1];
1129 cDest[2] = pipe->destColorPtr[0];
1130 aDest = *pipe->destAlphaPtr;
1131
1132 //----- source alpha
1133 aSrc = div255(pipe->aInput * pipe->shape);
1134
1135 //----- result alpha and non-isolated group element correction
1136 aResult = aSrc + aDest - div255(aSrc * aDest);
1137 alpha2 = aResult;
1138
1139 //----- result color
1140 if (alpha2 == 0) {
1141 cResult0 = 0;
1142 cResult1 = 0;
1143 cResult2 = 0;
1144 } else {
1145 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1146 aSrc * pipe->cSrc[0]) / alpha2)];
1147 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1148 aSrc * pipe->cSrc[1]) / alpha2)];
1149 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1150 aSrc * pipe->cSrc[2]) / alpha2)];
1151 }
1152
1153 //----- write destination pixel
1154 *pipe->destColorPtr++ = cResult2;
1155 *pipe->destColorPtr++ = cResult1;
1156 *pipe->destColorPtr++ = cResult0;
1157 *pipe->destColorPtr++ = 255;
1158 *pipe->destAlphaPtr++ = aResult;
1159
1160 ++pipe->x;
1161 }
1162
1163 // special case:
1164 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1165 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1166 // !pipe->nonIsolatedGroup &&
1167 // bitmap->mode == splashModeBGR8 && pipe->destAlphaPtr
pipeRunAABGR8(SplashPipe * pipe)1168 void Splash::pipeRunAABGR8(SplashPipe *pipe) {
1169 Guchar aSrc, aDest, alpha2, aResult;
1170 SplashColor cDest;
1171 Guchar cResult0, cResult1, cResult2;
1172
1173 //----- read destination pixel
1174 cDest[0] = pipe->destColorPtr[2];
1175 cDest[1] = pipe->destColorPtr[1];
1176 cDest[2] = pipe->destColorPtr[0];
1177 aDest = *pipe->destAlphaPtr;
1178
1179 //----- source alpha
1180 aSrc = div255(pipe->aInput * pipe->shape);
1181
1182 //----- result alpha and non-isolated group element correction
1183 aResult = aSrc + aDest - div255(aSrc * aDest);
1184 alpha2 = aResult;
1185
1186 //----- result color
1187 if (alpha2 == 0) {
1188 cResult0 = 0;
1189 cResult1 = 0;
1190 cResult2 = 0;
1191 } else {
1192 cResult0 = state->rgbTransferR[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1193 aSrc * pipe->cSrc[0]) / alpha2)];
1194 cResult1 = state->rgbTransferG[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1195 aSrc * pipe->cSrc[1]) / alpha2)];
1196 cResult2 = state->rgbTransferB[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1197 aSrc * pipe->cSrc[2]) / alpha2)];
1198 }
1199
1200 //----- write destination pixel
1201 *pipe->destColorPtr++ = cResult2;
1202 *pipe->destColorPtr++ = cResult1;
1203 *pipe->destColorPtr++ = cResult0;
1204 *pipe->destAlphaPtr++ = aResult;
1205
1206 ++pipe->x;
1207 }
1208
1209 #if SPLASH_CMYK
1210 // special case:
1211 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1212 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1213 // !pipe->nonIsolatedGroup &&
1214 // bitmap->mode == splashModeCMYK8 && pipe->destAlphaPtr
pipeRunAACMYK8(SplashPipe * pipe)1215 void Splash::pipeRunAACMYK8(SplashPipe *pipe) {
1216 Guchar aSrc, aDest, alpha2, aResult;
1217 SplashColor cDest;
1218 Guchar cResult0, cResult1, cResult2, cResult3;
1219
1220 //----- read destination pixel
1221 cDest[0] = pipe->destColorPtr[0];
1222 cDest[1] = pipe->destColorPtr[1];
1223 cDest[2] = pipe->destColorPtr[2];
1224 cDest[3] = pipe->destColorPtr[3];
1225 aDest = *pipe->destAlphaPtr;
1226
1227 //----- source alpha
1228 aSrc = div255(pipe->aInput * pipe->shape);
1229
1230 //----- result alpha and non-isolated group element correction
1231 aResult = aSrc + aDest - div255(aSrc * aDest);
1232 alpha2 = aResult;
1233
1234 //----- result color
1235 if (alpha2 == 0) {
1236 cResult0 = 0;
1237 cResult1 = 0;
1238 cResult2 = 0;
1239 cResult3 = 0;
1240 } else {
1241 cResult0 = state->cmykTransferC[(Guchar)(((alpha2 - aSrc) * cDest[0] +
1242 aSrc * pipe->cSrc[0]) / alpha2)];
1243 cResult1 = state->cmykTransferM[(Guchar)(((alpha2 - aSrc) * cDest[1] +
1244 aSrc * pipe->cSrc[1]) / alpha2)];
1245 cResult2 = state->cmykTransferY[(Guchar)(((alpha2 - aSrc) * cDest[2] +
1246 aSrc * pipe->cSrc[2]) / alpha2)];
1247 cResult3 = state->cmykTransferK[(Guchar)(((alpha2 - aSrc) * cDest[3] +
1248 aSrc * pipe->cSrc[3]) / alpha2)];
1249 }
1250
1251 //----- write destination pixel
1252 if (state->overprintMask & 1) {
1253 pipe->destColorPtr[0] = (state->overprintAdditive && pipe->shape != 0) ?
1254 std::min<int>(pipe->destColorPtr[0] + cResult0, 255) :
1255 cResult0;
1256 }
1257 if (state->overprintMask & 2) {
1258 pipe->destColorPtr[1] = (state->overprintAdditive && pipe->shape != 0) ?
1259 std::min<int>(pipe->destColorPtr[1] + cResult1, 255) :
1260 cResult1;
1261 }
1262 if (state->overprintMask & 4) {
1263 pipe->destColorPtr[2] = (state->overprintAdditive && pipe->shape != 0) ?
1264 std::min<int>(pipe->destColorPtr[2] + cResult2, 255) :
1265 cResult2;
1266 }
1267 if (state->overprintMask & 8) {
1268 pipe->destColorPtr[3] = (state->overprintAdditive && pipe->shape != 0) ?
1269 std::min<int>(pipe->destColorPtr[3] + cResult3, 255) :
1270 cResult3;
1271 }
1272 pipe->destColorPtr += 4;
1273 *pipe->destAlphaPtr++ = aResult;
1274
1275 ++pipe->x;
1276 }
1277
1278 // special case:
1279 // !pipe->pattern && !pipe->noTransparency && !state->softMask &&
1280 // pipe->usesShape && !pipe->alpha0Ptr && !state->blendFunc &&
1281 // !pipe->nonIsolatedGroup &&
1282 // bitmap->mode == splashModeDeviceN8 && pipe->destAlphaPtr
pipeRunAADeviceN8(SplashPipe * pipe)1283 void Splash::pipeRunAADeviceN8(SplashPipe *pipe) {
1284 Guchar aSrc, aDest, alpha2, aResult;
1285 SplashColor cDest;
1286 Guchar cResult[SPOT_NCOMPS+4];
1287 int cp, mask;
1288
1289 //----- read destination pixel
1290 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1291 cDest[cp] = pipe->destColorPtr[cp];
1292 aDest = *pipe->destAlphaPtr;
1293
1294 //----- source alpha
1295 aSrc = div255(pipe->aInput * pipe->shape);
1296
1297 //----- result alpha and non-isolated group element correction
1298 aResult = aSrc + aDest - div255(aSrc * aDest);
1299 alpha2 = aResult;
1300
1301 //----- result color
1302 if (alpha2 == 0) {
1303 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1304 cResult[cp] = 0;
1305 } else {
1306 for (cp=0; cp < SPOT_NCOMPS+4; cp++)
1307 cResult[cp] = state->deviceNTransfer[cp][(Guchar)(((alpha2 - aSrc) * cDest[cp] +
1308 aSrc * pipe->cSrc[cp]) / alpha2)];
1309 }
1310
1311 //----- write destination pixel
1312 mask = 1;
1313 for (cp=0; cp < SPOT_NCOMPS+4; cp++) {
1314 if (state->overprintMask & mask) {
1315 pipe->destColorPtr[cp] = cResult[cp];
1316 }
1317 mask <<= 1;
1318 }
1319 pipe->destColorPtr += (SPOT_NCOMPS+4);
1320 *pipe->destAlphaPtr++ = aResult;
1321
1322 ++pipe->x;
1323 }
1324 #endif
1325
pipeSetXY(SplashPipe * pipe,int x,int y)1326 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
1327 pipe->x = x;
1328 pipe->y = y;
1329 if (state->softMask) {
1330 pipe->softMaskPtr =
1331 &state->softMask->data[y * state->softMask->rowSize + x];
1332 }
1333 switch (bitmap->mode) {
1334 case splashModeMono1:
1335 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
1336 pipe->destColorMask = 0x80 >> (x & 7);
1337 break;
1338 case splashModeMono8:
1339 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
1340 break;
1341 case splashModeRGB8:
1342 case splashModeBGR8:
1343 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
1344 break;
1345 case splashModeXBGR8:
1346 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1347 break;
1348 #if SPLASH_CMYK
1349 case splashModeCMYK8:
1350 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
1351 break;
1352 case splashModeDeviceN8:
1353 pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (SPOT_NCOMPS + 4) * x];
1354 break;
1355 #endif
1356 }
1357 if (bitmap->alpha) {
1358 pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
1359 } else {
1360 pipe->destAlphaPtr = NULL;
1361 }
1362 if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
1363 pipe->alpha0Ptr =
1364 &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
1365 (alpha0X + x)];
1366 } else {
1367 pipe->alpha0Ptr = NULL;
1368 }
1369 }
1370
pipeIncX(SplashPipe * pipe)1371 inline void Splash::pipeIncX(SplashPipe *pipe) {
1372 ++pipe->x;
1373 if (state->softMask) {
1374 ++pipe->softMaskPtr;
1375 }
1376 switch (bitmap->mode) {
1377 case splashModeMono1:
1378 if (!(pipe->destColorMask >>= 1)) {
1379 pipe->destColorMask = 0x80;
1380 ++pipe->destColorPtr;
1381 }
1382 break;
1383 case splashModeMono8:
1384 ++pipe->destColorPtr;
1385 break;
1386 case splashModeRGB8:
1387 case splashModeBGR8:
1388 pipe->destColorPtr += 3;
1389 break;
1390 case splashModeXBGR8:
1391 pipe->destColorPtr += 4;
1392 break;
1393 #if SPLASH_CMYK
1394 case splashModeCMYK8:
1395 pipe->destColorPtr += 4;
1396 break;
1397 case splashModeDeviceN8:
1398 pipe->destColorPtr += (SPOT_NCOMPS+4);
1399 break;
1400 #endif
1401 }
1402 if (pipe->destAlphaPtr) {
1403 ++pipe->destAlphaPtr;
1404 }
1405 if (pipe->alpha0Ptr) {
1406 ++pipe->alpha0Ptr;
1407 }
1408 }
1409
drawPixel(SplashPipe * pipe,int x,int y,GBool noClip)1410 inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
1411 if (unlikely(y < 0))
1412 return;
1413
1414 if (noClip || state->clip->test(x, y)) {
1415 pipeSetXY(pipe, x, y);
1416 (this->*pipe->run)(pipe);
1417 updateModX(x);
1418 updateModY(y);
1419 }
1420 }
1421
drawAAPixelInit()1422 inline void Splash::drawAAPixelInit() {
1423 aaBufY = -1;
1424 }
1425
drawAAPixel(SplashPipe * pipe,int x,int y)1426 inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
1427 #if splashAASize == 4
1428 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1429 1, 2, 2, 3, 2, 3, 3, 4 };
1430 int w;
1431 #else
1432 int xx, yy;
1433 #endif
1434 SplashColorPtr p;
1435 int x0, x1, t;
1436
1437 if (x < 0 || x >= bitmap->width ||
1438 y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
1439 return;
1440 }
1441
1442 // update aaBuf
1443 if (y != aaBufY) {
1444 memset(aaBuf->getDataPtr(), 0xff,
1445 aaBuf->getRowSize() * aaBuf->getHeight());
1446 x0 = 0;
1447 x1 = bitmap->width - 1;
1448 state->clip->clipAALine(aaBuf, &x0, &x1, y);
1449 aaBufY = y;
1450 }
1451
1452 // compute the shape value
1453 #if splashAASize == 4
1454 p = aaBuf->getDataPtr() + (x >> 1);
1455 w = aaBuf->getRowSize();
1456 if (x & 1) {
1457 t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
1458 bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
1459 } else {
1460 t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
1461 bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
1462 }
1463 #else
1464 t = 0;
1465 for (yy = 0; yy < splashAASize; ++yy) {
1466 for (xx = 0; xx < splashAASize; ++xx) {
1467 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
1468 ((x * splashAASize + xx) >> 3);
1469 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1470 }
1471 }
1472 #endif
1473
1474 // draw the pixel
1475 if (t != 0) {
1476 pipeSetXY(pipe, x, y);
1477 pipe->shape = div255(aaGamma[t] * pipe->shape);
1478 (this->*pipe->run)(pipe);
1479 updateModX(x);
1480 updateModY(y);
1481 }
1482 }
1483
drawSpan(SplashPipe * pipe,int x0,int x1,int y,GBool noClip)1484 inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
1485 GBool noClip) {
1486 int x;
1487
1488 if (noClip) {
1489 pipeSetXY(pipe, x0, y);
1490 for (x = x0; x <= x1; ++x) {
1491 (this->*pipe->run)(pipe);
1492 }
1493 updateModX(x0);
1494 updateModX(x1);
1495 updateModY(y);
1496 } else {
1497 if (x0 < state->clip->getXMinI()) {
1498 x0 = state->clip->getXMinI();
1499 }
1500 if (x1 > state->clip->getXMaxI()) {
1501 x1 = state->clip->getXMaxI();
1502 }
1503 pipeSetXY(pipe, x0, y);
1504 for (x = x0; x <= x1; ++x) {
1505 if (state->clip->test(x, y)) {
1506 (this->*pipe->run)(pipe);
1507 updateModX(x);
1508 updateModY(y);
1509 } else {
1510 pipeIncX(pipe);
1511 }
1512 }
1513 }
1514 }
1515
drawAALine(SplashPipe * pipe,int x0,int x1,int y,GBool adjustLine,Guchar lineOpacity)1516 inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y, GBool adjustLine, Guchar lineOpacity) {
1517 #if splashAASize == 4
1518 static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
1519 1, 2, 2, 3, 2, 3, 3, 4 };
1520 SplashColorPtr p0, p1, p2, p3;
1521 int t;
1522 #else
1523 SplashColorPtr p;
1524 int xx, yy, t;
1525 #endif
1526 int x;
1527
1528 #if splashAASize == 4
1529 p0 = aaBuf->getDataPtr() + (x0 >> 1);
1530 p1 = p0 + aaBuf->getRowSize();
1531 p2 = p1 + aaBuf->getRowSize();
1532 p3 = p2 + aaBuf->getRowSize();
1533 #endif
1534 pipeSetXY(pipe, x0, y);
1535 for (x = x0; x <= x1; ++x) {
1536
1537 // compute the shape value
1538 #if splashAASize == 4
1539 if (x & 1) {
1540 t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
1541 bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
1542 ++p0; ++p1; ++p2; ++p3;
1543 } else {
1544 t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
1545 bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
1546 }
1547 #else
1548 t = 0;
1549 for (yy = 0; yy < splashAASize; ++yy) {
1550 for (xx = 0; xx < splashAASize; ++xx) {
1551 p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
1552 ((x * splashAASize + xx) >> 3);
1553 t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
1554 }
1555 }
1556 #endif
1557
1558 if (t != 0) {
1559 pipe->shape = (adjustLine) ? div255((int) lineOpacity * (double)aaGamma[t]) : (double)aaGamma[t];
1560 (this->*pipe->run)(pipe);
1561 updateModX(x);
1562 updateModY(y);
1563 } else {
1564 pipeIncX(pipe);
1565 }
1566 }
1567 }
1568
1569 //------------------------------------------------------------------------
1570
1571 // Transform a point from user space to device space.
transform(SplashCoord * matrix,SplashCoord xi,SplashCoord yi,SplashCoord * xo,SplashCoord * yo)1572 inline void Splash::transform(SplashCoord *matrix,
1573 SplashCoord xi, SplashCoord yi,
1574 SplashCoord *xo, SplashCoord *yo) {
1575 // [ m[0] m[1] 0 ]
1576 // [xo yo 1] = [xi yi 1] * [ m[2] m[3] 0 ]
1577 // [ m[4] m[5] 1 ]
1578 *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
1579 *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
1580 }
1581
1582 //------------------------------------------------------------------------
1583 // Splash
1584 //------------------------------------------------------------------------
1585
Splash(SplashBitmap * bitmapA,GBool vectorAntialiasA,SplashScreenParams * screenParams)1586 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
1587 SplashScreenParams *screenParams) {
1588 int i;
1589
1590 bitmap = bitmapA;
1591 vectorAntialias = vectorAntialiasA;
1592 inShading = gFalse;
1593 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
1594 screenParams);
1595 if (vectorAntialias) {
1596 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1597 1, splashModeMono1, gFalse);
1598 for (i = 0; i <= splashAASize * splashAASize; ++i) {
1599 aaGamma[i] = (Guchar)splashRound(
1600 splashPow((SplashCoord)i /
1601 (SplashCoord)(splashAASize * splashAASize),
1602 splashAAGamma) * 255);
1603 }
1604 } else {
1605 aaBuf = NULL;
1606 }
1607 minLineWidth = 0;
1608 thinLineMode = splashThinLineDefault;
1609 clearModRegion();
1610 debugMode = gFalse;
1611 alpha0Bitmap = NULL;
1612 }
1613
Splash(SplashBitmap * bitmapA,GBool vectorAntialiasA,SplashScreen * screenA)1614 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
1615 SplashScreen *screenA) {
1616 int i;
1617
1618 bitmap = bitmapA;
1619 inShading = gFalse;
1620 vectorAntialias = vectorAntialiasA;
1621 state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
1622 screenA);
1623 if (vectorAntialias) {
1624 aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
1625 1, splashModeMono1, gFalse);
1626 for (i = 0; i <= splashAASize * splashAASize; ++i) {
1627 aaGamma[i] = (Guchar)splashRound(
1628 splashPow((SplashCoord)i /
1629 (SplashCoord)(splashAASize * splashAASize),
1630 splashAAGamma) * 255);
1631 }
1632 } else {
1633 aaBuf = NULL;
1634 }
1635 minLineWidth = 0;
1636 thinLineMode = splashThinLineDefault;
1637 clearModRegion();
1638 debugMode = gFalse;
1639 alpha0Bitmap = NULL;
1640 }
1641
~Splash()1642 Splash::~Splash() {
1643 while (state->next) {
1644 restoreState();
1645 }
1646 delete state;
1647 if (vectorAntialias) {
1648 delete aaBuf;
1649 }
1650 }
1651
1652 //------------------------------------------------------------------------
1653 // state read
1654 //------------------------------------------------------------------------
1655
getMatrix()1656 SplashCoord *Splash::getMatrix() {
1657 return state->matrix;
1658 }
1659
getStrokePattern()1660 SplashPattern *Splash::getStrokePattern() {
1661 return state->strokePattern;
1662 }
1663
getFillPattern()1664 SplashPattern *Splash::getFillPattern() {
1665 return state->fillPattern;
1666 }
1667
getScreen()1668 SplashScreen *Splash::getScreen() {
1669 return state->screen;
1670 }
1671
getBlendFunc()1672 SplashBlendFunc Splash::getBlendFunc() {
1673 return state->blendFunc;
1674 }
1675
getStrokeAlpha()1676 SplashCoord Splash::getStrokeAlpha() {
1677 return state->strokeAlpha;
1678 }
1679
getFillAlpha()1680 SplashCoord Splash::getFillAlpha() {
1681 return state->fillAlpha;
1682 }
1683
getLineWidth()1684 SplashCoord Splash::getLineWidth() {
1685 return state->lineWidth;
1686 }
1687
getLineCap()1688 int Splash::getLineCap() {
1689 return state->lineCap;
1690 }
1691
getLineJoin()1692 int Splash::getLineJoin() {
1693 return state->lineJoin;
1694 }
1695
getMiterLimit()1696 SplashCoord Splash::getMiterLimit() {
1697 return state->miterLimit;
1698 }
1699
getFlatness()1700 SplashCoord Splash::getFlatness() {
1701 return state->flatness;
1702 }
1703
getLineDash()1704 SplashCoord *Splash::getLineDash() {
1705 return state->lineDash;
1706 }
1707
getLineDashLength()1708 int Splash::getLineDashLength() {
1709 return state->lineDashLength;
1710 }
1711
getLineDashPhase()1712 SplashCoord Splash::getLineDashPhase() {
1713 return state->lineDashPhase;
1714 }
1715
getStrokeAdjust()1716 GBool Splash::getStrokeAdjust() {
1717 return state->strokeAdjust;
1718 }
1719
getClip()1720 SplashClip *Splash::getClip() {
1721 return state->clip;
1722 }
1723
getSoftMask()1724 SplashBitmap *Splash::getSoftMask() {
1725 return state->softMask;
1726 }
1727
getInNonIsolatedGroup()1728 GBool Splash::getInNonIsolatedGroup() {
1729 return state->inNonIsolatedGroup;
1730 }
1731
1732 //------------------------------------------------------------------------
1733 // state write
1734 //------------------------------------------------------------------------
1735
setMatrix(SplashCoord * matrix)1736 void Splash::setMatrix(SplashCoord *matrix) {
1737 memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
1738 }
1739
setStrokePattern(SplashPattern * strokePattern)1740 void Splash::setStrokePattern(SplashPattern *strokePattern) {
1741 state->setStrokePattern(strokePattern);
1742 }
1743
setFillPattern(SplashPattern * fillPattern)1744 void Splash::setFillPattern(SplashPattern *fillPattern) {
1745 state->setFillPattern(fillPattern);
1746 }
1747
setScreen(SplashScreen * screen)1748 void Splash::setScreen(SplashScreen *screen) {
1749 state->setScreen(screen);
1750 }
1751
setBlendFunc(SplashBlendFunc func)1752 void Splash::setBlendFunc(SplashBlendFunc func) {
1753 state->blendFunc = func;
1754 }
1755
setStrokeAlpha(SplashCoord alpha)1756 void Splash::setStrokeAlpha(SplashCoord alpha) {
1757 state->strokeAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha;
1758 }
1759
setFillAlpha(SplashCoord alpha)1760 void Splash::setFillAlpha(SplashCoord alpha) {
1761 state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha;
1762 }
1763
setPatternAlpha(SplashCoord strokeAlpha,SplashCoord fillAlpha)1764 void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) {
1765 state->patternStrokeAlpha = strokeAlpha;
1766 state->patternFillAlpha = fillAlpha;
1767 state->multiplyPatternAlpha = gTrue;
1768 }
1769
clearPatternAlpha()1770 void Splash::clearPatternAlpha() {
1771 state->patternStrokeAlpha = 1;
1772 state->patternFillAlpha = 1;
1773 state->multiplyPatternAlpha = gFalse;
1774 }
1775
setFillOverprint(GBool fop)1776 void Splash::setFillOverprint(GBool fop) {
1777 state->fillOverprint = fop;
1778 }
1779
setStrokeOverprint(GBool gop)1780 void Splash::setStrokeOverprint(GBool gop) {
1781 state->strokeOverprint = gop;
1782 }
1783
setOverprintMode(int opm)1784 void Splash::setOverprintMode(int opm) {
1785 state->overprintMode = opm;
1786 }
1787
setLineWidth(SplashCoord lineWidth)1788 void Splash::setLineWidth(SplashCoord lineWidth) {
1789 state->lineWidth = lineWidth;
1790 }
1791
setLineCap(int lineCap)1792 void Splash::setLineCap(int lineCap) {
1793 state->lineCap = lineCap;
1794 }
1795
setLineJoin(int lineJoin)1796 void Splash::setLineJoin(int lineJoin) {
1797 state->lineJoin = lineJoin;
1798 }
1799
setMiterLimit(SplashCoord miterLimit)1800 void Splash::setMiterLimit(SplashCoord miterLimit) {
1801 state->miterLimit = miterLimit;
1802 }
1803
setFlatness(SplashCoord flatness)1804 void Splash::setFlatness(SplashCoord flatness) {
1805 if (flatness < 1) {
1806 state->flatness = 1;
1807 } else {
1808 state->flatness = flatness;
1809 }
1810 }
1811
setLineDash(SplashCoord * lineDash,int lineDashLength,SplashCoord lineDashPhase)1812 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
1813 SplashCoord lineDashPhase) {
1814 state->setLineDash(lineDash, lineDashLength, lineDashPhase);
1815 }
1816
setStrokeAdjust(GBool strokeAdjust)1817 void Splash::setStrokeAdjust(GBool strokeAdjust) {
1818 state->strokeAdjust = strokeAdjust;
1819 }
1820
clipResetToRect(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1)1821 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
1822 SplashCoord x1, SplashCoord y1) {
1823 state->clip->resetToRect(x0, y0, x1, y1);
1824 }
1825
clipToRect(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1)1826 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
1827 SplashCoord x1, SplashCoord y1) {
1828 return state->clip->clipToRect(x0, y0, x1, y1);
1829 }
1830
clipToPath(SplashPath * path,GBool eo)1831 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
1832 return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
1833 }
1834
setSoftMask(SplashBitmap * softMask)1835 void Splash::setSoftMask(SplashBitmap *softMask) {
1836 state->setSoftMask(softMask);
1837 }
1838
setInNonIsolatedGroup(SplashBitmap * alpha0BitmapA,int alpha0XA,int alpha0YA)1839 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
1840 int alpha0XA, int alpha0YA) {
1841 alpha0Bitmap = alpha0BitmapA;
1842 alpha0X = alpha0XA;
1843 alpha0Y = alpha0YA;
1844 state->inNonIsolatedGroup = gTrue;
1845 }
1846
setTransfer(Guchar * red,Guchar * green,Guchar * blue,Guchar * gray)1847 void Splash::setTransfer(Guchar *red, Guchar *green, Guchar *blue,
1848 Guchar *gray) {
1849 state->setTransfer(red, green, blue, gray);
1850 }
1851
setOverprintMask(Guint overprintMask,GBool additive)1852 void Splash::setOverprintMask(Guint overprintMask, GBool additive) {
1853 state->overprintMask = overprintMask;
1854 state->overprintAdditive = additive;
1855 }
1856
1857 //------------------------------------------------------------------------
1858 // state save/restore
1859 //------------------------------------------------------------------------
1860
saveState()1861 void Splash::saveState() {
1862 SplashState *newState;
1863
1864 newState = state->copy();
1865 newState->next = state;
1866 state = newState;
1867 }
1868
restoreState()1869 SplashError Splash::restoreState() {
1870 SplashState *oldState;
1871
1872 if (!state->next) {
1873 return splashErrNoSave;
1874 }
1875 oldState = state;
1876 state = state->next;
1877 delete oldState;
1878 return splashOk;
1879 }
1880
1881 //------------------------------------------------------------------------
1882 // drawing operations
1883 //------------------------------------------------------------------------
1884
clear(SplashColorPtr color,Guchar alpha)1885 void Splash::clear(SplashColorPtr color, Guchar alpha) {
1886 SplashColorPtr row, p;
1887 Guchar mono;
1888 int x, y;
1889
1890 switch (bitmap->mode) {
1891 case splashModeMono1:
1892 mono = (color[0] & 0x80) ? 0xff : 0x00;
1893 if (bitmap->rowSize < 0) {
1894 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1895 mono, -bitmap->rowSize * bitmap->height);
1896 } else {
1897 memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1898 }
1899 break;
1900 case splashModeMono8:
1901 if (bitmap->rowSize < 0) {
1902 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1903 color[0], -bitmap->rowSize * bitmap->height);
1904 } else {
1905 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1906 }
1907 break;
1908 case splashModeRGB8:
1909 if (color[0] == color[1] && color[1] == color[2]) {
1910 if (bitmap->rowSize < 0) {
1911 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1912 color[0], -bitmap->rowSize * bitmap->height);
1913 } else {
1914 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1915 }
1916 } else {
1917 row = bitmap->data;
1918 for (y = 0; y < bitmap->height; ++y) {
1919 p = row;
1920 for (x = 0; x < bitmap->width; ++x) {
1921 *p++ = color[2];
1922 *p++ = color[1];
1923 *p++ = color[0];
1924 }
1925 row += bitmap->rowSize;
1926 }
1927 }
1928 break;
1929 case splashModeXBGR8:
1930 if (color[0] == color[1] && color[1] == color[2]) {
1931 if (bitmap->rowSize < 0) {
1932 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1933 color[0], -bitmap->rowSize * bitmap->height);
1934 } else {
1935 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1936 }
1937 } else {
1938 row = bitmap->data;
1939 for (y = 0; y < bitmap->height; ++y) {
1940 p = row;
1941 for (x = 0; x < bitmap->width; ++x) {
1942 *p++ = color[0];
1943 *p++ = color[1];
1944 *p++ = color[2];
1945 *p++ = 255;
1946 }
1947 row += bitmap->rowSize;
1948 }
1949 }
1950 break;
1951 case splashModeBGR8:
1952 if (color[0] == color[1] && color[1] == color[2]) {
1953 if (bitmap->rowSize < 0) {
1954 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1955 color[0], -bitmap->rowSize * bitmap->height);
1956 } else {
1957 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1958 }
1959 } else {
1960 row = bitmap->data;
1961 for (y = 0; y < bitmap->height; ++y) {
1962 p = row;
1963 for (x = 0; x < bitmap->width; ++x) {
1964 *p++ = color[0];
1965 *p++ = color[1];
1966 *p++ = color[2];
1967 }
1968 row += bitmap->rowSize;
1969 }
1970 }
1971 break;
1972 #if SPLASH_CMYK
1973 case splashModeCMYK8:
1974 if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1975 if (bitmap->rowSize < 0) {
1976 memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1977 color[0], -bitmap->rowSize * bitmap->height);
1978 } else {
1979 memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1980 }
1981 } else {
1982 row = bitmap->data;
1983 for (y = 0; y < bitmap->height; ++y) {
1984 p = row;
1985 for (x = 0; x < bitmap->width; ++x) {
1986 *p++ = color[0];
1987 *p++ = color[1];
1988 *p++ = color[2];
1989 *p++ = color[3];
1990 }
1991 row += bitmap->rowSize;
1992 }
1993 }
1994 break;
1995 case splashModeDeviceN8:
1996 row = bitmap->data;
1997 for (y = 0; y < bitmap->height; ++y) {
1998 p = row;
1999 for (x = 0; x < bitmap->width; ++x) {
2000 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
2001 *p++ = color[cp];
2002 }
2003 row += bitmap->rowSize;
2004 }
2005 break;
2006 #endif
2007 }
2008
2009 if (bitmap->alpha) {
2010 memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
2011 }
2012
2013 updateModX(0);
2014 updateModY(0);
2015 updateModX(bitmap->width - 1);
2016 updateModY(bitmap->height - 1);
2017 }
2018
stroke(SplashPath * path)2019 SplashError Splash::stroke(SplashPath *path) {
2020 SplashPath *path2, *dPath;
2021 SplashCoord d1, d2, t1, t2, w;
2022
2023 if (debugMode) {
2024 printf("stroke [dash:%d] [width:%.2f]:\n",
2025 state->lineDashLength, (double)state->lineWidth);
2026 dumpPath(path);
2027 }
2028 opClipRes = splashClipAllOutside;
2029 if (path->length == 0) {
2030 return splashErrEmptyPath;
2031 }
2032 path2 = flattenPath(path, state->matrix, state->flatness);
2033 if (state->lineDashLength > 0) {
2034 dPath = makeDashedPath(path2);
2035 delete path2;
2036 path2 = dPath;
2037 if (path2->length == 0) {
2038 delete path2;
2039 return splashErrEmptyPath;
2040 }
2041 }
2042
2043 // transform a unit square, and take the half the max of the two
2044 // diagonals; the product of this number and the line width is the
2045 // (approximate) transformed line width
2046 t1 = state->matrix[0] + state->matrix[2];
2047 t2 = state->matrix[1] + state->matrix[3];
2048 d1 = t1 * t1 + t2 * t2;
2049 t1 = state->matrix[0] - state->matrix[2];
2050 t2 = state->matrix[1] - state->matrix[3];
2051 d2 = t1 * t1 + t2 * t2;
2052 if (d2 > d1) {
2053 d1 = d2;
2054 }
2055 d1 *= 0.5;
2056 if (d1 > 0 &&
2057 d1 * state->lineWidth * state->lineWidth < minLineWidth * minLineWidth) {
2058 w = minLineWidth / splashSqrt(d1);
2059 strokeWide(path2, w);
2060 } else if (bitmap->mode == splashModeMono1) {
2061 // this gets close to Adobe's behavior in mono mode
2062 if (d1 * state->lineWidth <= 2) {
2063 strokeNarrow(path2);
2064 } else {
2065 strokeWide(path2, state->lineWidth);
2066 }
2067 } else {
2068 if (state->lineWidth == 0) {
2069 strokeNarrow(path2);
2070 } else {
2071 strokeWide(path2, state->lineWidth);
2072 }
2073 }
2074
2075 delete path2;
2076 return splashOk;
2077 }
2078
strokeNarrow(SplashPath * path)2079 void Splash::strokeNarrow(SplashPath *path) {
2080 SplashPipe pipe;
2081 SplashXPath *xPath;
2082 SplashXPathSeg *seg;
2083 int x0, x1, y0, y1, xa, xb, y;
2084 SplashCoord dxdy;
2085 SplashClipResult clipRes;
2086 int nClipRes[3];
2087 int i;
2088
2089 nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
2090
2091 xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
2092
2093 pipeInit(&pipe, 0, 0, state->strokePattern, NULL,
2094 (Guchar)splashRound(state->strokeAlpha * 255),
2095 gFalse, gFalse);
2096
2097 for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
2098 if (seg->y0 <= seg->y1) {
2099 y0 = splashFloor(seg->y0);
2100 y1 = splashFloor(seg->y1);
2101 x0 = splashFloor(seg->x0);
2102 x1 = splashFloor(seg->x1);
2103 } else {
2104 y0 = splashFloor(seg->y1);
2105 y1 = splashFloor(seg->y0);
2106 x0 = splashFloor(seg->x1);
2107 x1 = splashFloor(seg->x0);
2108 }
2109 if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
2110 x0 <= x1 ? x1 : x0, y1))
2111 != splashClipAllOutside) {
2112 if (y0 == y1) {
2113 if (x0 <= x1) {
2114 drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
2115 } else {
2116 drawSpan(&pipe, x1, x0, y0, clipRes == splashClipAllInside);
2117 }
2118 } else {
2119 dxdy = seg->dxdy;
2120 if (y0 < state->clip->getYMinI()) {
2121 y0 = state->clip->getYMinI();
2122 x0 = splashFloor(seg->x0 + ((SplashCoord)y0 - seg->y0) * dxdy);
2123 }
2124 if (y1 > state->clip->getYMaxI()) {
2125 y1 = state->clip->getYMaxI();
2126 x1 = splashFloor(seg->x0 + ((SplashCoord)y1 - seg->y0) * dxdy);
2127 }
2128 if (x0 <= x1) {
2129 xa = x0;
2130 for (y = y0; y <= y1; ++y) {
2131 if (y < y1) {
2132 xb = splashFloor(seg->x0 +
2133 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2134 } else {
2135 xb = x1 + 1;
2136 }
2137 if (xa == xb) {
2138 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2139 } else {
2140 drawSpan(&pipe, xa, xb - 1, y, clipRes == splashClipAllInside);
2141 }
2142 xa = xb;
2143 }
2144 } else {
2145 xa = x0;
2146 for (y = y0; y <= y1; ++y) {
2147 if (y < y1) {
2148 xb = splashFloor(seg->x0 +
2149 ((SplashCoord)y + 1 - seg->y0) * dxdy);
2150 } else {
2151 xb = x1 - 1;
2152 }
2153 if (xa == xb) {
2154 drawPixel(&pipe, xa, y, clipRes == splashClipAllInside);
2155 } else {
2156 drawSpan(&pipe, xb + 1, xa, y, clipRes == splashClipAllInside);
2157 }
2158 xa = xb;
2159 }
2160 }
2161 }
2162 }
2163 ++nClipRes[clipRes];
2164 }
2165 if (nClipRes[splashClipPartial] ||
2166 (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
2167 opClipRes = splashClipPartial;
2168 } else if (nClipRes[splashClipAllInside]) {
2169 opClipRes = splashClipAllInside;
2170 } else {
2171 opClipRes = splashClipAllOutside;
2172 }
2173
2174 delete xPath;
2175 }
2176
strokeWide(SplashPath * path,SplashCoord w)2177 void Splash::strokeWide(SplashPath *path, SplashCoord w) {
2178 SplashPath *path2;
2179
2180 path2 = makeStrokePath(path, w, gFalse);
2181 fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
2182 delete path2;
2183 }
2184
flattenPath(SplashPath * path,SplashCoord * matrix,SplashCoord flatness)2185 SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
2186 SplashCoord flatness) {
2187 SplashPath *fPath;
2188 SplashCoord flatness2;
2189 Guchar flag;
2190 int i;
2191
2192 fPath = new SplashPath();
2193 #if USE_FIXEDPOINT
2194 flatness2 = flatness;
2195 #else
2196 flatness2 = flatness * flatness;
2197 #endif
2198 i = 0;
2199 while (i < path->length) {
2200 flag = path->flags[i];
2201 if (flag & splashPathFirst) {
2202 fPath->moveTo(path->pts[i].x, path->pts[i].y);
2203 ++i;
2204 } else {
2205 if (flag & splashPathCurve) {
2206 flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
2207 path->pts[i ].x, path->pts[i ].y,
2208 path->pts[i+1].x, path->pts[i+1].y,
2209 path->pts[i+2].x, path->pts[i+2].y,
2210 matrix, flatness2, fPath);
2211 i += 3;
2212 } else {
2213 fPath->lineTo(path->pts[i].x, path->pts[i].y);
2214 ++i;
2215 }
2216 if (path->flags[i-1] & splashPathClosed) {
2217 fPath->close();
2218 }
2219 }
2220 }
2221 return fPath;
2222 }
2223
flattenCurve(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1,SplashCoord x2,SplashCoord y2,SplashCoord x3,SplashCoord y3,SplashCoord * matrix,SplashCoord flatness2,SplashPath * fPath)2224 void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
2225 SplashCoord x1, SplashCoord y1,
2226 SplashCoord x2, SplashCoord y2,
2227 SplashCoord x3, SplashCoord y3,
2228 SplashCoord *matrix, SplashCoord flatness2,
2229 SplashPath *fPath) {
2230 SplashCoord cx[splashMaxCurveSplits + 1][3];
2231 SplashCoord cy[splashMaxCurveSplits + 1][3];
2232 int cNext[splashMaxCurveSplits + 1];
2233 SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
2234 SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
2235 SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
2236 int p1, p2, p3;
2237
2238 // initial segment
2239 p1 = 0;
2240 p2 = splashMaxCurveSplits;
2241 cx[p1][0] = x0; cy[p1][0] = y0;
2242 cx[p1][1] = x1; cy[p1][1] = y1;
2243 cx[p1][2] = x2; cy[p1][2] = y2;
2244 cx[p2][0] = x3; cy[p2][0] = y3;
2245 cNext[p1] = p2;
2246
2247 while (p1 < splashMaxCurveSplits) {
2248
2249 // get the next segment
2250 xl0 = cx[p1][0]; yl0 = cy[p1][0];
2251 xx1 = cx[p1][1]; yy1 = cy[p1][1];
2252 xx2 = cx[p1][2]; yy2 = cy[p1][2];
2253 p2 = cNext[p1];
2254 xr3 = cx[p2][0]; yr3 = cy[p2][0];
2255
2256 // compute the distances (in device space) from the control points
2257 // to the midpoint of the straight line (this is a bit of a hack,
2258 // but it's much faster than computing the actual distances to the
2259 // line)
2260 transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
2261 transform(matrix, xx1, yy1, &tx, &ty);
2262 #if USE_FIXEDPOINT
2263 d1 = splashDist(tx, ty, mx, my);
2264 #else
2265 dx = tx - mx;
2266 dy = ty - my;
2267 d1 = dx*dx + dy*dy;
2268 #endif
2269 transform(matrix, xx2, yy2, &tx, &ty);
2270 #if USE_FIXEDPOINT
2271 d2 = splashDist(tx, ty, mx, my);
2272 #else
2273 dx = tx - mx;
2274 dy = ty - my;
2275 d2 = dx*dx + dy*dy;
2276 #endif
2277
2278 // if the curve is flat enough, or no more subdivisions are
2279 // allowed, add the straight line segment
2280 if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
2281 fPath->lineTo(xr3, yr3);
2282 p1 = p2;
2283
2284 // otherwise, subdivide the curve
2285 } else {
2286 xl1 = splashAvg(xl0, xx1);
2287 yl1 = splashAvg(yl0, yy1);
2288 xh = splashAvg(xx1, xx2);
2289 yh = splashAvg(yy1, yy2);
2290 xl2 = splashAvg(xl1, xh);
2291 yl2 = splashAvg(yl1, yh);
2292 xr2 = splashAvg(xx2, xr3);
2293 yr2 = splashAvg(yy2, yr3);
2294 xr1 = splashAvg(xh, xr2);
2295 yr1 = splashAvg(yh, yr2);
2296 xr0 = splashAvg(xl2, xr1);
2297 yr0 = splashAvg(yl2, yr1);
2298 // add the new subdivision points
2299 p3 = (p1 + p2) / 2;
2300 cx[p1][1] = xl1; cy[p1][1] = yl1;
2301 cx[p1][2] = xl2; cy[p1][2] = yl2;
2302 cNext[p1] = p3;
2303 cx[p3][0] = xr0; cy[p3][0] = yr0;
2304 cx[p3][1] = xr1; cy[p3][1] = yr1;
2305 cx[p3][2] = xr2; cy[p3][2] = yr2;
2306 cNext[p3] = p2;
2307 }
2308 }
2309 }
2310
makeDashedPath(SplashPath * path)2311 SplashPath *Splash::makeDashedPath(SplashPath *path) {
2312 SplashPath *dPath;
2313 SplashCoord lineDashTotal;
2314 SplashCoord lineDashStartPhase, lineDashDist, segLen;
2315 SplashCoord x0, y0, x1, y1, xa, ya;
2316 GBool lineDashStartOn, lineDashOn, newPath;
2317 int lineDashStartIdx, lineDashIdx;
2318 int i, j, k;
2319
2320 lineDashTotal = 0;
2321 for (i = 0; i < state->lineDashLength; ++i) {
2322 lineDashTotal += state->lineDash[i];
2323 }
2324 // Acrobat simply draws nothing if the dash array is [0]
2325 if (lineDashTotal == 0) {
2326 return new SplashPath();
2327 }
2328 lineDashStartPhase = state->lineDashPhase;
2329 i = splashFloor(lineDashStartPhase / lineDashTotal);
2330 lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
2331 lineDashStartOn = gTrue;
2332 lineDashStartIdx = 0;
2333 if (lineDashStartPhase > 0) {
2334 while (lineDashStartIdx < state->lineDashLength && lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
2335 lineDashStartOn = !lineDashStartOn;
2336 lineDashStartPhase -= state->lineDash[lineDashStartIdx];
2337 ++lineDashStartIdx;
2338 }
2339 if (unlikely(lineDashStartIdx == state->lineDashLength)) {
2340 return new SplashPath();
2341 }
2342 }
2343
2344 dPath = new SplashPath();
2345
2346 // process each subpath
2347 i = 0;
2348 while (i < path->length) {
2349
2350 // find the end of the subpath
2351 for (j = i;
2352 j < path->length - 1 && !(path->flags[j] & splashPathLast);
2353 ++j) ;
2354
2355 // initialize the dash parameters
2356 lineDashOn = lineDashStartOn;
2357 lineDashIdx = lineDashStartIdx;
2358 lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
2359
2360 // process each segment of the subpath
2361 newPath = gTrue;
2362 for (k = i; k < j; ++k) {
2363
2364 // grab the segment
2365 x0 = path->pts[k].x;
2366 y0 = path->pts[k].y;
2367 x1 = path->pts[k+1].x;
2368 y1 = path->pts[k+1].y;
2369 segLen = splashDist(x0, y0, x1, y1);
2370
2371 // process the segment
2372 while (segLen > 0) {
2373
2374 if (lineDashDist >= segLen) {
2375 if (lineDashOn) {
2376 if (newPath) {
2377 dPath->moveTo(x0, y0);
2378 newPath = gFalse;
2379 }
2380 dPath->lineTo(x1, y1);
2381 }
2382 lineDashDist -= segLen;
2383 segLen = 0;
2384
2385 } else {
2386 xa = x0 + (lineDashDist / segLen) * (x1 - x0);
2387 ya = y0 + (lineDashDist / segLen) * (y1 - y0);
2388 if (lineDashOn) {
2389 if (newPath) {
2390 dPath->moveTo(x0, y0);
2391 newPath = gFalse;
2392 }
2393 dPath->lineTo(xa, ya);
2394 }
2395 x0 = xa;
2396 y0 = ya;
2397 segLen -= lineDashDist;
2398 lineDashDist = 0;
2399 }
2400
2401 // get the next entry in the dash array
2402 if (lineDashDist <= 0) {
2403 lineDashOn = !lineDashOn;
2404 if (++lineDashIdx == state->lineDashLength) {
2405 lineDashIdx = 0;
2406 }
2407 lineDashDist = state->lineDash[lineDashIdx];
2408 newPath = gTrue;
2409 }
2410 }
2411 }
2412 i = j + 1;
2413 }
2414
2415 if (dPath->length == 0) {
2416 GBool allSame = gTrue;
2417 for (int i = 0; allSame && i < path->length - 1; ++i) {
2418 allSame = path->pts[i].x == path->pts[i + 1].x && path->pts[i].y == path->pts[i + 1].y;
2419 }
2420 if (allSame) {
2421 x0 = path->pts[0].x;
2422 y0 = path->pts[0].y;
2423 dPath->moveTo(x0, y0);
2424 dPath->lineTo(x0, y0);
2425 }
2426 }
2427
2428 return dPath;
2429 }
2430
fill(SplashPath * path,GBool eo)2431 SplashError Splash::fill(SplashPath *path, GBool eo) {
2432 if (debugMode) {
2433 printf("fill [eo:%d]:\n", eo);
2434 dumpPath(path);
2435 }
2436 return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
2437 }
2438
getBBoxFP(SplashPath * path,SplashCoord * xMinA,SplashCoord * yMinA,SplashCoord * xMaxA,SplashCoord * yMaxA)2439 inline void Splash::getBBoxFP(SplashPath *path, SplashCoord *xMinA, SplashCoord *yMinA,
2440 SplashCoord *xMaxA, SplashCoord *yMaxA) {
2441 SplashCoord xMinFP, yMinFP, xMaxFP, yMaxFP, tx, ty;
2442
2443 // make compiler happy:
2444 xMinFP = xMaxFP = yMinFP = yMaxFP = 0;
2445 for (int i = 0; i < path->length; ++i) {
2446 transform(state->matrix, path->pts[i].x, path->pts[i].y, &tx, &ty);
2447 if (i == 0) {
2448 xMinFP = xMaxFP = tx;
2449 yMinFP = yMaxFP = ty;
2450 } else {
2451 if (tx < xMinFP) xMinFP = tx;
2452 if (tx > xMaxFP) xMaxFP = tx;
2453 if (ty < yMinFP) yMinFP = ty;
2454 if (ty > yMaxFP) yMaxFP = ty;
2455 }
2456 }
2457
2458 *xMinA = xMinFP;
2459 *yMinA = yMinFP;
2460 *xMaxA = xMaxFP;
2461 *yMaxA = yMaxFP;
2462 }
2463
fillWithPattern(SplashPath * path,GBool eo,SplashPattern * pattern,SplashCoord alpha)2464 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
2465 SplashPattern *pattern,
2466 SplashCoord alpha) {
2467 SplashPipe pipe;
2468 SplashXPath *xPath;
2469 SplashXPathScanner *scanner;
2470 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2471 SplashClipResult clipRes, clipRes2;
2472 GBool adjustLine = gFalse;
2473 int linePosI = 0;
2474
2475 if (path->length == 0) {
2476 return splashErrEmptyPath;
2477 }
2478 if (pathAllOutside(path)) {
2479 opClipRes = splashClipAllOutside;
2480 return splashOk;
2481 }
2482
2483 // add stroke adjustment hints for filled rectangles -- this only
2484 // applies to paths that consist of a single subpath
2485 // (this appears to match Acrobat's behavior)
2486 if (state->strokeAdjust && !path->hints) {
2487 int n;
2488 n = path->getLength();
2489 if (n == 4 &&
2490 !(path->flags[0] & splashPathClosed) &&
2491 !(path->flags[1] & splashPathLast) &&
2492 !(path->flags[2] & splashPathLast)) {
2493 path->close(gTrue);
2494 path->addStrokeAdjustHint(0, 2, 0, 4);
2495 path->addStrokeAdjustHint(1, 3, 0, 4);
2496 } else if (n == 5 &&
2497 (path->flags[0] & splashPathClosed) &&
2498 !(path->flags[1] & splashPathLast) &&
2499 !(path->flags[2] & splashPathLast) &&
2500 !(path->flags[3] & splashPathLast)) {
2501 path->addStrokeAdjustHint(0, 2, 0, 4);
2502 path->addStrokeAdjustHint(1, 3, 0, 4);
2503 }
2504 }
2505
2506 if (thinLineMode != splashThinLineDefault) {
2507 if (state->clip->getXMinI() == state->clip->getXMaxI()) {
2508 linePosI = state->clip->getXMinI();
2509 adjustLine = gTrue;
2510 } else if (state->clip->getXMinI() == state->clip->getXMaxI() - 1) {
2511 adjustLine = gTrue;
2512 linePosI = splashFloor(state->clip->getXMin() + state->lineWidth);
2513 } else if (state->clip->getYMinI() == state->clip->getYMaxI()) {
2514 linePosI = state->clip->getYMinI();
2515 adjustLine = gTrue;
2516 } else if (state->clip->getYMinI() == state->clip->getYMaxI() - 1) {
2517 adjustLine = gTrue;
2518 linePosI = splashFloor(state->clip->getYMin() + state->lineWidth);
2519 }
2520 }
2521
2522 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue,
2523 adjustLine, linePosI);
2524 if (vectorAntialias && !inShading) {
2525 xPath->aaScale();
2526 }
2527 xPath->sort();
2528 yMinI = state->clip->getYMinI();
2529 yMaxI = state->clip->getYMaxI();
2530 if (vectorAntialias && !inShading) {
2531 yMinI = yMinI * splashAASize;
2532 yMaxI = (yMaxI + 1) * splashAASize - 1;
2533 }
2534 scanner = new SplashXPathScanner(xPath, eo, yMinI, yMaxI);
2535
2536 // get the min and max x and y values
2537 if (vectorAntialias && !inShading) {
2538 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
2539 } else {
2540 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2541 }
2542
2543 if (eo && (yMinI == yMaxI || xMinI == xMaxI) && thinLineMode != splashThinLineDefault) {
2544 SplashCoord delta, xMinFP, yMinFP, xMaxFP, yMaxFP;
2545 getBBoxFP(path, &xMinFP, &yMinFP, &xMaxFP, &yMaxFP);
2546 delta = (yMinI == yMaxI) ? yMaxFP - yMinFP : xMaxFP - xMinFP;
2547 if (delta < 0.2) {
2548 opClipRes = splashClipAllOutside;
2549 delete scanner;
2550 delete xPath;
2551 return splashOk;
2552 }
2553 }
2554
2555 // check clipping
2556 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2557 != splashClipAllOutside) {
2558 if (scanner->hasPartialClip()) {
2559 clipRes = splashClipPartial;
2560 }
2561
2562 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(alpha * 255),
2563 vectorAntialias && !inShading, gFalse);
2564
2565 // draw the spans
2566 if (vectorAntialias && !inShading) {
2567 for (y = yMinI; y <= yMaxI; ++y) {
2568 scanner->renderAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2569 if (clipRes != splashClipAllInside) {
2570 state->clip->clipAALine(aaBuf, &x0, &x1, y, thinLineMode != splashThinLineDefault && xMinI == xMaxI);
2571 }
2572 Guchar lineShape = 255;
2573 GBool adjustLine = gFalse;
2574 if (thinLineMode == splashThinLineShape && (xMinI == xMaxI || yMinI == yMaxI)) {
2575 // compute line shape for thin lines:
2576 SplashCoord mx, my, delta;
2577 transform(state->matrix, 0, 0, &mx, &my);
2578 transform(state->matrix, state->lineWidth, 0, &delta, &my);
2579 adjustLine = gTrue;
2580 lineShape = clip255((delta - mx) * 255);
2581 }
2582 drawAALine(&pipe, x0, x1, y, adjustLine, lineShape);
2583 }
2584 } else {
2585 for (y = yMinI; y <= yMaxI; ++y) {
2586 while (scanner->getNextSpan(y, &x0, &x1)) {
2587 if (clipRes == splashClipAllInside) {
2588 drawSpan(&pipe, x0, x1, y, gTrue);
2589 } else {
2590 // limit the x range
2591 if (x0 < state->clip->getXMinI()) {
2592 x0 = state->clip->getXMinI();
2593 }
2594 if (x1 > state->clip->getXMaxI()) {
2595 x1 = state->clip->getXMaxI();
2596 }
2597 clipRes2 = state->clip->testSpan(x0, x1, y);
2598 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2599 }
2600 }
2601 }
2602 }
2603 }
2604 opClipRes = clipRes;
2605
2606 delete scanner;
2607 delete xPath;
2608 return splashOk;
2609 }
2610
pathAllOutside(SplashPath * path)2611 GBool Splash::pathAllOutside(SplashPath *path) {
2612 SplashCoord xMin1, yMin1, xMax1, yMax1;
2613 SplashCoord xMin2, yMin2, xMax2, yMax2;
2614 SplashCoord x, y;
2615 int xMinI, yMinI, xMaxI, yMaxI;
2616 int i;
2617
2618 xMin1 = xMax1 = path->pts[0].x;
2619 yMin1 = yMax1 = path->pts[0].y;
2620 for (i = 1; i < path->length; ++i) {
2621 if (path->pts[i].x < xMin1) {
2622 xMin1 = path->pts[i].x;
2623 } else if (path->pts[i].x > xMax1) {
2624 xMax1 = path->pts[i].x;
2625 }
2626 if (path->pts[i].y < yMin1) {
2627 yMin1 = path->pts[i].y;
2628 } else if (path->pts[i].y > yMax1) {
2629 yMax1 = path->pts[i].y;
2630 }
2631 }
2632
2633 transform(state->matrix, xMin1, yMin1, &x, &y);
2634 xMin2 = xMax2 = x;
2635 yMin2 = yMax2 = y;
2636 transform(state->matrix, xMin1, yMax1, &x, &y);
2637 if (x < xMin2) {
2638 xMin2 = x;
2639 } else if (x > xMax2) {
2640 xMax2 = x;
2641 }
2642 if (y < yMin2) {
2643 yMin2 = y;
2644 } else if (y > yMax2) {
2645 yMax2 = y;
2646 }
2647 transform(state->matrix, xMax1, yMin1, &x, &y);
2648 if (x < xMin2) {
2649 xMin2 = x;
2650 } else if (x > xMax2) {
2651 xMax2 = x;
2652 }
2653 if (y < yMin2) {
2654 yMin2 = y;
2655 } else if (y > yMax2) {
2656 yMax2 = y;
2657 }
2658 transform(state->matrix, xMax1, yMax1, &x, &y);
2659 if (x < xMin2) {
2660 xMin2 = x;
2661 } else if (x > xMax2) {
2662 xMax2 = x;
2663 }
2664 if (y < yMin2) {
2665 yMin2 = y;
2666 } else if (y > yMax2) {
2667 yMax2 = y;
2668 }
2669 xMinI = splashFloor(xMin2);
2670 yMinI = splashFloor(yMin2);
2671 xMaxI = splashFloor(xMax2);
2672 yMaxI = splashFloor(yMax2);
2673
2674 return state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI) ==
2675 splashClipAllOutside;
2676 }
2677
xorFill(SplashPath * path,GBool eo)2678 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
2679 SplashPipe pipe;
2680 SplashXPath *xPath;
2681 SplashXPathScanner *scanner;
2682 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
2683 SplashClipResult clipRes, clipRes2;
2684 SplashBlendFunc origBlendFunc;
2685
2686 if (path->length == 0) {
2687 return splashErrEmptyPath;
2688 }
2689 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
2690 xPath->sort();
2691 scanner = new SplashXPathScanner(xPath, eo, state->clip->getYMinI(),
2692 state->clip->getYMaxI());
2693
2694 // get the min and max x and y values
2695 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
2696
2697 // check clipping
2698 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
2699 != splashClipAllOutside) {
2700 if (scanner->hasPartialClip()) {
2701 clipRes = splashClipPartial;
2702 }
2703
2704 origBlendFunc = state->blendFunc;
2705 state->blendFunc = &blendXor;
2706 pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 255, gFalse, gFalse);
2707
2708 // draw the spans
2709 for (y = yMinI; y <= yMaxI; ++y) {
2710 while (scanner->getNextSpan(y, &x0, &x1)) {
2711 if (clipRes == splashClipAllInside) {
2712 drawSpan(&pipe, x0, x1, y, gTrue);
2713 } else {
2714 // limit the x range
2715 if (x0 < state->clip->getXMinI()) {
2716 x0 = state->clip->getXMinI();
2717 }
2718 if (x1 > state->clip->getXMaxI()) {
2719 x1 = state->clip->getXMaxI();
2720 }
2721 clipRes2 = state->clip->testSpan(x0, x1, y);
2722 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
2723 }
2724 }
2725 }
2726 state->blendFunc = origBlendFunc;
2727 }
2728 opClipRes = clipRes;
2729
2730 delete scanner;
2731 delete xPath;
2732 return splashOk;
2733 }
2734
fillChar(SplashCoord x,SplashCoord y,int c,SplashFont * font)2735 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
2736 int c, SplashFont *font) {
2737 SplashGlyphBitmap glyph;
2738 SplashCoord xt, yt;
2739 int x0, y0, xFrac, yFrac;
2740 SplashClipResult clipRes;
2741
2742 if (debugMode) {
2743 printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
2744 (double)x, (double)y, c, c, c);
2745 }
2746 transform(state->matrix, x, y, &xt, &yt);
2747 x0 = splashFloor(xt);
2748 xFrac = splashFloor((xt - x0) * splashFontFraction);
2749 y0 = splashFloor(yt);
2750 yFrac = splashFloor((yt - y0) * splashFontFraction);
2751 if (!font->getGlyph(c, xFrac, yFrac, &glyph, x0, y0, state->clip, &clipRes)) {
2752 return splashErrNoGlyph;
2753 }
2754 if (clipRes != splashClipAllOutside) {
2755 fillGlyph2(x0, y0, &glyph, clipRes == splashClipAllInside);
2756 }
2757 opClipRes = clipRes;
2758 if (glyph.freeData) {
2759 gfree(glyph.data);
2760 }
2761 return splashOk;
2762 }
2763
fillGlyph(SplashCoord x,SplashCoord y,SplashGlyphBitmap * glyph)2764 void Splash::fillGlyph(SplashCoord x, SplashCoord y,
2765 SplashGlyphBitmap *glyph) {
2766 SplashCoord xt, yt;
2767 int x0, y0;
2768
2769 transform(state->matrix, x, y, &xt, &yt);
2770 x0 = splashFloor(xt);
2771 y0 = splashFloor(yt);
2772 SplashClipResult clipRes = state->clip->testRect(x0 - glyph->x,
2773 y0 - glyph->y,
2774 x0 - glyph->x + glyph->w - 1,
2775 y0 - glyph->y + glyph->h - 1);
2776 if (clipRes != splashClipAllOutside) {
2777 fillGlyph2(x0, y0, glyph, clipRes == splashClipAllInside);
2778 }
2779 opClipRes = clipRes;
2780 }
2781
fillGlyph2(int x0,int y0,SplashGlyphBitmap * glyph,GBool noClip)2782 void Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph, GBool noClip) {
2783 SplashPipe pipe;
2784 int alpha0;
2785 Guchar alpha;
2786 Guchar *p;
2787 int x1, y1, xx, xx1, yy;
2788
2789 p = glyph->data;
2790 int xStart = x0 - glyph->x;
2791 int yStart = y0 - glyph->y;
2792 int xxLimit = glyph->w;
2793 int yyLimit = glyph->h;
2794 int xShift = 0;
2795
2796 if (yStart < 0)
2797 {
2798 p += (glyph->aa ? glyph->w : splashCeil(glyph->w / 8.0)) * -yStart; // move p to the beginning of the first painted row
2799 yyLimit += yStart;
2800 yStart = 0;
2801 }
2802
2803 if (xStart < 0)
2804 {
2805 if (glyph->aa) {
2806 p += -xStart;
2807 } else {
2808 p += (-xStart) / 8;
2809 xShift = (-xStart) % 8;
2810 }
2811 xxLimit += xStart;
2812 xStart = 0;
2813 }
2814
2815 if (xxLimit + xStart >= bitmap->width) xxLimit = bitmap->width - xStart;
2816 if (yyLimit + yStart >= bitmap->height) yyLimit = bitmap->height - yStart;
2817
2818 if (noClip) {
2819 if (glyph->aa) {
2820 pipeInit(&pipe, xStart, yStart,
2821 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2822 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2823 pipeSetXY(&pipe, xStart, y1);
2824 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2825 alpha = p[xx];
2826 if (alpha != 0) {
2827 pipe.shape = alpha;
2828 (this->*pipe.run)(&pipe);
2829 updateModX(x1);
2830 updateModY(y1);
2831 } else {
2832 pipeIncX(&pipe);
2833 }
2834 }
2835 p += glyph->w;
2836 }
2837 } else {
2838 const int widthEight = splashCeil(glyph->w / 8.0);
2839
2840 pipeInit(&pipe, xStart, yStart,
2841 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2842 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2843 pipeSetXY(&pipe, xStart, y1);
2844 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2845 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2846 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2847 if (alpha0 & 0x80) {
2848 (this->*pipe.run)(&pipe);
2849 updateModX(x1);
2850 updateModY(y1);
2851 } else {
2852 pipeIncX(&pipe);
2853 }
2854 alpha0 <<= 1;
2855 }
2856 }
2857 p += widthEight;
2858 }
2859 }
2860 } else {
2861 if (glyph->aa) {
2862 pipeInit(&pipe, xStart, yStart,
2863 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
2864 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2865 pipeSetXY(&pipe, xStart, y1);
2866 for (xx = 0, x1 = xStart; xx < xxLimit; ++xx, ++x1) {
2867 if (state->clip->test(x1, y1)) {
2868 alpha = p[xx];
2869 if (alpha != 0) {
2870 pipe.shape = alpha;
2871 (this->*pipe.run)(&pipe);
2872 updateModX(x1);
2873 updateModY(y1);
2874 } else {
2875 pipeIncX(&pipe);
2876 }
2877 } else {
2878 pipeIncX(&pipe);
2879 }
2880 }
2881 p += glyph->w;
2882 }
2883 } else {
2884 const int widthEight = splashCeil(glyph->w / 8.0);
2885
2886 pipeInit(&pipe, xStart, yStart,
2887 state->fillPattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), gFalse, gFalse);
2888 for (yy = 0, y1 = yStart; yy < yyLimit; ++yy, ++y1) {
2889 pipeSetXY(&pipe, xStart, y1);
2890 for (xx = 0, x1 = xStart; xx < xxLimit; xx += 8) {
2891 alpha0 = (xShift > 0 ? (p[xx / 8] << xShift) | (p[xx / 8 + 1] >> (8 - xShift)) : p[xx / 8]);
2892 for (xx1 = 0; xx1 < 8 && xx + xx1 < xxLimit; ++xx1, ++x1) {
2893 if (state->clip->test(x1, y1)) {
2894 if (alpha0 & 0x80) {
2895 (this->*pipe.run)(&pipe);
2896 updateModX(x1);
2897 updateModY(y1);
2898 } else {
2899 pipeIncX(&pipe);
2900 }
2901 } else {
2902 pipeIncX(&pipe);
2903 }
2904 alpha0 <<= 1;
2905 }
2906 }
2907 p += widthEight;
2908 }
2909 }
2910 }
2911 }
2912
fillImageMask(SplashImageMaskSource src,void * srcData,int w,int h,SplashCoord * mat,GBool glyphMode)2913 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
2914 int w, int h, SplashCoord *mat,
2915 GBool glyphMode) {
2916 SplashBitmap *scaledMask;
2917 SplashClipResult clipRes;
2918 GBool minorAxisZero;
2919 int x0, y0, x1, y1, scaledWidth, scaledHeight;
2920 int yp;
2921
2922 if (debugMode) {
2923 printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2924 w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2925 (double)mat[3], (double)mat[4], (double)mat[5]);
2926 }
2927
2928 if (w == 0 && h == 0) return splashErrZeroImage;
2929
2930 // check for singular matrix
2931 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
2932 return splashErrSingularMatrix;
2933 }
2934
2935 minorAxisZero = mat[1] == 0 && mat[2] == 0;
2936
2937 // scaling only
2938 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
2939 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2940 y0 = imgCoordMungeLowerC(mat[5], glyphMode);
2941 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2942 y1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode);
2943 // make sure narrow images cover at least one pixel
2944 if (x0 == x1) {
2945 ++x1;
2946 }
2947 if (y0 == y1) {
2948 ++y1;
2949 }
2950 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2951 opClipRes = clipRes;
2952 if (clipRes != splashClipAllOutside) {
2953 scaledWidth = x1 - x0;
2954 scaledHeight = y1 - y0;
2955 yp = h / scaledHeight;
2956 if (yp < 0 || yp > INT_MAX - 1) {
2957 return splashErrBadArg;
2958 }
2959 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2960 blitMask(scaledMask, x0, y0, clipRes);
2961 delete scaledMask;
2962 }
2963
2964 // scaling plus vertical flip
2965 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
2966 x0 = imgCoordMungeLowerC(mat[4], glyphMode);
2967 y0 = imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
2968 x1 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode);
2969 y1 = imgCoordMungeUpperC(mat[5], glyphMode);
2970 // make sure narrow images cover at least one pixel
2971 if (x0 == x1) {
2972 ++x1;
2973 }
2974 if (y0 == y1) {
2975 ++y1;
2976 }
2977 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
2978 opClipRes = clipRes;
2979 if (clipRes != splashClipAllOutside) {
2980 scaledWidth = x1 - x0;
2981 scaledHeight = y1 - y0;
2982 yp = h / scaledHeight;
2983 if (yp < 0 || yp > INT_MAX - 1) {
2984 return splashErrBadArg;
2985 }
2986 scaledMask = scaleMask(src, srcData, w, h, scaledWidth, scaledHeight);
2987 vertFlipImage(scaledMask, scaledWidth, scaledHeight, 1);
2988 blitMask(scaledMask, x0, y0, clipRes);
2989 delete scaledMask;
2990 }
2991
2992 // all other cases
2993 } else {
2994 arbitraryTransformMask(src, srcData, w, h, mat, glyphMode);
2995 }
2996
2997 return splashOk;
2998 }
2999
arbitraryTransformMask(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,SplashCoord * mat,GBool glyphMode)3000 void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData,
3001 int srcWidth, int srcHeight,
3002 SplashCoord *mat, GBool glyphMode) {
3003 SplashBitmap *scaledMask;
3004 SplashClipResult clipRes, clipRes2;
3005 SplashPipe pipe;
3006 int scaledWidth, scaledHeight, t0, t1;
3007 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3008 SplashCoord vx[4], vy[4];
3009 int xMin, yMin, xMax, yMax;
3010 ImageSection section[3];
3011 int nSections;
3012 int y, xa, xb, x, i, xx, yy;
3013
3014 // compute the four vertices of the target quadrilateral
3015 vx[0] = mat[4]; vy[0] = mat[5];
3016 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3017 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3018 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3019
3020 // clipping
3021 xMin = imgCoordMungeLowerC(vx[0], glyphMode);
3022 xMax = imgCoordMungeUpperC(vx[0], glyphMode);
3023 yMin = imgCoordMungeLowerC(vy[0], glyphMode);
3024 yMax = imgCoordMungeUpperC(vy[0], glyphMode);
3025 for (i = 1; i < 4; ++i) {
3026 t0 = imgCoordMungeLowerC(vx[i], glyphMode);
3027 if (t0 < xMin) {
3028 xMin = t0;
3029 }
3030 t0 = imgCoordMungeUpperC(vx[i], glyphMode);
3031 if (t0 > xMax) {
3032 xMax = t0;
3033 }
3034 t1 = imgCoordMungeLowerC(vy[i], glyphMode);
3035 if (t1 < yMin) {
3036 yMin = t1;
3037 }
3038 t1 = imgCoordMungeUpperC(vy[i], glyphMode);
3039 if (t1 > yMax) {
3040 yMax = t1;
3041 }
3042 }
3043 clipRes = state->clip->testRect(xMin, yMin, xMax - 1, yMax - 1);
3044 opClipRes = clipRes;
3045 if (clipRes == splashClipAllOutside) {
3046 return;
3047 }
3048
3049 // compute the scale factors
3050 if (mat[0] >= 0) {
3051 t0 = imgCoordMungeUpperC(mat[0] + mat[4], glyphMode) -
3052 imgCoordMungeLowerC(mat[4], glyphMode);
3053 } else {
3054 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3055 imgCoordMungeLowerC(mat[0] + mat[4], glyphMode);
3056 }
3057 if (mat[1] >= 0) {
3058 t1 = imgCoordMungeUpperC(mat[1] + mat[5], glyphMode) -
3059 imgCoordMungeLowerC(mat[5], glyphMode);
3060 } else {
3061 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3062 imgCoordMungeLowerC(mat[1] + mat[5], glyphMode);
3063 }
3064 scaledWidth = t0 > t1 ? t0 : t1;
3065 if (mat[2] >= 0) {
3066 t0 = imgCoordMungeUpperC(mat[2] + mat[4], glyphMode) -
3067 imgCoordMungeLowerC(mat[4], glyphMode);
3068 } else {
3069 t0 = imgCoordMungeUpperC(mat[4], glyphMode) -
3070 imgCoordMungeLowerC(mat[2] + mat[4], glyphMode);
3071 }
3072 if (mat[3] >= 0) {
3073 t1 = imgCoordMungeUpperC(mat[3] + mat[5], glyphMode) -
3074 imgCoordMungeLowerC(mat[5], glyphMode);
3075 } else {
3076 t1 = imgCoordMungeUpperC(mat[5], glyphMode) -
3077 imgCoordMungeLowerC(mat[3] + mat[5], glyphMode);
3078 }
3079 scaledHeight = t0 > t1 ? t0 : t1;
3080 if (scaledWidth == 0) {
3081 scaledWidth = 1;
3082 }
3083 if (scaledHeight == 0) {
3084 scaledHeight = 1;
3085 }
3086
3087 // compute the inverse transform (after scaling) matrix
3088 r00 = mat[0] / scaledWidth;
3089 r01 = mat[1] / scaledWidth;
3090 r10 = mat[2] / scaledHeight;
3091 r11 = mat[3] / scaledHeight;
3092 det = r00 * r11 - r01 * r10;
3093 if (splashAbs(det) < 1e-6) {
3094 // this should be caught by the singular matrix check in fillImageMask
3095 return;
3096 }
3097 ir00 = r11 / det;
3098 ir01 = -r01 / det;
3099 ir10 = -r10 / det;
3100 ir11 = r00 / det;
3101
3102 // scale the input image
3103 scaledMask = scaleMask(src, srcData, srcWidth, srcHeight,
3104 scaledWidth, scaledHeight);
3105 if (scaledMask->data == NULL) {
3106 error(errInternal, -1, "scaledMask->data is NULL in Splash::arbitraryTransformMask");
3107 delete scaledMask;
3108 return;
3109 }
3110
3111 // construct the three sections
3112 i = (vy[2] <= vy[3]) ? 2 : 3;
3113 if (vy[1] <= vy[i]) {
3114 i = 1;
3115 }
3116 if (vy[0] < vy[i] || (i != 3 && vy[0] == vy[i])) {
3117 i = 0;
3118 }
3119 if (vy[i] == vy[(i+1) & 3]) {
3120 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3121 section[0].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3122 if (vx[i] < vx[(i+1) & 3]) {
3123 section[0].ia0 = i;
3124 section[0].ia1 = (i+3) & 3;
3125 section[0].ib0 = (i+1) & 3;
3126 section[0].ib1 = (i+2) & 3;
3127 } else {
3128 section[0].ia0 = (i+1) & 3;
3129 section[0].ia1 = (i+2) & 3;
3130 section[0].ib0 = i;
3131 section[0].ib1 = (i+3) & 3;
3132 }
3133 nSections = 1;
3134 } else {
3135 section[0].y0 = imgCoordMungeLowerC(vy[i], glyphMode);
3136 section[2].y1 = imgCoordMungeUpperC(vy[(i+2) & 3], glyphMode) - 1;
3137 section[0].ia0 = section[0].ib0 = i;
3138 section[2].ia1 = section[2].ib1 = (i+2) & 3;
3139 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3140 section[0].ia1 = section[2].ia0 = (i+1) & 3;
3141 section[0].ib1 = section[2].ib0 = (i+3) & 3;
3142 } else {
3143 section[0].ia1 = section[2].ia0 = (i+3) & 3;
3144 section[0].ib1 = section[2].ib0 = (i+1) & 3;
3145 }
3146 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
3147 section[1].y0 = imgCoordMungeLowerC(vy[(i+1) & 3], glyphMode);
3148 section[2].y0 = imgCoordMungeUpperC(vy[(i+3) & 3], glyphMode);
3149 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3150 section[1].ia0 = (i+1) & 3;
3151 section[1].ia1 = (i+2) & 3;
3152 section[1].ib0 = i;
3153 section[1].ib1 = (i+3) & 3;
3154 } else {
3155 section[1].ia0 = i;
3156 section[1].ia1 = (i+3) & 3;
3157 section[1].ib0 = (i+1) & 3;
3158 section[1].ib1 = (i+2) & 3;
3159 }
3160 } else {
3161 section[1].y0 = imgCoordMungeLowerC(vy[(i+3) & 3], glyphMode);
3162 section[2].y0 = imgCoordMungeUpperC(vy[(i+1) & 3], glyphMode);
3163 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
3164 section[1].ia0 = i;
3165 section[1].ia1 = (i+1) & 3;
3166 section[1].ib0 = (i+3) & 3;
3167 section[1].ib1 = (i+2) & 3;
3168 } else {
3169 section[1].ia0 = (i+3) & 3;
3170 section[1].ia1 = (i+2) & 3;
3171 section[1].ib0 = i;
3172 section[1].ib1 = (i+1) & 3;
3173 }
3174 }
3175 section[0].y1 = section[1].y0 - 1;
3176 section[1].y1 = section[2].y0 - 1;
3177 nSections = 3;
3178 }
3179 for (i = 0; i < nSections; ++i) {
3180 section[i].xa0 = vx[section[i].ia0];
3181 section[i].ya0 = vy[section[i].ia0];
3182 section[i].xa1 = vx[section[i].ia1];
3183 section[i].ya1 = vy[section[i].ia1];
3184 section[i].xb0 = vx[section[i].ib0];
3185 section[i].yb0 = vy[section[i].ib0];
3186 section[i].xb1 = vx[section[i].ib1];
3187 section[i].yb1 = vy[section[i].ib1];
3188 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
3189 (section[i].ya1 - section[i].ya0);
3190 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
3191 (section[i].yb1 - section[i].yb0);
3192 }
3193
3194 // initialize the pixel pipe
3195 pipeInit(&pipe, 0, 0, state->fillPattern, NULL,
3196 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3197 if (vectorAntialias) {
3198 drawAAPixelInit();
3199 }
3200
3201 // make sure narrow images cover at least one pixel
3202 if (nSections == 1) {
3203 if (section[0].y0 == section[0].y1) {
3204 ++section[0].y1;
3205 clipRes = opClipRes = splashClipPartial;
3206 }
3207 } else {
3208 if (section[0].y0 == section[2].y1) {
3209 ++section[1].y1;
3210 clipRes = opClipRes = splashClipPartial;
3211 }
3212 }
3213
3214 // scan all pixels inside the target region
3215 for (i = 0; i < nSections; ++i) {
3216 for (y = section[i].y0; y <= section[i].y1; ++y) {
3217 xa = imgCoordMungeLowerC(section[i].xa0 +
3218 ((SplashCoord)y + 0.5 - section[i].ya0) *
3219 section[i].dxdya,
3220 glyphMode);
3221 xb = imgCoordMungeUpperC(section[i].xb0 +
3222 ((SplashCoord)y + 0.5 - section[i].yb0) *
3223 section[i].dxdyb,
3224 glyphMode);
3225 if (unlikely(xa < 0))
3226 xa = 0;
3227 // make sure narrow images cover at least one pixel
3228 if (xa == xb) {
3229 ++xb;
3230 }
3231 if (clipRes != splashClipAllInside) {
3232 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
3233 } else {
3234 clipRes2 = clipRes;
3235 }
3236 for (x = xa; x < xb; ++x) {
3237 // map (x+0.5, y+0.5) back to the scaled image
3238 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
3239 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3240 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
3241 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3242 // xx should always be within bounds, but floating point
3243 // inaccuracy can cause problems
3244 if (xx < 0) {
3245 xx = 0;
3246 } else if (xx >= scaledWidth) {
3247 xx = scaledWidth - 1;
3248 }
3249 if (yy < 0) {
3250 yy = 0;
3251 } else if (yy >= scaledHeight) {
3252 yy = scaledHeight - 1;
3253 }
3254 pipe.shape = scaledMask->data[yy * scaledWidth + xx];
3255 if (vectorAntialias && clipRes2 != splashClipAllInside) {
3256 drawAAPixel(&pipe, x, y);
3257 } else {
3258 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
3259 }
3260 }
3261 }
3262 }
3263
3264 delete scaledMask;
3265 }
3266
3267 // Scale an image mask into a SplashBitmap.
scaleMask(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight)3268 SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData,
3269 int srcWidth, int srcHeight,
3270 int scaledWidth, int scaledHeight) {
3271 SplashBitmap *dest;
3272
3273 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8,
3274 gFalse);
3275 if (scaledHeight < srcHeight) {
3276 if (scaledWidth < srcWidth) {
3277 scaleMaskYdXd(src, srcData, srcWidth, srcHeight,
3278 scaledWidth, scaledHeight, dest);
3279 } else {
3280 scaleMaskYdXu(src, srcData, srcWidth, srcHeight,
3281 scaledWidth, scaledHeight, dest);
3282 }
3283 } else {
3284 if (scaledWidth < srcWidth) {
3285 scaleMaskYuXd(src, srcData, srcWidth, srcHeight,
3286 scaledWidth, scaledHeight, dest);
3287 } else {
3288 scaleMaskYuXu(src, srcData, srcWidth, srcHeight,
3289 scaledWidth, scaledHeight, dest);
3290 }
3291 }
3292 return dest;
3293 }
3294
scaleMaskYdXd(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)3295 void Splash::scaleMaskYdXd(SplashImageMaskSource src, void *srcData,
3296 int srcWidth, int srcHeight,
3297 int scaledWidth, int scaledHeight,
3298 SplashBitmap *dest) {
3299 Guchar *lineBuf;
3300 Guint *pixBuf;
3301 Guint pix;
3302 Guchar *destPtr;
3303 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3304 int i, j;
3305
3306 // Bresenham parameters for y scale
3307 yp = srcHeight / scaledHeight;
3308 yq = srcHeight % scaledHeight;
3309
3310 // Bresenham parameters for x scale
3311 xp = srcWidth / scaledWidth;
3312 xq = srcWidth % scaledWidth;
3313
3314 // allocate buffers
3315 lineBuf = (Guchar *)gmalloc(srcWidth);
3316 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3317
3318 // init y scale Bresenham
3319 yt = 0;
3320
3321 destPtr = dest->data;
3322 for (y = 0; y < scaledHeight; ++y) {
3323
3324 // y scale Bresenham
3325 if ((yt += yq) >= scaledHeight) {
3326 yt -= scaledHeight;
3327 yStep = yp + 1;
3328 } else {
3329 yStep = yp;
3330 }
3331
3332 // read rows from image
3333 memset(pixBuf, 0, srcWidth * sizeof(int));
3334 for (i = 0; i < yStep; ++i) {
3335 (*src)(srcData, lineBuf);
3336 for (j = 0; j < srcWidth; ++j) {
3337 pixBuf[j] += lineBuf[j];
3338 }
3339 }
3340
3341 // init x scale Bresenham
3342 xt = 0;
3343 d0 = (255 << 23) / (yStep * xp);
3344 d1 = (255 << 23) / (yStep * (xp + 1));
3345
3346 xx = 0;
3347 for (x = 0; x < scaledWidth; ++x) {
3348
3349 // x scale Bresenham
3350 if ((xt += xq) >= scaledWidth) {
3351 xt -= scaledWidth;
3352 xStep = xp + 1;
3353 d = d1;
3354 } else {
3355 xStep = xp;
3356 d = d0;
3357 }
3358
3359 // compute the final pixel
3360 pix = 0;
3361 for (i = 0; i < xStep; ++i) {
3362 pix += pixBuf[xx++];
3363 }
3364 // (255 * pix) / xStep * yStep
3365 pix = (pix * d) >> 23;
3366
3367 // store the pixel
3368 *destPtr++ = (Guchar)pix;
3369 }
3370 }
3371
3372 gfree(pixBuf);
3373 gfree(lineBuf);
3374 }
3375
scaleMaskYdXu(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)3376 void Splash::scaleMaskYdXu(SplashImageMaskSource src, void *srcData,
3377 int srcWidth, int srcHeight,
3378 int scaledWidth, int scaledHeight,
3379 SplashBitmap *dest) {
3380 Guchar *lineBuf;
3381 Guint *pixBuf;
3382 Guint pix;
3383 Guchar *destPtr;
3384 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
3385 int i, j;
3386
3387 destPtr = dest->data;
3388 if (destPtr == NULL) {
3389 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYdXu");
3390 return;
3391 }
3392
3393 // Bresenham parameters for y scale
3394 yp = srcHeight / scaledHeight;
3395 yq = srcHeight % scaledHeight;
3396
3397 // Bresenham parameters for x scale
3398 xp = scaledWidth / srcWidth;
3399 xq = scaledWidth % srcWidth;
3400
3401 // allocate buffers
3402 lineBuf = (Guchar *)gmalloc(srcWidth);
3403 pixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
3404
3405 // init y scale Bresenham
3406 yt = 0;
3407
3408 for (y = 0; y < scaledHeight; ++y) {
3409
3410 // y scale Bresenham
3411 if ((yt += yq) >= scaledHeight) {
3412 yt -= scaledHeight;
3413 yStep = yp + 1;
3414 } else {
3415 yStep = yp;
3416 }
3417
3418 // read rows from image
3419 memset(pixBuf, 0, srcWidth * sizeof(int));
3420 for (i = 0; i < yStep; ++i) {
3421 (*src)(srcData, lineBuf);
3422 for (j = 0; j < srcWidth; ++j) {
3423 pixBuf[j] += lineBuf[j];
3424 }
3425 }
3426
3427 // init x scale Bresenham
3428 xt = 0;
3429 d = (255 << 23) / yStep;
3430
3431 for (x = 0; x < srcWidth; ++x) {
3432
3433 // x scale Bresenham
3434 if ((xt += xq) >= srcWidth) {
3435 xt -= srcWidth;
3436 xStep = xp + 1;
3437 } else {
3438 xStep = xp;
3439 }
3440
3441 // compute the final pixel
3442 pix = pixBuf[x];
3443 // (255 * pix) / yStep
3444 pix = (pix * d) >> 23;
3445
3446 // store the pixel
3447 for (i = 0; i < xStep; ++i) {
3448 *destPtr++ = (Guchar)pix;
3449 }
3450 }
3451 }
3452
3453 gfree(pixBuf);
3454 gfree(lineBuf);
3455 }
3456
scaleMaskYuXd(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)3457 void Splash::scaleMaskYuXd(SplashImageMaskSource src, void *srcData,
3458 int srcWidth, int srcHeight,
3459 int scaledWidth, int scaledHeight,
3460 SplashBitmap *dest) {
3461 Guchar *lineBuf;
3462 Guint pix;
3463 Guchar *destPtr0, *destPtr;
3464 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, d, d0, d1;
3465 int i;
3466
3467 destPtr0 = dest->data;
3468 if (destPtr0 == NULL) {
3469 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXd");
3470 return;
3471 }
3472
3473 // Bresenham parameters for y scale
3474 yp = scaledHeight / srcHeight;
3475 yq = scaledHeight % srcHeight;
3476
3477 // Bresenham parameters for x scale
3478 xp = srcWidth / scaledWidth;
3479 xq = srcWidth % scaledWidth;
3480
3481 // allocate buffers
3482 lineBuf = (Guchar *)gmalloc(srcWidth);
3483
3484 // init y scale Bresenham
3485 yt = 0;
3486
3487 for (y = 0; y < srcHeight; ++y) {
3488
3489 // y scale Bresenham
3490 if ((yt += yq) >= srcHeight) {
3491 yt -= srcHeight;
3492 yStep = yp + 1;
3493 } else {
3494 yStep = yp;
3495 }
3496
3497 // read row from image
3498 (*src)(srcData, lineBuf);
3499
3500 // init x scale Bresenham
3501 xt = 0;
3502 d0 = (255 << 23) / xp;
3503 d1 = (255 << 23) / (xp + 1);
3504
3505 xx = 0;
3506 for (x = 0; x < scaledWidth; ++x) {
3507
3508 // x scale Bresenham
3509 if ((xt += xq) >= scaledWidth) {
3510 xt -= scaledWidth;
3511 xStep = xp + 1;
3512 d = d1;
3513 } else {
3514 xStep = xp;
3515 d = d0;
3516 }
3517
3518 // compute the final pixel
3519 pix = 0;
3520 for (i = 0; i < xStep; ++i) {
3521 pix += lineBuf[xx++];
3522 }
3523 // (255 * pix) / xStep
3524 pix = (pix * d) >> 23;
3525
3526 // store the pixel
3527 for (i = 0; i < yStep; ++i) {
3528 destPtr = destPtr0 + i * scaledWidth + x;
3529 *destPtr = (Guchar)pix;
3530 }
3531 }
3532
3533 destPtr0 += yStep * scaledWidth;
3534 }
3535
3536 gfree(lineBuf);
3537 }
3538
scaleMaskYuXu(SplashImageMaskSource src,void * srcData,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)3539 void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData,
3540 int srcWidth, int srcHeight,
3541 int scaledWidth, int scaledHeight,
3542 SplashBitmap *dest) {
3543 Guchar *lineBuf;
3544 Guint pix;
3545 Guchar *destPtr0, *destPtr;
3546 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
3547 int i, j;
3548
3549 destPtr0 = dest->data;
3550 if (destPtr0 == NULL) {
3551 error(errInternal, -1, "dest->data is NULL in Splash::scaleMaskYuXu");
3552 return;
3553 }
3554
3555 // Bresenham parameters for y scale
3556 yp = scaledHeight / srcHeight;
3557 yq = scaledHeight % srcHeight;
3558
3559 // Bresenham parameters for x scale
3560 xp = scaledWidth / srcWidth;
3561 xq = scaledWidth % srcWidth;
3562
3563 // allocate buffers
3564 lineBuf = (Guchar *)gmalloc(srcWidth);
3565
3566 // init y scale Bresenham
3567 yt = 0;
3568
3569 for (y = 0; y < srcHeight; ++y) {
3570
3571 // y scale Bresenham
3572 if ((yt += yq) >= srcHeight) {
3573 yt -= srcHeight;
3574 yStep = yp + 1;
3575 } else {
3576 yStep = yp;
3577 }
3578
3579 // read row from image
3580 (*src)(srcData, lineBuf);
3581
3582 // init x scale Bresenham
3583 xt = 0;
3584
3585 xx = 0;
3586 for (x = 0; x < srcWidth; ++x) {
3587
3588 // x scale Bresenham
3589 if ((xt += xq) >= srcWidth) {
3590 xt -= srcWidth;
3591 xStep = xp + 1;
3592 } else {
3593 xStep = xp;
3594 }
3595
3596 // compute the final pixel
3597 pix = lineBuf[x] ? 255 : 0;
3598
3599 // store the pixel
3600 for (i = 0; i < yStep; ++i) {
3601 for (j = 0; j < xStep; ++j) {
3602 destPtr = destPtr0 + i * scaledWidth + xx + j;
3603 *destPtr++ = (Guchar)pix;
3604 }
3605 }
3606
3607 xx += xStep;
3608 }
3609
3610 destPtr0 += yStep * scaledWidth;
3611 }
3612
3613 gfree(lineBuf);
3614 }
3615
blitMask(SplashBitmap * src,int xDest,int yDest,SplashClipResult clipRes)3616 void Splash::blitMask(SplashBitmap *src, int xDest, int yDest,
3617 SplashClipResult clipRes) {
3618 SplashPipe pipe;
3619 Guchar *p;
3620 int w, h, x, y;
3621
3622 w = src->getWidth();
3623 h = src->getHeight();
3624 p = src->getDataPtr();
3625 if (p == NULL) {
3626 error(errInternal, -1, "src->getDataPtr() is NULL in Splash::blitMask");
3627 return;
3628 }
3629 if (vectorAntialias && clipRes != splashClipAllInside) {
3630 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3631 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3632 drawAAPixelInit();
3633 for (y = 0; y < h; ++y) {
3634 for (x = 0; x < w; ++x) {
3635 pipe.shape = *p++;
3636 drawAAPixel(&pipe, xDest + x, yDest + y);
3637 }
3638 }
3639 } else {
3640 pipeInit(&pipe, xDest, yDest, state->fillPattern, NULL,
3641 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
3642 if (clipRes == splashClipAllInside) {
3643 for (y = 0; y < h; ++y) {
3644 pipeSetXY(&pipe, xDest, yDest + y);
3645 for (x = 0; x < w; ++x) {
3646 if (*p) {
3647 pipe.shape = *p;
3648 (this->*pipe.run)(&pipe);
3649 } else {
3650 pipeIncX(&pipe);
3651 }
3652 ++p;
3653 }
3654 }
3655 updateModX(xDest);
3656 updateModX(xDest + w - 1);
3657 updateModY(yDest);
3658 updateModY(yDest + h - 1);
3659 } else {
3660 for (y = 0; y < h; ++y) {
3661 pipeSetXY(&pipe, xDest, yDest + y);
3662 for (x = 0; x < w; ++x) {
3663 if (*p && state->clip->test(xDest + x, yDest + y)) {
3664 pipe.shape = *p;
3665 (this->*pipe.run)(&pipe);
3666 updateModX(xDest + x);
3667 updateModY(yDest + y);
3668 } else {
3669 pipeIncX(&pipe);
3670 }
3671 ++p;
3672 }
3673 }
3674 }
3675 }
3676 }
3677
drawImage(SplashImageSource src,SplashICCTransform tf,void * srcData,SplashColorMode srcMode,GBool srcAlpha,int w,int h,SplashCoord * mat,GBool interpolate,GBool tilingPattern)3678 SplashError Splash::drawImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3679 SplashColorMode srcMode, GBool srcAlpha,
3680 int w, int h, SplashCoord *mat, GBool interpolate,
3681 GBool tilingPattern) {
3682 GBool ok;
3683 SplashBitmap *scaledImg;
3684 SplashClipResult clipRes;
3685 GBool minorAxisZero;
3686 int x0, y0, x1, y1, scaledWidth, scaledHeight;
3687 int nComps;
3688 int yp;
3689
3690 if (debugMode) {
3691 printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
3692 srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
3693 (double)mat[3], (double)mat[4], (double)mat[5]);
3694 }
3695
3696 // check color modes
3697 ok = gFalse; // make gcc happy
3698 nComps = 0; // make gcc happy
3699 switch (bitmap->mode) {
3700 case splashModeMono1:
3701 case splashModeMono8:
3702 ok = srcMode == splashModeMono8;
3703 nComps = 1;
3704 break;
3705 case splashModeRGB8:
3706 ok = srcMode == splashModeRGB8;
3707 nComps = 3;
3708 break;
3709 case splashModeXBGR8:
3710 ok = srcMode == splashModeXBGR8;
3711 nComps = 4;
3712 break;
3713 case splashModeBGR8:
3714 ok = srcMode == splashModeBGR8;
3715 nComps = 3;
3716 break;
3717 #if SPLASH_CMYK
3718 case splashModeCMYK8:
3719 ok = srcMode == splashModeCMYK8;
3720 nComps = 4;
3721 break;
3722 case splashModeDeviceN8:
3723 ok = srcMode == splashModeDeviceN8;
3724 nComps = SPOT_NCOMPS+4;
3725 break;
3726 #endif
3727 default:
3728 ok = gFalse;
3729 break;
3730 }
3731 if (!ok) {
3732 return splashErrModeMismatch;
3733 }
3734
3735 // check for singular matrix
3736 if (!splashCheckDet(mat[0], mat[1], mat[2], mat[3], 0.000001)) {
3737 return splashErrSingularMatrix;
3738 }
3739
3740 minorAxisZero = mat[1] == 0 && mat[2] == 0;
3741
3742 // scaling only
3743 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
3744 x0 = imgCoordMungeLower(mat[4]);
3745 y0 = imgCoordMungeLower(mat[5]);
3746 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3747 y1 = imgCoordMungeUpper(mat[3] + mat[5]);
3748 // make sure narrow images cover at least one pixel
3749 if (x0 == x1) {
3750 ++x1;
3751 }
3752 if (y0 == y1) {
3753 ++y1;
3754 }
3755 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3756 opClipRes = clipRes;
3757 if (clipRes != splashClipAllOutside) {
3758 scaledWidth = x1 - x0;
3759 scaledHeight = y1 - y0;
3760 yp = h / scaledHeight;
3761 if (yp < 0 || yp > INT_MAX - 1) {
3762 return splashErrBadArg;
3763 }
3764 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3765 scaledWidth, scaledHeight, interpolate, tilingPattern);
3766 if (scaledImg == NULL) {
3767 return splashErrBadArg;
3768 }
3769 if (tf != NULL) {
3770 (*tf)(srcData, scaledImg);
3771 }
3772 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3773 delete scaledImg;
3774 }
3775
3776 // scaling plus vertical flip
3777 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
3778 x0 = imgCoordMungeLower(mat[4]);
3779 y0 = imgCoordMungeLower(mat[3] + mat[5]);
3780 x1 = imgCoordMungeUpper(mat[0] + mat[4]);
3781 y1 = imgCoordMungeUpper(mat[5]);
3782 if (x0 == x1) {
3783 if (mat[4] + mat[0] * 0.5 < x0) {
3784 --x0;
3785 } else {
3786 ++x1;
3787 }
3788 }
3789 if (y0 == y1) {
3790 if (mat[5] + mat[1] * 0.5 < y0) {
3791 --y0;
3792 } else {
3793 ++y1;
3794 }
3795 }
3796 clipRes = state->clip->testRect(x0, y0, x1 - 1, y1 - 1);
3797 opClipRes = clipRes;
3798 if (clipRes != splashClipAllOutside) {
3799 scaledWidth = x1 - x0;
3800 scaledHeight = y1 - y0;
3801 yp = h / scaledHeight;
3802 if (yp < 0 || yp > INT_MAX - 1) {
3803 return splashErrBadArg;
3804 }
3805 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, w, h,
3806 scaledWidth, scaledHeight, interpolate, tilingPattern);
3807 if (scaledImg == NULL) {
3808 return splashErrBadArg;
3809 }
3810 if (tf != NULL) {
3811 (*tf)(srcData, scaledImg);
3812 }
3813 vertFlipImage(scaledImg, scaledWidth, scaledHeight, nComps);
3814 blitImage(scaledImg, srcAlpha, x0, y0, clipRes);
3815 delete scaledImg;
3816 }
3817
3818 // all other cases
3819 } else {
3820 return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha,
3821 w, h, mat, interpolate, tilingPattern);
3822 }
3823
3824 return splashOk;
3825 }
3826
arbitraryTransformImage(SplashImageSource src,SplashICCTransform tf,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,SplashCoord * mat,GBool interpolate,GBool tilingPattern)3827 SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData,
3828 SplashColorMode srcMode, int nComps,
3829 GBool srcAlpha,
3830 int srcWidth, int srcHeight,
3831 SplashCoord *mat, GBool interpolate,
3832 GBool tilingPattern) {
3833 SplashBitmap *scaledImg;
3834 SplashClipResult clipRes, clipRes2;
3835 SplashPipe pipe;
3836 SplashColor pixel;
3837 int scaledWidth, scaledHeight, t0, t1, th;
3838 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3839 SplashCoord vx[4], vy[4];
3840 int xMin, yMin, xMax, yMax;
3841 ImageSection section[3];
3842 int nSections;
3843 int y, xa, xb, x, i, xx, yy, yp;
3844
3845 // compute the four vertices of the target quadrilateral
3846 vx[0] = mat[4]; vy[0] = mat[5];
3847 vx[1] = mat[2] + mat[4]; vy[1] = mat[3] + mat[5];
3848 vx[2] = mat[0] + mat[2] + mat[4]; vy[2] = mat[1] + mat[3] + mat[5];
3849 vx[3] = mat[0] + mat[4]; vy[3] = mat[1] + mat[5];
3850
3851 // clipping
3852 xMin = imgCoordMungeLower(vx[0]);
3853 xMax = imgCoordMungeUpper(vx[0]);
3854 yMin = imgCoordMungeLower(vy[0]);
3855 yMax = imgCoordMungeUpper(vy[0]);
3856 for (i = 1; i < 4; ++i) {
3857 t0 = imgCoordMungeLower(vx[i]);
3858 if (t0 < xMin) {
3859 xMin = t0;
3860 }
3861 t0 = imgCoordMungeUpper(vx[i]);
3862 if (t0 > xMax) {
3863 xMax = t0;
3864 }
3865 t1 = imgCoordMungeLower(vy[i]);
3866 if (t1 < yMin) {
3867 yMin = t1;
3868 }
3869 t1 = imgCoordMungeUpper(vy[i]);
3870 if (t1 > yMax) {
3871 yMax = t1;
3872 }
3873 }
3874 clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
3875 opClipRes = clipRes;
3876 if (clipRes == splashClipAllOutside) {
3877 return splashOk;
3878 }
3879
3880 // compute the scale factors
3881 if (splashAbs(mat[0]) >= splashAbs(mat[1])) {
3882 scaledWidth = xMax - xMin;
3883 scaledHeight = yMax - yMin;
3884 } else {
3885 scaledWidth = yMax - yMin;
3886 scaledHeight = xMax - xMin;
3887 }
3888 if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) {
3889 if (mat[0] >= 0) {
3890 t0 = imgCoordMungeUpper(mat[0] + mat[4]) - imgCoordMungeLower(mat[4]);
3891 } else {
3892 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[0] + mat[4]);
3893 }
3894 if (mat[1] >= 0) {
3895 t1 = imgCoordMungeUpper(mat[1] + mat[5]) - imgCoordMungeLower(mat[5]);
3896 } else {
3897 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[1] + mat[5]);
3898 }
3899 scaledWidth = t0 > t1 ? t0 : t1;
3900 if (mat[2] >= 0) {
3901 t0 = imgCoordMungeUpper(mat[2] + mat[4]) - imgCoordMungeLower(mat[4]);
3902 if (splashAbs(mat[1]) >= 1) {
3903 th = imgCoordMungeUpper(mat[2]) - imgCoordMungeLower(mat[0] * mat[3] / mat[1]);
3904 if (th > t0) t0 = th;
3905 }
3906 } else {
3907 t0 = imgCoordMungeUpper(mat[4]) - imgCoordMungeLower(mat[2] + mat[4]);
3908 if (splashAbs(mat[1]) >= 1) {
3909 th = imgCoordMungeUpper(mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(mat[2]);
3910 if (th > t0) t0 = th;
3911 }
3912 }
3913 if (mat[3] >= 0) {
3914 t1 = imgCoordMungeUpper(mat[3] + mat[5]) - imgCoordMungeLower(mat[5]);
3915 if (splashAbs(mat[0]) >= 1) {
3916 th = imgCoordMungeUpper(mat[3]) - imgCoordMungeLower(mat[1] * mat[2] / mat[0]);
3917 if (th > t1) t1 = th;
3918 }
3919 } else {
3920 t1 = imgCoordMungeUpper(mat[5]) - imgCoordMungeLower(mat[3] + mat[5]);
3921 if (splashAbs(mat[0]) >= 1) {
3922 th = imgCoordMungeUpper(mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(mat[3]);
3923 if (th > t1) t1 = th;
3924 }
3925 }
3926 scaledHeight = t0 > t1 ? t0 : t1;
3927 }
3928 if (scaledWidth == 0) {
3929 scaledWidth = 1;
3930 }
3931 if (scaledHeight == 0) {
3932 scaledHeight = 1;
3933 }
3934
3935 // compute the inverse transform (after scaling) matrix
3936 r00 = mat[0] / scaledWidth;
3937 r01 = mat[1] / scaledWidth;
3938 r10 = mat[2] / scaledHeight;
3939 r11 = mat[3] / scaledHeight;
3940 det = r00 * r11 - r01 * r10;
3941 if (splashAbs(det) < 1e-6) {
3942 // this should be caught by the singular matrix check in drawImage
3943 return splashErrBadArg;
3944 }
3945 ir00 = r11 / det;
3946 ir01 = -r01 / det;
3947 ir10 = -r10 / det;
3948 ir11 = r00 / det;
3949
3950 // scale the input image
3951 yp = srcHeight / scaledHeight;
3952 if (yp < 0 || yp > INT_MAX - 1) {
3953 return splashErrBadArg;
3954 }
3955 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha,
3956 srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
3957
3958 if (scaledImg == NULL) {
3959 return splashErrBadArg;
3960 }
3961
3962 if (tf != NULL) {
3963 (*tf)(srcData, scaledImg);
3964 }
3965 // construct the three sections
3966 i = 0;
3967 if (vy[1] < vy[i]) {
3968 i = 1;
3969 }
3970 if (vy[2] < vy[i]) {
3971 i = 2;
3972 }
3973 if (vy[3] < vy[i]) {
3974 i = 3;
3975 }
3976 // NB: if using fixed point, 0.000001 will be truncated to zero,
3977 // so these two comparisons must be <=, not <
3978 if (splashAbs(vy[i] - vy[(i-1) & 3]) <= 0.000001 &&
3979 vy[(i-1) & 3] < vy[(i+1) & 3]) {
3980 i = (i-1) & 3;
3981 }
3982 if (splashAbs(vy[i] - vy[(i+1) & 3]) <= 0.000001) {
3983 section[0].y0 = imgCoordMungeLower(vy[i]);
3984 section[0].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
3985 if (vx[i] < vx[(i+1) & 3]) {
3986 section[0].ia0 = i;
3987 section[0].ia1 = (i+3) & 3;
3988 section[0].ib0 = (i+1) & 3;
3989 section[0].ib1 = (i+2) & 3;
3990 } else {
3991 section[0].ia0 = (i+1) & 3;
3992 section[0].ia1 = (i+2) & 3;
3993 section[0].ib0 = i;
3994 section[0].ib1 = (i+3) & 3;
3995 }
3996 nSections = 1;
3997 } else {
3998 section[0].y0 = imgCoordMungeLower(vy[i]);
3999 section[2].y1 = imgCoordMungeUpper(vy[(i+2) & 3]) - 1;
4000 section[0].ia0 = section[0].ib0 = i;
4001 section[2].ia1 = section[2].ib1 = (i+2) & 3;
4002 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4003 section[0].ia1 = section[2].ia0 = (i+1) & 3;
4004 section[0].ib1 = section[2].ib0 = (i+3) & 3;
4005 } else {
4006 section[0].ia1 = section[2].ia0 = (i+3) & 3;
4007 section[0].ib1 = section[2].ib0 = (i+1) & 3;
4008 }
4009 if (vy[(i+1) & 3] < vy[(i+3) & 3]) {
4010 section[1].y0 = imgCoordMungeLower(vy[(i+1) & 3]);
4011 section[2].y0 = imgCoordMungeUpper(vy[(i+3) & 3]);
4012 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4013 section[1].ia0 = (i+1) & 3;
4014 section[1].ia1 = (i+2) & 3;
4015 section[1].ib0 = i;
4016 section[1].ib1 = (i+3) & 3;
4017 } else {
4018 section[1].ia0 = i;
4019 section[1].ia1 = (i+3) & 3;
4020 section[1].ib0 = (i+1) & 3;
4021 section[1].ib1 = (i+2) & 3;
4022 }
4023 } else {
4024 section[1].y0 = imgCoordMungeLower(vy[(i+3) & 3]);
4025 section[2].y0 = imgCoordMungeUpper(vy[(i+1) & 3]);
4026 if (vx[(i+1) & 3] < vx[(i+3) & 3]) {
4027 section[1].ia0 = i;
4028 section[1].ia1 = (i+1) & 3;
4029 section[1].ib0 = (i+3) & 3;
4030 section[1].ib1 = (i+2) & 3;
4031 } else {
4032 section[1].ia0 = (i+3) & 3;
4033 section[1].ia1 = (i+2) & 3;
4034 section[1].ib0 = i;
4035 section[1].ib1 = (i+1) & 3;
4036 }
4037 }
4038 section[0].y1 = section[1].y0 - 1;
4039 section[1].y1 = section[2].y0 - 1;
4040 nSections = 3;
4041 }
4042 for (i = 0; i < nSections; ++i) {
4043 section[i].xa0 = vx[section[i].ia0];
4044 section[i].ya0 = vy[section[i].ia0];
4045 section[i].xa1 = vx[section[i].ia1];
4046 section[i].ya1 = vy[section[i].ia1];
4047 section[i].xb0 = vx[section[i].ib0];
4048 section[i].yb0 = vy[section[i].ib0];
4049 section[i].xb1 = vx[section[i].ib1];
4050 section[i].yb1 = vy[section[i].ib1];
4051 section[i].dxdya = (section[i].xa1 - section[i].xa0) /
4052 (section[i].ya1 - section[i].ya0);
4053 section[i].dxdyb = (section[i].xb1 - section[i].xb0) /
4054 (section[i].yb1 - section[i].yb0);
4055 }
4056
4057 // initialize the pixel pipe
4058 pipeInit(&pipe, 0, 0, NULL, pixel,
4059 (Guchar)splashRound(state->fillAlpha * 255),
4060 srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
4061 gFalse);
4062 if (vectorAntialias) {
4063 drawAAPixelInit();
4064 }
4065
4066 // make sure narrow images cover at least one pixel
4067 if (nSections == 1) {
4068 if (section[0].y0 == section[0].y1) {
4069 ++section[0].y1;
4070 clipRes = opClipRes = splashClipPartial;
4071 }
4072 } else {
4073 if (section[0].y0 == section[2].y1) {
4074 ++section[1].y1;
4075 clipRes = opClipRes = splashClipPartial;
4076 }
4077 }
4078
4079 // scan all pixels inside the target region
4080 for (i = 0; i < nSections; ++i) {
4081 for (y = section[i].y0; y <= section[i].y1; ++y) {
4082 xa = imgCoordMungeLower(section[i].xa0 +
4083 ((SplashCoord)y + 0.5 - section[i].ya0) *
4084 section[i].dxdya);
4085 if (unlikely(xa < 0))
4086 xa = 0;
4087 xb = imgCoordMungeUpper(section[i].xb0 +
4088 ((SplashCoord)y + 0.5 - section[i].yb0) *
4089 section[i].dxdyb);
4090 // make sure narrow images cover at least one pixel
4091 if (xa == xb) {
4092 ++xb;
4093 }
4094 if (clipRes != splashClipAllInside) {
4095 clipRes2 = state->clip->testSpan(xa, xb - 1, y);
4096 } else {
4097 clipRes2 = clipRes;
4098 }
4099 for (x = xa; x < xb; ++x) {
4100 // map (x+0.5, y+0.5) back to the scaled image
4101 xx = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir00 +
4102 ((SplashCoord)y + 0.5 - mat[5]) * ir10);
4103 yy = splashFloor(((SplashCoord)x + 0.5 - mat[4]) * ir01 +
4104 ((SplashCoord)y + 0.5 - mat[5]) * ir11);
4105 // xx should always be within bounds, but floating point
4106 // inaccuracy can cause problems
4107 if (xx < 0) {
4108 xx = 0;
4109 } else if (xx >= scaledWidth) {
4110 xx = scaledWidth - 1;
4111 }
4112 if (yy < 0) {
4113 yy = 0;
4114 } else if (yy >= scaledHeight) {
4115 yy = scaledHeight - 1;
4116 }
4117 scaledImg->getPixel(xx, yy, pixel);
4118 if (srcAlpha) {
4119 pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
4120 } else {
4121 pipe.shape = 255;
4122 }
4123 if (vectorAntialias && clipRes2 != splashClipAllInside) {
4124 drawAAPixel(&pipe, x, y);
4125 } else {
4126 drawPixel(&pipe, x, y, clipRes2 == splashClipAllInside);
4127 }
4128 }
4129 }
4130 }
4131
4132 delete scaledImg;
4133 return splashOk;
4134 }
4135
4136 // determine if a scaled image requires interpolation based on the scale and
4137 // the interpolate flag from the image dictionary
isImageInterpolationRequired(int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,GBool interpolate)4138 static GBool isImageInterpolationRequired(int srcWidth, int srcHeight,
4139 int scaledWidth, int scaledHeight,
4140 GBool interpolate) {
4141 if (interpolate)
4142 return gTrue;
4143
4144 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
4145 if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4)
4146 return gFalse;
4147
4148 return gTrue;
4149 }
4150
4151 // Scale an image into a SplashBitmap.
scaleImage(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,GBool interpolate,GBool tilingPattern)4152 SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData,
4153 SplashColorMode srcMode, int nComps,
4154 GBool srcAlpha, int srcWidth, int srcHeight,
4155 int scaledWidth, int scaledHeight, GBool interpolate, GBool tilingPattern) {
4156 SplashBitmap *dest;
4157
4158 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, gTrue, bitmap->getSeparationList());
4159 if (dest->getDataPtr() != NULL) {
4160 if (scaledHeight < srcHeight) {
4161 if (scaledWidth < srcWidth) {
4162 scaleImageYdXd(src, srcData, srcMode, nComps, srcAlpha,
4163 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4164 } else {
4165 scaleImageYdXu(src, srcData, srcMode, nComps, srcAlpha,
4166 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4167 }
4168 } else {
4169 if (scaledWidth < srcWidth) {
4170 scaleImageYuXd(src, srcData, srcMode, nComps, srcAlpha,
4171 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4172 } else {
4173 if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
4174 scaleImageYuXuBilinear(src, srcData, srcMode, nComps, srcAlpha,
4175 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4176 } else {
4177 scaleImageYuXu(src, srcData, srcMode, nComps, srcAlpha,
4178 srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
4179 }
4180 }
4181 }
4182 } else {
4183 delete dest;
4184 dest = NULL;
4185 }
4186 return dest;
4187 }
4188
scaleImageYdXd(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)4189 void Splash::scaleImageYdXd(SplashImageSource src, void *srcData,
4190 SplashColorMode srcMode, int nComps,
4191 GBool srcAlpha, int srcWidth, int srcHeight,
4192 int scaledWidth, int scaledHeight,
4193 SplashBitmap *dest) {
4194 Guchar *lineBuf, *alphaLineBuf;
4195 Guint *pixBuf, *alphaPixBuf;
4196 Guint pix0, pix1, pix2;
4197 #if SPLASH_CMYK
4198 Guint pix3;
4199 Guint pix[SPOT_NCOMPS+4], cp;
4200 #endif
4201 Guint alpha;
4202 Guchar *destPtr, *destAlphaPtr;
4203 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4204 int i, j;
4205
4206 // Bresenham parameters for y scale
4207 yp = srcHeight / scaledHeight;
4208 yq = srcHeight % scaledHeight;
4209
4210 // Bresenham parameters for x scale
4211 xp = srcWidth / scaledWidth;
4212 xq = srcWidth % scaledWidth;
4213
4214 // allocate buffers
4215 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4216 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4217 if (srcAlpha) {
4218 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4219 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4220 } else {
4221 alphaLineBuf = NULL;
4222 alphaPixBuf = NULL;
4223 }
4224
4225 // init y scale Bresenham
4226 yt = 0;
4227
4228 destPtr = dest->data;
4229 destAlphaPtr = dest->alpha;
4230 for (y = 0; y < scaledHeight; ++y) {
4231
4232 // y scale Bresenham
4233 if ((yt += yq) >= scaledHeight) {
4234 yt -= scaledHeight;
4235 yStep = yp + 1;
4236 } else {
4237 yStep = yp;
4238 }
4239
4240 // read rows from image
4241 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4242 if (srcAlpha) {
4243 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4244 }
4245 for (i = 0; i < yStep; ++i) {
4246 (*src)(srcData, lineBuf, alphaLineBuf);
4247 for (j = 0; j < srcWidth * nComps; ++j) {
4248 pixBuf[j] += lineBuf[j];
4249 }
4250 if (srcAlpha) {
4251 for (j = 0; j < srcWidth; ++j) {
4252 alphaPixBuf[j] += alphaLineBuf[j];
4253 }
4254 }
4255 }
4256
4257 // init x scale Bresenham
4258 xt = 0;
4259 d0 = (1 << 23) / (yStep * xp);
4260 d1 = (1 << 23) / (yStep * (xp + 1));
4261
4262 xx = xxa = 0;
4263 for (x = 0; x < scaledWidth; ++x) {
4264
4265 // x scale Bresenham
4266 if ((xt += xq) >= scaledWidth) {
4267 xt -= scaledWidth;
4268 xStep = xp + 1;
4269 d = d1;
4270 } else {
4271 xStep = xp;
4272 d = d0;
4273 }
4274
4275 switch (srcMode) {
4276
4277 case splashModeMono8:
4278
4279 // compute the final pixel
4280 pix0 = 0;
4281 for (i = 0; i < xStep; ++i) {
4282 pix0 += pixBuf[xx++];
4283 }
4284 // pix / xStep * yStep
4285 pix0 = (pix0 * d) >> 23;
4286
4287 // store the pixel
4288 *destPtr++ = (Guchar)pix0;
4289 break;
4290
4291 case splashModeRGB8:
4292
4293 // compute the final pixel
4294 pix0 = pix1 = pix2 = 0;
4295 for (i = 0; i < xStep; ++i) {
4296 pix0 += pixBuf[xx];
4297 pix1 += pixBuf[xx+1];
4298 pix2 += pixBuf[xx+2];
4299 xx += 3;
4300 }
4301 // pix / xStep * yStep
4302 pix0 = (pix0 * d) >> 23;
4303 pix1 = (pix1 * d) >> 23;
4304 pix2 = (pix2 * d) >> 23;
4305
4306 // store the pixel
4307 *destPtr++ = (Guchar)pix0;
4308 *destPtr++ = (Guchar)pix1;
4309 *destPtr++ = (Guchar)pix2;
4310 break;
4311
4312 case splashModeXBGR8:
4313
4314 // compute the final pixel
4315 pix0 = pix1 = pix2 = 0;
4316 for (i = 0; i < xStep; ++i) {
4317 pix0 += pixBuf[xx];
4318 pix1 += pixBuf[xx+1];
4319 pix2 += pixBuf[xx+2];
4320 xx += 4;
4321 }
4322 // pix / xStep * yStep
4323 pix0 = (pix0 * d) >> 23;
4324 pix1 = (pix1 * d) >> 23;
4325 pix2 = (pix2 * d) >> 23;
4326
4327 // store the pixel
4328 *destPtr++ = (Guchar)pix2;
4329 *destPtr++ = (Guchar)pix1;
4330 *destPtr++ = (Guchar)pix0;
4331 *destPtr++ = (Guchar)255;
4332 break;
4333
4334 case splashModeBGR8:
4335
4336 // compute the final pixel
4337 pix0 = pix1 = pix2 = 0;
4338 for (i = 0; i < xStep; ++i) {
4339 pix0 += pixBuf[xx];
4340 pix1 += pixBuf[xx+1];
4341 pix2 += pixBuf[xx+2];
4342 xx += 3;
4343 }
4344 // pix / xStep * yStep
4345 pix0 = (pix0 * d) >> 23;
4346 pix1 = (pix1 * d) >> 23;
4347 pix2 = (pix2 * d) >> 23;
4348
4349 // store the pixel
4350 *destPtr++ = (Guchar)pix2;
4351 *destPtr++ = (Guchar)pix1;
4352 *destPtr++ = (Guchar)pix0;
4353 break;
4354
4355 #if SPLASH_CMYK
4356 case splashModeCMYK8:
4357
4358 // compute the final pixel
4359 pix0 = pix1 = pix2 = pix3 = 0;
4360 for (i = 0; i < xStep; ++i) {
4361 pix0 += pixBuf[xx];
4362 pix1 += pixBuf[xx+1];
4363 pix2 += pixBuf[xx+2];
4364 pix3 += pixBuf[xx+3];
4365 xx += 4;
4366 }
4367 // pix / xStep * yStep
4368 pix0 = (pix0 * d) >> 23;
4369 pix1 = (pix1 * d) >> 23;
4370 pix2 = (pix2 * d) >> 23;
4371 pix3 = (pix3 * d) >> 23;
4372
4373 // store the pixel
4374 *destPtr++ = (Guchar)pix0;
4375 *destPtr++ = (Guchar)pix1;
4376 *destPtr++ = (Guchar)pix2;
4377 *destPtr++ = (Guchar)pix3;
4378 break;
4379 case splashModeDeviceN8:
4380
4381 // compute the final pixel
4382 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4383 pix[cp] = 0;
4384 for (i = 0; i < xStep; ++i) {
4385 for (cp = 0; cp < SPOT_NCOMPS+4; cp++) {
4386 pix[cp] += pixBuf[xx + cp];
4387 }
4388 xx += (SPOT_NCOMPS+4);
4389 }
4390 // pix / xStep * yStep
4391 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4392 pix[cp] = (pix[cp] * d) >> 23;
4393
4394 // store the pixel
4395 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
4396 *destPtr++ = (Guchar)pix[cp];
4397 break;
4398 #endif
4399
4400
4401 case splashModeMono1: // mono1 is not allowed
4402 default:
4403 break;
4404 }
4405
4406 // process alpha
4407 if (srcAlpha) {
4408 alpha = 0;
4409 for (i = 0; i < xStep; ++i, ++xxa) {
4410 alpha += alphaPixBuf[xxa];
4411 }
4412 // alpha / xStep * yStep
4413 alpha = (alpha * d) >> 23;
4414 *destAlphaPtr++ = (Guchar)alpha;
4415 }
4416 }
4417 }
4418
4419 gfree(alphaPixBuf);
4420 gfree(alphaLineBuf);
4421 gfree(pixBuf);
4422 gfree(lineBuf);
4423 }
4424
scaleImageYdXu(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)4425 void Splash::scaleImageYdXu(SplashImageSource src, void *srcData,
4426 SplashColorMode srcMode, int nComps,
4427 GBool srcAlpha, int srcWidth, int srcHeight,
4428 int scaledWidth, int scaledHeight,
4429 SplashBitmap *dest) {
4430 Guchar *lineBuf, *alphaLineBuf;
4431 Guint *pixBuf, *alphaPixBuf;
4432 Guint pix[splashMaxColorComps];
4433 Guint alpha;
4434 Guchar *destPtr, *destAlphaPtr;
4435 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
4436 int i, j;
4437
4438 // Bresenham parameters for y scale
4439 yp = srcHeight / scaledHeight;
4440 yq = srcHeight % scaledHeight;
4441
4442 // Bresenham parameters for x scale
4443 xp = scaledWidth / srcWidth;
4444 xq = scaledWidth % srcWidth;
4445
4446 // allocate buffers
4447 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4448 pixBuf = (Guint *)gmallocn(srcWidth, nComps * sizeof(int));
4449 if (srcAlpha) {
4450 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4451 alphaPixBuf = (Guint *)gmallocn(srcWidth, sizeof(int));
4452 } else {
4453 alphaLineBuf = NULL;
4454 alphaPixBuf = NULL;
4455 }
4456
4457 // init y scale Bresenham
4458 yt = 0;
4459
4460 destPtr = dest->data;
4461 destAlphaPtr = dest->alpha;
4462 for (y = 0; y < scaledHeight; ++y) {
4463
4464 // y scale Bresenham
4465 if ((yt += yq) >= scaledHeight) {
4466 yt -= scaledHeight;
4467 yStep = yp + 1;
4468 } else {
4469 yStep = yp;
4470 }
4471
4472 // read rows from image
4473 memset(pixBuf, 0, srcWidth * nComps * sizeof(int));
4474 if (srcAlpha) {
4475 memset(alphaPixBuf, 0, srcWidth * sizeof(int));
4476 }
4477 for (i = 0; i < yStep; ++i) {
4478 (*src)(srcData, lineBuf, alphaLineBuf);
4479 for (j = 0; j < srcWidth * nComps; ++j) {
4480 pixBuf[j] += lineBuf[j];
4481 }
4482 if (srcAlpha) {
4483 for (j = 0; j < srcWidth; ++j) {
4484 alphaPixBuf[j] += alphaLineBuf[j];
4485 }
4486 }
4487 }
4488
4489 // init x scale Bresenham
4490 xt = 0;
4491 d = (1 << 23) / yStep;
4492
4493 for (x = 0; x < srcWidth; ++x) {
4494
4495 // x scale Bresenham
4496 if ((xt += xq) >= srcWidth) {
4497 xt -= srcWidth;
4498 xStep = xp + 1;
4499 } else {
4500 xStep = xp;
4501 }
4502
4503 // compute the final pixel
4504 for (i = 0; i < nComps; ++i) {
4505 // pixBuf[] / yStep
4506 pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
4507 }
4508
4509 // store the pixel
4510 switch (srcMode) {
4511 case splashModeMono1: // mono1 is not allowed
4512 break;
4513 case splashModeMono8:
4514 for (i = 0; i < xStep; ++i) {
4515 *destPtr++ = (Guchar)pix[0];
4516 }
4517 break;
4518 case splashModeRGB8:
4519 for (i = 0; i < xStep; ++i) {
4520 *destPtr++ = (Guchar)pix[0];
4521 *destPtr++ = (Guchar)pix[1];
4522 *destPtr++ = (Guchar)pix[2];
4523 }
4524 break;
4525 case splashModeXBGR8:
4526 for (i = 0; i < xStep; ++i) {
4527 *destPtr++ = (Guchar)pix[2];
4528 *destPtr++ = (Guchar)pix[1];
4529 *destPtr++ = (Guchar)pix[0];
4530 *destPtr++ = (Guchar)255;
4531 }
4532 break;
4533 case splashModeBGR8:
4534 for (i = 0; i < xStep; ++i) {
4535 *destPtr++ = (Guchar)pix[2];
4536 *destPtr++ = (Guchar)pix[1];
4537 *destPtr++ = (Guchar)pix[0];
4538 }
4539 break;
4540 #if SPLASH_CMYK
4541 case splashModeCMYK8:
4542 for (i = 0; i < xStep; ++i) {
4543 *destPtr++ = (Guchar)pix[0];
4544 *destPtr++ = (Guchar)pix[1];
4545 *destPtr++ = (Guchar)pix[2];
4546 *destPtr++ = (Guchar)pix[3];
4547 }
4548 break;
4549 case splashModeDeviceN8:
4550 for (i = 0; i < xStep; ++i) {
4551 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4552 *destPtr++ = (Guchar)pix[cp];
4553 }
4554 break;
4555 #endif
4556 }
4557
4558 // process alpha
4559 if (srcAlpha) {
4560 // alphaPixBuf[] / yStep
4561 alpha = (alphaPixBuf[x] * d) >> 23;
4562 for (i = 0; i < xStep; ++i) {
4563 *destAlphaPtr++ = (Guchar)alpha;
4564 }
4565 }
4566 }
4567 }
4568
4569 gfree(alphaPixBuf);
4570 gfree(alphaLineBuf);
4571 gfree(pixBuf);
4572 gfree(lineBuf);
4573 }
4574
scaleImageYuXd(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)4575 void Splash::scaleImageYuXd(SplashImageSource src, void *srcData,
4576 SplashColorMode srcMode, int nComps,
4577 GBool srcAlpha, int srcWidth, int srcHeight,
4578 int scaledWidth, int scaledHeight,
4579 SplashBitmap *dest) {
4580 Guchar *lineBuf, *alphaLineBuf;
4581 Guint pix[splashMaxColorComps];
4582 Guint alpha;
4583 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4584 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4585 int i, j;
4586
4587 // Bresenham parameters for y scale
4588 yp = scaledHeight / srcHeight;
4589 yq = scaledHeight % srcHeight;
4590
4591 // Bresenham parameters for x scale
4592 xp = srcWidth / scaledWidth;
4593 xq = srcWidth % scaledWidth;
4594
4595 // allocate buffers
4596 lineBuf = (Guchar *)gmallocn_checkoverflow(srcWidth, nComps);
4597 if (unlikely(!lineBuf))
4598 return;
4599 if (srcAlpha) {
4600 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4601 } else {
4602 alphaLineBuf = NULL;
4603 }
4604
4605 // init y scale Bresenham
4606 yt = 0;
4607
4608 destPtr0 = dest->data;
4609 destAlphaPtr0 = dest->alpha;
4610 for (y = 0; y < srcHeight; ++y) {
4611
4612 // y scale Bresenham
4613 if ((yt += yq) >= srcHeight) {
4614 yt -= srcHeight;
4615 yStep = yp + 1;
4616 } else {
4617 yStep = yp;
4618 }
4619
4620 // read row from image
4621 (*src)(srcData, lineBuf, alphaLineBuf);
4622
4623 // init x scale Bresenham
4624 xt = 0;
4625 d0 = (1 << 23) / xp;
4626 d1 = (1 << 23) / (xp + 1);
4627
4628 xx = xxa = 0;
4629 for (x = 0; x < scaledWidth; ++x) {
4630
4631 // x scale Bresenham
4632 if ((xt += xq) >= scaledWidth) {
4633 xt -= scaledWidth;
4634 xStep = xp + 1;
4635 d = d1;
4636 } else {
4637 xStep = xp;
4638 d = d0;
4639 }
4640
4641 // compute the final pixel
4642 for (i = 0; i < nComps; ++i) {
4643 pix[i] = 0;
4644 }
4645 for (i = 0; i < xStep; ++i) {
4646 for (j = 0; j < nComps; ++j, ++xx) {
4647 pix[j] += lineBuf[xx];
4648 }
4649 }
4650 for (i = 0; i < nComps; ++i) {
4651 // pix[] / xStep
4652 pix[i] = (pix[i] * d) >> 23;
4653 }
4654
4655 // store the pixel
4656 switch (srcMode) {
4657 case splashModeMono1: // mono1 is not allowed
4658 break;
4659 case splashModeMono8:
4660 for (i = 0; i < yStep; ++i) {
4661 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4662 *destPtr++ = (Guchar)pix[0];
4663 }
4664 break;
4665 case splashModeRGB8:
4666 for (i = 0; i < yStep; ++i) {
4667 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4668 *destPtr++ = (Guchar)pix[0];
4669 *destPtr++ = (Guchar)pix[1];
4670 *destPtr++ = (Guchar)pix[2];
4671 }
4672 break;
4673 case splashModeXBGR8:
4674 for (i = 0; i < yStep; ++i) {
4675 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4676 *destPtr++ = (Guchar)pix[2];
4677 *destPtr++ = (Guchar)pix[1];
4678 *destPtr++ = (Guchar)pix[0];
4679 *destPtr++ = (Guchar)255;
4680 }
4681 break;
4682 case splashModeBGR8:
4683 for (i = 0; i < yStep; ++i) {
4684 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4685 *destPtr++ = (Guchar)pix[2];
4686 *destPtr++ = (Guchar)pix[1];
4687 *destPtr++ = (Guchar)pix[0];
4688 }
4689 break;
4690 #if SPLASH_CMYK
4691 case splashModeCMYK8:
4692 for (i = 0; i < yStep; ++i) {
4693 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4694 *destPtr++ = (Guchar)pix[0];
4695 *destPtr++ = (Guchar)pix[1];
4696 *destPtr++ = (Guchar)pix[2];
4697 *destPtr++ = (Guchar)pix[3];
4698 }
4699 break;
4700 case splashModeDeviceN8:
4701 for (i = 0; i < yStep; ++i) {
4702 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4703 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4704 *destPtr++ = (Guchar)pix[cp];
4705 }
4706 break;
4707 #endif
4708 }
4709
4710 // process alpha
4711 if (srcAlpha) {
4712 alpha = 0;
4713 for (i = 0; i < xStep; ++i, ++xxa) {
4714 alpha += alphaLineBuf[xxa];
4715 }
4716 // alpha / xStep
4717 alpha = (alpha * d) >> 23;
4718 for (i = 0; i < yStep; ++i) {
4719 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
4720 *destAlphaPtr = (Guchar)alpha;
4721 }
4722 }
4723 }
4724
4725 destPtr0 += yStep * scaledWidth * nComps;
4726 if (srcAlpha) {
4727 destAlphaPtr0 += yStep * scaledWidth;
4728 }
4729 }
4730
4731 gfree(alphaLineBuf);
4732 gfree(lineBuf);
4733 }
4734
scaleImageYuXu(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)4735 void Splash::scaleImageYuXu(SplashImageSource src, void *srcData,
4736 SplashColorMode srcMode, int nComps,
4737 GBool srcAlpha, int srcWidth, int srcHeight,
4738 int scaledWidth, int scaledHeight,
4739 SplashBitmap *dest) {
4740 Guchar *lineBuf, *alphaLineBuf;
4741 Guint pix[splashMaxColorComps];
4742 Guint alpha;
4743 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4744 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
4745 int i, j;
4746
4747 // Bresenham parameters for y scale
4748 yp = scaledHeight / srcHeight;
4749 yq = scaledHeight % srcHeight;
4750
4751 // Bresenham parameters for x scale
4752 xp = scaledWidth / srcWidth;
4753 xq = scaledWidth % srcWidth;
4754
4755 // allocate buffers
4756 lineBuf = (Guchar *)gmallocn(srcWidth, nComps);
4757 if (srcAlpha) {
4758 alphaLineBuf = (Guchar *)gmalloc(srcWidth);
4759 } else {
4760 alphaLineBuf = NULL;
4761 }
4762
4763 // init y scale Bresenham
4764 yt = 0;
4765
4766 destPtr0 = dest->data;
4767 destAlphaPtr0 = dest->alpha;
4768 for (y = 0; y < srcHeight; ++y) {
4769
4770 // y scale Bresenham
4771 if ((yt += yq) >= srcHeight) {
4772 yt -= srcHeight;
4773 yStep = yp + 1;
4774 } else {
4775 yStep = yp;
4776 }
4777
4778 // read row from image
4779 (*src)(srcData, lineBuf, alphaLineBuf);
4780
4781 // init x scale Bresenham
4782 xt = 0;
4783
4784 xx = 0;
4785 for (x = 0; x < srcWidth; ++x) {
4786
4787 // x scale Bresenham
4788 if ((xt += xq) >= srcWidth) {
4789 xt -= srcWidth;
4790 xStep = xp + 1;
4791 } else {
4792 xStep = xp;
4793 }
4794
4795 // compute the final pixel
4796 for (i = 0; i < nComps; ++i) {
4797 pix[i] = lineBuf[x * nComps + i];
4798 }
4799
4800 // store the pixel
4801 switch (srcMode) {
4802 case splashModeMono1: // mono1 is not allowed
4803 break;
4804 case splashModeMono8:
4805 for (i = 0; i < yStep; ++i) {
4806 for (j = 0; j < xStep; ++j) {
4807 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4808 *destPtr++ = (Guchar)pix[0];
4809 }
4810 }
4811 break;
4812 case splashModeRGB8:
4813 for (i = 0; i < yStep; ++i) {
4814 for (j = 0; j < xStep; ++j) {
4815 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4816 *destPtr++ = (Guchar)pix[0];
4817 *destPtr++ = (Guchar)pix[1];
4818 *destPtr++ = (Guchar)pix[2];
4819 }
4820 }
4821 break;
4822 case splashModeXBGR8:
4823 for (i = 0; i < yStep; ++i) {
4824 for (j = 0; j < xStep; ++j) {
4825 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4826 *destPtr++ = (Guchar)pix[2];
4827 *destPtr++ = (Guchar)pix[1];
4828 *destPtr++ = (Guchar)pix[0];
4829 *destPtr++ = (Guchar)255;
4830 }
4831 }
4832 break;
4833 case splashModeBGR8:
4834 for (i = 0; i < yStep; ++i) {
4835 for (j = 0; j < xStep; ++j) {
4836 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4837 *destPtr++ = (Guchar)pix[2];
4838 *destPtr++ = (Guchar)pix[1];
4839 *destPtr++ = (Guchar)pix[0];
4840 }
4841 }
4842 break;
4843 #if SPLASH_CMYK
4844 case splashModeCMYK8:
4845 for (i = 0; i < yStep; ++i) {
4846 for (j = 0; j < xStep; ++j) {
4847 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4848 *destPtr++ = (Guchar)pix[0];
4849 *destPtr++ = (Guchar)pix[1];
4850 *destPtr++ = (Guchar)pix[2];
4851 *destPtr++ = (Guchar)pix[3];
4852 }
4853 }
4854 break;
4855 case splashModeDeviceN8:
4856 for (i = 0; i < yStep; ++i) {
4857 for (j = 0; j < xStep; ++j) {
4858 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4859 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
4860 *destPtr++ = (Guchar)pix[cp];
4861 }
4862 }
4863 break;
4864 #endif
4865 }
4866
4867 // process alpha
4868 if (srcAlpha) {
4869 alpha = alphaLineBuf[x];
4870 for (i = 0; i < yStep; ++i) {
4871 for (j = 0; j < xStep; ++j) {
4872 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j;
4873 *destAlphaPtr = (Guchar)alpha;
4874 }
4875 }
4876 }
4877
4878 xx += xStep;
4879 }
4880
4881 destPtr0 += yStep * scaledWidth * nComps;
4882 if (srcAlpha) {
4883 destAlphaPtr0 += yStep * scaledWidth;
4884 }
4885 }
4886
4887 gfree(alphaLineBuf);
4888 gfree(lineBuf);
4889 }
4890
4891 // expand source row to scaledWidth using linear interpolation
expandRow(Guchar * srcBuf,Guchar * dstBuf,int srcWidth,int scaledWidth,int nComps)4892 static void expandRow(Guchar *srcBuf, Guchar *dstBuf, int srcWidth, int scaledWidth, int nComps)
4893 {
4894 double xStep = (double)srcWidth/scaledWidth;
4895 double xSrc = 0.0;
4896 double xFrac, xInt;
4897 int p;
4898
4899 // pad the source with an extra pixel equal to the last pixel
4900 // so that when xStep is inside the last pixel we still have two
4901 // pixels to interpolate between.
4902 for (int i = 0; i < nComps; i++)
4903 srcBuf[srcWidth*nComps + i] = srcBuf[(srcWidth-1)*nComps + i];
4904
4905 for (int x = 0; x < scaledWidth; x++) {
4906 xFrac = modf(xSrc, &xInt);
4907 p = (int)xInt;
4908 for (int c = 0; c < nComps; c++) {
4909 dstBuf[nComps*x + c] = srcBuf[nComps*p + c]*(1.0 - xFrac) + srcBuf[nComps*(p+1) + c]*xFrac;
4910 }
4911 xSrc += xStep;
4912 }
4913 }
4914
4915 // Scale up image using bilinear interpolation
scaleImageYuXuBilinear(SplashImageSource src,void * srcData,SplashColorMode srcMode,int nComps,GBool srcAlpha,int srcWidth,int srcHeight,int scaledWidth,int scaledHeight,SplashBitmap * dest)4916 void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData,
4917 SplashColorMode srcMode, int nComps,
4918 GBool srcAlpha, int srcWidth, int srcHeight,
4919 int scaledWidth, int scaledHeight,
4920 SplashBitmap *dest) {
4921 Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2;
4922 Guint pix[splashMaxColorComps];
4923 Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4924 int i;
4925
4926 if (srcWidth < 1 || srcHeight < 1)
4927 return;
4928
4929 // allocate buffers
4930 srcBuf = (Guchar *)gmallocn(srcWidth+1, nComps); // + 1 pixel of padding
4931 lineBuf1 = (Guchar *)gmallocn(scaledWidth, nComps);
4932 lineBuf2 = (Guchar *)gmallocn(scaledWidth, nComps);
4933 if (srcAlpha) {
4934 alphaSrcBuf = (Guchar *)gmalloc(srcWidth+1); // + 1 pixel of padding
4935 alphaLineBuf1 = (Guchar *)gmalloc(scaledWidth);
4936 alphaLineBuf2 = (Guchar *)gmalloc(scaledWidth);
4937 } else {
4938 alphaSrcBuf = NULL;
4939 alphaLineBuf1 = NULL;
4940 alphaLineBuf2 = NULL;
4941 }
4942
4943 double ySrc = 0.0;
4944 double yStep = (double)srcHeight/scaledHeight;
4945 double yFrac, yInt;
4946 int currentSrcRow = -1;
4947 (*src)(srcData, srcBuf, alphaSrcBuf);
4948 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4949 if (srcAlpha)
4950 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4951
4952 destPtr0 = dest->data;
4953 destAlphaPtr0 = dest->alpha;
4954 for (int y = 0; y < scaledHeight; y++) {
4955 yFrac = modf(ySrc, &yInt);
4956 if ((int)yInt > currentSrcRow) {
4957 currentSrcRow++;
4958 // Copy line2 data to line1 and get next line2 data.
4959 // If line2 already contains the last source row we don't touch it.
4960 // This effectively adds an extra row of padding for interpolating the
4961 // last source row with.
4962 memcpy(lineBuf1, lineBuf2, scaledWidth * nComps);
4963 if (srcAlpha)
4964 memcpy(alphaLineBuf1, alphaLineBuf2, scaledWidth);
4965 if (currentSrcRow < srcHeight) {
4966 (*src)(srcData, srcBuf, alphaSrcBuf);
4967 expandRow(srcBuf, lineBuf2, srcWidth, scaledWidth, nComps);
4968 if (srcAlpha)
4969 expandRow(alphaSrcBuf, alphaLineBuf2, srcWidth, scaledWidth, 1);
4970 }
4971 }
4972
4973 // write row y using linear interpolation on lineBuf1 and lineBuf2
4974 for (int x = 0; x < scaledWidth; ++x) {
4975 // compute the final pixel
4976 for (i = 0; i < nComps; ++i) {
4977 pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac;
4978 }
4979
4980 // store the pixel
4981 destPtr = destPtr0 + (y * scaledWidth + x) * nComps;
4982 switch (srcMode) {
4983 case splashModeMono1: // mono1 is not allowed
4984 break;
4985 case splashModeMono8:
4986 *destPtr++ = (Guchar)pix[0];
4987 break;
4988 case splashModeRGB8:
4989 *destPtr++ = (Guchar)pix[0];
4990 *destPtr++ = (Guchar)pix[1];
4991 *destPtr++ = (Guchar)pix[2];
4992 break;
4993 case splashModeXBGR8:
4994 *destPtr++ = (Guchar)pix[2];
4995 *destPtr++ = (Guchar)pix[1];
4996 *destPtr++ = (Guchar)pix[0];
4997 *destPtr++ = (Guchar)255;
4998 break;
4999 case splashModeBGR8:
5000 *destPtr++ = (Guchar)pix[2];
5001 *destPtr++ = (Guchar)pix[1];
5002 *destPtr++ = (Guchar)pix[0];
5003 break;
5004 #if SPLASH_CMYK
5005 case splashModeCMYK8:
5006 *destPtr++ = (Guchar)pix[0];
5007 *destPtr++ = (Guchar)pix[1];
5008 *destPtr++ = (Guchar)pix[2];
5009 *destPtr++ = (Guchar)pix[3];
5010 break;
5011 case splashModeDeviceN8:
5012 for (int cp = 0; cp < SPOT_NCOMPS+4; cp++)
5013 *destPtr++ = (Guchar)pix[cp];
5014 break;
5015 #endif
5016 }
5017
5018 // process alpha
5019 if (srcAlpha) {
5020 destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x;
5021 *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac;
5022 }
5023 }
5024
5025 ySrc += yStep;
5026 }
5027
5028 gfree(alphaSrcBuf);
5029 gfree(alphaLineBuf1);
5030 gfree(alphaLineBuf2);
5031 gfree(srcBuf);
5032 gfree(lineBuf1);
5033 gfree(lineBuf2);
5034 }
5035
vertFlipImage(SplashBitmap * img,int width,int height,int nComps)5036 void Splash::vertFlipImage(SplashBitmap *img, int width, int height,
5037 int nComps) {
5038 Guchar *lineBuf;
5039 Guchar *p0, *p1;
5040 int w;
5041
5042 if (unlikely(img->data == NULL)) {
5043 error(errInternal, -1, "img->data is NULL in Splash::vertFlipImage");
5044 return;
5045 }
5046
5047 w = width * nComps;
5048 lineBuf = (Guchar *)gmalloc(w);
5049 for (p0 = img->data, p1 = img->data + (height - 1) * w;
5050 p0 < p1;
5051 p0 += w, p1 -= w) {
5052 memcpy(lineBuf, p0, w);
5053 memcpy(p0, p1, w);
5054 memcpy(p1, lineBuf, w);
5055 }
5056 if (img->alpha) {
5057 for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width;
5058 p0 < p1;
5059 p0 += width, p1 -= width) {
5060 memcpy(lineBuf, p0, width);
5061 memcpy(p0, p1, width);
5062 memcpy(p1, lineBuf, width);
5063 }
5064 }
5065 gfree(lineBuf);
5066 }
5067
blitImage(SplashBitmap * src,GBool srcAlpha,int xDest,int yDest)5068 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest) {
5069 SplashClipResult clipRes = state->clip->testRect(xDest, yDest, xDest + src->getWidth() - 1, yDest + src->getHeight() - 1);
5070 if (clipRes != splashClipAllOutside) {
5071 blitImage(src, srcAlpha, xDest, yDest, clipRes);
5072 }
5073 }
5074
blitImage(SplashBitmap * src,GBool srcAlpha,int xDest,int yDest,SplashClipResult clipRes)5075 void Splash::blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest,
5076 SplashClipResult clipRes) {
5077 SplashPipe pipe;
5078 SplashColor pixel;
5079 Guchar *ap;
5080 int w, h, x0, y0, x1, y1, x, y;
5081
5082 // split the image into clipped and unclipped regions
5083 w = src->getWidth();
5084 h = src->getHeight();
5085 if (clipRes == splashClipAllInside) {
5086 x0 = 0;
5087 y0 = 0;
5088 x1 = w;
5089 y1 = h;
5090 } else {
5091 if (state->clip->getNumPaths()) {
5092 x0 = x1 = w;
5093 y0 = y1 = h;
5094 } else {
5095 if ((x0 = splashCeil(state->clip->getXMin()) - xDest) < 0) {
5096 x0 = 0;
5097 }
5098 if ((y0 = splashCeil(state->clip->getYMin()) - yDest) < 0) {
5099 y0 = 0;
5100 }
5101 if ((x1 = splashFloor(state->clip->getXMax()) - xDest) > w) {
5102 x1 = w;
5103 }
5104 if (x1 < x0) {
5105 x1 = x0;
5106 }
5107 if ((y1 = splashFloor(state->clip->getYMax()) - yDest) > h) {
5108 y1 = h;
5109 }
5110 if (y1 < y0) {
5111 y1 = y0;
5112 }
5113 }
5114 }
5115
5116 // draw the unclipped region
5117 if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
5118 pipeInit(&pipe, xDest + x0, yDest + y0, NULL, pixel,
5119 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5120 if (srcAlpha) {
5121 for (y = y0; y < y1; ++y) {
5122 pipeSetXY(&pipe, xDest + x0, yDest + y);
5123 ap = src->getAlphaPtr() + y * w + x0;
5124 for (x = x0; x < x1; ++x) {
5125 src->getPixel(x, y, pixel);
5126 pipe.shape = *ap++;
5127 (this->*pipe.run)(&pipe);
5128 }
5129 }
5130 } else {
5131 for (y = y0; y < y1; ++y) {
5132 pipeSetXY(&pipe, xDest + x0, yDest + y);
5133 for (x = x0; x < x1; ++x) {
5134 src->getPixel(x, y, pixel);
5135 (this->*pipe.run)(&pipe);
5136 }
5137 }
5138 }
5139 updateModX(xDest + x0);
5140 updateModX(xDest + x1 - 1);
5141 updateModY(yDest + y0);
5142 updateModY(yDest + y1 - 1);
5143 }
5144
5145 // draw the clipped regions
5146 if (y0 > 0) {
5147 blitImageClipped(src, srcAlpha, 0, 0, xDest, yDest, w, y0);
5148 }
5149 if (y1 < h) {
5150 blitImageClipped(src, srcAlpha, 0, y1, xDest, yDest + y1, w, h - y1);
5151 }
5152 if (x0 > 0 && y0 < y1) {
5153 blitImageClipped(src, srcAlpha, 0, y0, xDest, yDest + y0, x0, y1 - y0);
5154 }
5155 if (x1 < w && y0 < y1) {
5156 blitImageClipped(src, srcAlpha, x1, y0, xDest + x1, yDest + y0,
5157 w - x1, y1 - y0);
5158 }
5159 }
5160
blitImageClipped(SplashBitmap * src,GBool srcAlpha,int xSrc,int ySrc,int xDest,int yDest,int w,int h)5161 void Splash::blitImageClipped(SplashBitmap *src, GBool srcAlpha,
5162 int xSrc, int ySrc, int xDest, int yDest,
5163 int w, int h) {
5164 SplashPipe pipe;
5165 SplashColor pixel;
5166 Guchar *ap;
5167 int x, y;
5168
5169 if (vectorAntialias) {
5170 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5171 (Guchar)splashRound(state->fillAlpha * 255), gTrue, gFalse);
5172 drawAAPixelInit();
5173 if (srcAlpha) {
5174 for (y = 0; y < h; ++y) {
5175 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5176 for (x = 0; x < w; ++x) {
5177 src->getPixel(xSrc + x, ySrc + y, pixel);
5178 pipe.shape = *ap++;
5179 drawAAPixel(&pipe, xDest + x, yDest + y);
5180 }
5181 }
5182 } else {
5183 for (y = 0; y < h; ++y) {
5184 for (x = 0; x < w; ++x) {
5185 src->getPixel(xSrc + x, ySrc + y, pixel);
5186 pipe.shape = 255;
5187 drawAAPixel(&pipe, xDest + x, yDest + y);
5188 }
5189 }
5190 }
5191 } else {
5192 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5193 (Guchar)splashRound(state->fillAlpha * 255), srcAlpha, gFalse);
5194 if (srcAlpha) {
5195 for (y = 0; y < h; ++y) {
5196 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5197 pipeSetXY(&pipe, xDest, yDest + y);
5198 for (x = 0; x < w; ++x) {
5199 if (state->clip->test(xDest + x, yDest + y)) {
5200 src->getPixel(xSrc + x, ySrc + y, pixel);
5201 pipe.shape = *ap++;
5202 (this->*pipe.run)(&pipe);
5203 updateModX(xDest + x);
5204 updateModY(yDest + y);
5205 } else {
5206 pipeIncX(&pipe);
5207 ++ap;
5208 }
5209 }
5210 }
5211 } else {
5212 for (y = 0; y < h; ++y) {
5213 pipeSetXY(&pipe, xDest, yDest + y);
5214 for (x = 0; x < w; ++x) {
5215 if (state->clip->test(xDest + x, yDest + y)) {
5216 src->getPixel(xSrc + x, ySrc + y, pixel);
5217 (this->*pipe.run)(&pipe);
5218 updateModX(xDest + x);
5219 updateModY(yDest + y);
5220 } else {
5221 pipeIncX(&pipe);
5222 }
5223 }
5224 }
5225 }
5226 }
5227 }
5228
composite(SplashBitmap * src,int xSrc,int ySrc,int xDest,int yDest,int w,int h,GBool noClip,GBool nonIsolated,GBool knockout,SplashCoord knockoutOpacity)5229 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
5230 int xDest, int yDest, int w, int h,
5231 GBool noClip, GBool nonIsolated,
5232 GBool knockout, SplashCoord knockoutOpacity) {
5233 SplashPipe pipe;
5234 SplashColor pixel;
5235 Guchar alpha;
5236 Guchar *ap;
5237 int x, y;
5238
5239 if (src->mode != bitmap->mode) {
5240 return splashErrModeMismatch;
5241 }
5242
5243 if (unlikely(!bitmap->data)) {
5244 return splashErrZeroImage;
5245 }
5246
5247 if(src->getSeparationList()->getLength() > bitmap->getSeparationList()->getLength()) {
5248 for (x = bitmap->getSeparationList()->getLength(); x < src->getSeparationList()->getLength(); x++)
5249 bitmap->getSeparationList()->append(((GfxSeparationColorSpace *)src->getSeparationList()->get(x))->copy());
5250 }
5251 if (src->alpha) {
5252 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5253 (Guchar)splashRound(state->fillAlpha * 255), gTrue, nonIsolated,
5254 knockout, (Guchar)splashRound(knockoutOpacity * 255));
5255 if (noClip) {
5256 for (y = 0; y < h; ++y) {
5257 pipeSetXY(&pipe, xDest, yDest + y);
5258 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5259 for (x = 0; x < w; ++x) {
5260 src->getPixel(xSrc + x, ySrc + y, pixel);
5261 alpha = *ap++;
5262 // this uses shape instead of alpha, which isn't technically
5263 // correct, but works out the same
5264 pipe.shape = alpha;
5265 (this->*pipe.run)(&pipe);
5266 }
5267 }
5268 updateModX(xDest);
5269 updateModX(xDest + w - 1);
5270 updateModY(yDest);
5271 updateModY(yDest + h - 1);
5272 } else {
5273 for (y = 0; y < h; ++y) {
5274 pipeSetXY(&pipe, xDest, yDest + y);
5275 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5276 for (x = 0; x < w; ++x) {
5277 src->getPixel(xSrc + x, ySrc + y, pixel);
5278 alpha = *ap++;
5279 if (state->clip->test(xDest + x, yDest + y)) {
5280 // this uses shape instead of alpha, which isn't technically
5281 // correct, but works out the same
5282 pipe.shape = alpha;
5283 (this->*pipe.run)(&pipe);
5284 updateModX(xDest + x);
5285 updateModY(yDest + y);
5286 } else {
5287 pipeIncX(&pipe);
5288 }
5289 }
5290 }
5291 }
5292 } else {
5293 pipeInit(&pipe, xDest, yDest, NULL, pixel,
5294 (Guchar)splashRound(state->fillAlpha * 255), gFalse, nonIsolated);
5295 if (noClip) {
5296 for (y = 0; y < h; ++y) {
5297 pipeSetXY(&pipe, xDest, yDest + y);
5298 for (x = 0; x < w; ++x) {
5299 src->getPixel(xSrc + x, ySrc + y, pixel);
5300 (this->*pipe.run)(&pipe);
5301 }
5302 }
5303 updateModX(xDest);
5304 updateModX(xDest + w - 1);
5305 updateModY(yDest);
5306 updateModY(yDest + h - 1);
5307 } else {
5308 for (y = 0; y < h; ++y) {
5309 pipeSetXY(&pipe, xDest, yDest + y);
5310 for (x = 0; x < w; ++x) {
5311 src->getPixel(xSrc + x, ySrc + y, pixel);
5312 if (state->clip->test(xDest + x, yDest + y)) {
5313 (this->*pipe.run)(&pipe);
5314 updateModX(xDest + x);
5315 updateModY(yDest + y);
5316 } else {
5317 pipeIncX(&pipe);
5318 }
5319 }
5320 }
5321 }
5322 }
5323
5324 return splashOk;
5325 }
5326
compositeBackground(SplashColorPtr color)5327 void Splash::compositeBackground(SplashColorPtr color) {
5328 SplashColorPtr p;
5329 Guchar *q;
5330 Guchar alpha, alpha1, c, color0, color1, color2;
5331 #if SPLASH_CMYK
5332 Guchar color3;
5333 Guchar colorsp[SPOT_NCOMPS+4], cp;
5334 #endif
5335 int x, y, mask;
5336
5337 if (unlikely(bitmap->alpha == NULL)) {
5338 error(errInternal, -1, "bitmap->alpha is NULL in Splash::compositeBackground");
5339 return;
5340 }
5341
5342 switch (bitmap->mode) {
5343 case splashModeMono1:
5344 color0 = color[0];
5345 for (y = 0; y < bitmap->height; ++y) {
5346 p = &bitmap->data[y * bitmap->rowSize];
5347 q = &bitmap->alpha[y * bitmap->width];
5348 mask = 0x80;
5349 for (x = 0; x < bitmap->width; ++x) {
5350 alpha = *q++;
5351 alpha1 = 255 - alpha;
5352 c = (*p & mask) ? 0xff : 0x00;
5353 c = div255(alpha1 * color0 + alpha * c);
5354 if (c & 0x80) {
5355 *p |= mask;
5356 } else {
5357 *p &= ~mask;
5358 }
5359 if (!(mask >>= 1)) {
5360 mask = 0x80;
5361 ++p;
5362 }
5363 }
5364 }
5365 break;
5366 case splashModeMono8:
5367 color0 = color[0];
5368 for (y = 0; y < bitmap->height; ++y) {
5369 p = &bitmap->data[y * bitmap->rowSize];
5370 q = &bitmap->alpha[y * bitmap->width];
5371 for (x = 0; x < bitmap->width; ++x) {
5372 alpha = *q++;
5373 alpha1 = 255 - alpha;
5374 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5375 ++p;
5376 }
5377 }
5378 break;
5379 case splashModeRGB8:
5380 case splashModeBGR8:
5381 color0 = color[0];
5382 color1 = color[1];
5383 color2 = color[2];
5384 for (y = 0; y < bitmap->height; ++y) {
5385 p = &bitmap->data[y * bitmap->rowSize];
5386 q = &bitmap->alpha[y * bitmap->width];
5387 for (x = 0; x < bitmap->width; ++x) {
5388 alpha = *q++;
5389 if (alpha == 0)
5390 {
5391 p[0] = color0;
5392 p[1] = color1;
5393 p[2] = color2;
5394 }
5395 else if (alpha != 255)
5396 {
5397 alpha1 = 255 - alpha;
5398 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5399 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5400 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5401 }
5402 p += 3;
5403 }
5404 }
5405 break;
5406 case splashModeXBGR8:
5407 color0 = color[0];
5408 color1 = color[1];
5409 color2 = color[2];
5410 for (y = 0; y < bitmap->height; ++y) {
5411 p = &bitmap->data[y * bitmap->rowSize];
5412 q = &bitmap->alpha[y * bitmap->width];
5413 for (x = 0; x < bitmap->width; ++x) {
5414 alpha = *q++;
5415 if (alpha == 0)
5416 {
5417 p[0] = color0;
5418 p[1] = color1;
5419 p[2] = color2;
5420 }
5421 else if (alpha != 255)
5422 {
5423 alpha1 = 255 - alpha;
5424 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5425 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5426 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5427 }
5428 p[3] = 255;
5429 p += 4;
5430 }
5431 }
5432 break;
5433 #if SPLASH_CMYK
5434 case splashModeCMYK8:
5435 color0 = color[0];
5436 color1 = color[1];
5437 color2 = color[2];
5438 color3 = color[3];
5439 for (y = 0; y < bitmap->height; ++y) {
5440 p = &bitmap->data[y * bitmap->rowSize];
5441 q = &bitmap->alpha[y * bitmap->width];
5442 for (x = 0; x < bitmap->width; ++x) {
5443 alpha = *q++;
5444 if (alpha == 0)
5445 {
5446 p[0] = color0;
5447 p[1] = color1;
5448 p[2] = color2;
5449 p[3] = color3;
5450 }
5451 else if (alpha != 255)
5452 {
5453 alpha1 = 255 - alpha;
5454 p[0] = div255(alpha1 * color0 + alpha * p[0]);
5455 p[1] = div255(alpha1 * color1 + alpha * p[1]);
5456 p[2] = div255(alpha1 * color2 + alpha * p[2]);
5457 p[3] = div255(alpha1 * color3 + alpha * p[3]);
5458 }
5459 p += 4;
5460 }
5461 }
5462 break;
5463 case splashModeDeviceN8:
5464 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5465 colorsp[cp] = color[cp];
5466 for (y = 0; y < bitmap->height; ++y) {
5467 p = &bitmap->data[y * bitmap->rowSize];
5468 q = &bitmap->alpha[y * bitmap->width];
5469 for (x = 0; x < bitmap->width; ++x) {
5470 alpha = *q++;
5471 if (alpha == 0)
5472 {
5473 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5474 p[cp] = colorsp[cp];
5475 }
5476 else if (alpha != 255)
5477 {
5478 alpha1 = 255 - alpha;
5479 for (cp = 0; cp < SPOT_NCOMPS+4; cp++)
5480 p[cp] = div255(alpha1 * colorsp[cp] + alpha * p[cp]);
5481 }
5482 p += (SPOT_NCOMPS+4);
5483 }
5484 }
5485 break;
5486 #endif
5487 }
5488 memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
5489 }
5490
gouraudTriangleShadedFill(SplashGouraudColor * shading)5491 GBool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
5492 {
5493 double xdbl[3] = {0., 0., 0.};
5494 double ydbl[3] = {0., 0., 0.};
5495 int x[3] = {0, 0, 0};
5496 int y[3] = {0, 0, 0};
5497 double xt=0., xa=0., yt=0.;
5498 double ca=0., ct=0.;
5499
5500 // triangle interpolation:
5501 //
5502 double scanLimitMapL[2] = {0., 0.};
5503 double scanLimitMapR[2] = {0., 0.};
5504 double scanColorMapL[2] = {0., 0.};
5505 double scanColorMapR[2] = {0., 0.};
5506 double scanColorMap[2] = {0., 0.};
5507 int scanEdgeL[2] = { 0, 0 };
5508 int scanEdgeR[2] = { 0, 0 };
5509 GBool hasFurtherSegment = gFalse;
5510
5511 int scanLineOff = 0;
5512 int bitmapOff = 0;
5513 int scanLimitR = 0, scanLimitL = 0;
5514
5515 int bitmapWidth = bitmap->getWidth();
5516 SplashClip* clip = getClip();
5517 SplashBitmap *blitTarget = bitmap;
5518 SplashColorPtr bitmapData = bitmap->getDataPtr();
5519 int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
5520 SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
5521 SplashColorPtr cur = NULL;
5522 SplashCoord* userToCanvasMatrix = getMatrix();
5523 SplashColorMode bitmapMode = bitmap->getMode();
5524 GBool hasAlpha = (bitmapAlpha != NULL);
5525 int rowSize = bitmap->getRowSize();
5526 int colorComps = 0;
5527 switch (bitmapMode) {
5528 case splashModeMono1:
5529 break;
5530 case splashModeMono8:
5531 colorComps=1;
5532 break;
5533 case splashModeRGB8:
5534 colorComps=3;
5535 break;
5536 case splashModeBGR8:
5537 colorComps=3;
5538 break;
5539 case splashModeXBGR8:
5540 colorComps=4;
5541 break;
5542 #if SPLASH_CMYK
5543 case splashModeCMYK8:
5544 colorComps=4;
5545 break;
5546 case splashModeDeviceN8:
5547 colorComps=SPOT_NCOMPS+4;
5548 break;
5549 #endif
5550 }
5551
5552 SplashPipe pipe;
5553 SplashColor cSrcVal;
5554
5555 pipeInit(&pipe, 0, 0, NULL, cSrcVal, (Guchar)splashRound(state->strokeAlpha * 255), gFalse, gFalse);
5556
5557 if (vectorAntialias) {
5558 if (aaBuf == NULL)
5559 return gFalse; // fall back to old behaviour
5560 drawAAPixelInit();
5561 }
5562
5563 // idea:
5564 // 1. If pipe->noTransparency && !state->blendFunc
5565 // -> blit directly into the drawing surface!
5566 // -> disable alpha manually.
5567 // 2. Otherwise:
5568 // - blit also directly, but into an intermediate surface.
5569 // Afterwards, blit the intermediate surface using the drawing pipeline.
5570 // This is necessary because triangle elements can be on top of each
5571 // other, so the complete shading needs to be drawn before opacity is
5572 // applied.
5573 // - the final step, is performed using a SplashPipe:
5574 // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference
5575 // - invoke drawPixel(&pipe,X,Y,bNoClip);
5576 GBool bDirectBlit = vectorAntialias ? gFalse : pipe.noTransparency && !state->blendFunc;
5577 if (!bDirectBlit) {
5578 blitTarget = new SplashBitmap(bitmap->getWidth(),
5579 bitmap->getHeight(),
5580 bitmap->getRowPad(),
5581 bitmap->getMode(),
5582 gTrue,
5583 bitmap->getRowSize() >= 0);
5584 bitmapData = blitTarget->getDataPtr();
5585 bitmapAlpha = blitTarget->getAlphaPtr();
5586
5587 // initialisation seems to be necessary:
5588 int S = bitmap->getWidth() * bitmap->getHeight();
5589 for (int i = 0; i < S; ++i)
5590 bitmapAlpha[i] = 0;
5591 hasAlpha = gTrue;
5592 }
5593
5594 if (shading->isParameterized()) {
5595 double color[3];
5596 double colorinterp;
5597
5598 for (int i = 0; i < shading->getNTriangles(); ++i) {
5599 shading->getTriangle(i,
5600 xdbl + 0, ydbl + 0, color + 0,
5601 xdbl + 1, ydbl + 1, color + 1,
5602 xdbl + 2, ydbl + 2, color + 2);
5603 for (int m = 0; m < 3; ++m) {
5604 xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
5605 yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5];
5606 xdbl[m] = xt;
5607 ydbl[m] = yt;
5608 // we operate on scanlines which are integer offsets into the
5609 // raster image. The double offsets are of no use here.
5610 x[m] = splashRound(xt);
5611 y[m] = splashRound(yt);
5612 }
5613 // sort according to y coordinate to simplify sweep through scanlines:
5614 // INSERTION SORT.
5615 if (y[0] > y[1]) {
5616 Guswap(x[0], x[1]);
5617 Guswap(y[0], y[1]);
5618 Guswap(color[0], color[1]);
5619 }
5620 // first two are sorted.
5621 assert(y[0] <= y[1]);
5622 if (y[1] > y[2]) {
5623 int tmpX = x[2];
5624 int tmpY = y[2];
5625 double tmpC = color[2];
5626 x[2] = x[1]; y[2] = y[1]; color[2] = color[1];
5627
5628 if (y[0] > tmpY) {
5629 x[1] = x[0]; y[1] = y[0]; color[1] = color[0];
5630 x[0] = tmpX; y[0] = tmpY; color[0] = tmpC;
5631 } else {
5632 x[1] = tmpX; y[1] = tmpY; color[1] = tmpC;
5633 }
5634 }
5635 // first three are sorted
5636 assert(y[0] <= y[1]);
5637 assert(y[1] <= y[2]);
5638 /////
5639
5640 // this here is det( T ) == 0
5641 // where T is the matrix to map to barycentric coordinates.
5642 if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0)
5643 continue; // degenerate triangle.
5644
5645 // this here initialises the scanline generation.
5646 // We start with low Y coordinates and sweep up to the large Y
5647 // coordinates.
5648 //
5649 // scanEdgeL[m] in {0,1,2} m=0,1
5650 // scanEdgeR[m] in {0,1,2} m=0,1
5651 //
5652 // are the two edges between which scanlines are (currently)
5653 // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
5654 // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
5655 //
5656 scanEdgeL[0] = 0;
5657 scanEdgeR[0] = 0;
5658 if (y[0] == y[1]) {
5659 scanEdgeL[0] = 1;
5660 scanEdgeL[1] = scanEdgeR[1] = 2;
5661
5662 } else {
5663 scanEdgeL[1] = 1; scanEdgeR[1] = 2;
5664 }
5665 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5666 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5667
5668 // Ok. Now prepare the linear maps which map the y coordinate of
5669 // the current scanline to the corresponding LEFT and RIGHT x
5670 // coordinate (which define the scanline).
5671 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5672 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5673 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5674 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5675
5676 xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
5677 xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
5678 if (xa > xt) {
5679 // I have "left" is to the right of "right".
5680 // Exchange sides!
5681 Guswap(scanEdgeL[0], scanEdgeR[0]);
5682 Guswap(scanEdgeL[1], scanEdgeR[1]);
5683 Guswap(scanLimitMapL[0], scanLimitMapR[0]);
5684 Guswap(scanLimitMapL[1], scanLimitMapR[1]);
5685 // FIXME I'm sure there is a more efficient way to check this.
5686 }
5687
5688 // Same game: we can linearly interpolate the color based on the
5689 // current y coordinate (that's correct for triangle
5690 // interpolation due to linearity. We could also have done it in
5691 // barycentric coordinates, but that's slightly more involved)
5692 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5693 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5694 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5695 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5696
5697 hasFurtherSegment = (y[1] < y[2]);
5698 scanLineOff = y[0] * rowSize;
5699
5700 for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
5701 if (hasFurtherSegment && Y == y[1]) {
5702 // SWEEP EVENT: we encountered the next segment.
5703 //
5704 // switch to next segment, either at left end or at right
5705 // end:
5706 if (scanEdgeL[1] == 1) {
5707 scanEdgeL[0] = 1;
5708 scanEdgeL[1] = 2;
5709 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5710 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5711
5712 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5713 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5714 } else if (scanEdgeR[1] == 1) {
5715 scanEdgeR[0] = 1;
5716 scanEdgeR[1] = 2;
5717 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5718 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5719
5720 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5721 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5722 }
5723 assert( y[scanEdgeL[0]] < y[scanEdgeL[1]] );
5724 assert( y[scanEdgeR[0]] < y[scanEdgeR[1]] );
5725 hasFurtherSegment = gFalse;
5726 }
5727
5728 yt = Y;
5729
5730 xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
5731 xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
5732
5733 ca = yt * scanColorMapL[0] + scanColorMapL[1];
5734 ct = yt * scanColorMapR[0] + scanColorMapR[1];
5735
5736 scanLimitL = splashRound(xa);
5737 scanLimitR = splashRound(xt);
5738
5739 // Ok. Now: init the color interpolation depending on the X
5740 // coordinate inside of the current scanline:
5741 scanColorMap[0] = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL));
5742 scanColorMap[1] = ca - scanLimitL * scanColorMap[0];
5743
5744 // handled by clipping:
5745 // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
5746 assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
5747 assert(scanLineOff == Y * rowSize);
5748
5749 colorinterp = scanColorMap[0] * scanLimitL + scanColorMap[1];
5750
5751 bitmapOff = scanLineOff + scanLimitL * colorComps;
5752 for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap[0], bitmapOff += colorComps) {
5753 // FIXME : standard rectangular clipping can be done for a
5754 // complete scanline which is faster
5755 // --> see SplashClip and its methods
5756 if (!clip->test(X, Y))
5757 continue;
5758
5759 assert(fabs(colorinterp - (scanColorMap[0] * X + scanColorMap[1])) < 1e-10);
5760 assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
5761
5762 shading->getParameterizedColor(colorinterp, bitmapMode, &bitmapData[bitmapOff]);
5763
5764 // make the shading visible.
5765 // Note that opacity is handled by the bDirectBlit stuff, see
5766 // above for comments and below for implementation.
5767 if (hasAlpha)
5768 bitmapAlpha[Y * bitmapWidth + X] = 255;
5769 }
5770 }
5771 }
5772 } else {
5773 if (!bDirectBlit) {
5774 delete blitTarget;
5775 }
5776 return gFalse;
5777 }
5778
5779 if (!bDirectBlit) {
5780 // ok. Finalize the stuff by blitting the shading into the final
5781 // geometry, this time respecting the rendering pipe.
5782 int W = blitTarget->getWidth();
5783 int H = blitTarget->getHeight();
5784 cur = cSrcVal;
5785
5786 for (int X = 0; X < W; ++X) {
5787 for (int Y = 0; Y < H; ++Y) {
5788 if (!bitmapAlpha[Y * bitmapWidth + X])
5789 continue; // draw only parts of the shading!
5790 bitmapOff = Y * rowSize + colorComps * X;
5791
5792 for (int m = 0; m < colorComps; ++m)
5793 cur[m] = bitmapData[bitmapOff + m];
5794 if (vectorAntialias) {
5795 drawAAPixel(&pipe, X, Y);
5796 } else {
5797 drawPixel(&pipe, X, Y, gTrue); // no clipping - has already been done.
5798 }
5799 }
5800 }
5801
5802 delete blitTarget;
5803 blitTarget = NULL;
5804 }
5805
5806 return gTrue;
5807 }
5808
blitTransparent(SplashBitmap * src,int xSrc,int ySrc,int xDest,int yDest,int w,int h)5809 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
5810 int xDest, int yDest, int w, int h) {
5811 SplashColorPtr p, sp;
5812 Guchar *q;
5813 int x, y, mask, srcMask;
5814
5815 if (src->mode != bitmap->mode) {
5816 return splashErrModeMismatch;
5817 }
5818
5819 if (unlikely(!bitmap->data)) {
5820 return splashErrZeroImage;
5821 }
5822
5823 switch (bitmap->mode) {
5824 case splashModeMono1:
5825 for (y = 0; y < h; ++y) {
5826 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
5827 mask = 0x80 >> (xDest & 7);
5828 sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
5829 srcMask = 0x80 >> (xSrc & 7);
5830 for (x = 0; x < w; ++x) {
5831 if (*sp & srcMask) {
5832 *p |= mask;
5833 } else {
5834 *p &= ~mask;
5835 }
5836 if (!(mask >>= 1)) {
5837 mask = 0x80;
5838 ++p;
5839 }
5840 if (!(srcMask >>= 1)) {
5841 srcMask = 0x80;
5842 ++sp;
5843 }
5844 }
5845 }
5846 break;
5847 case splashModeMono8:
5848 for (y = 0; y < h; ++y) {
5849 p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
5850 sp = &src->data[(ySrc + y) * bitmap->rowSize + xSrc];
5851 for (x = 0; x < w; ++x) {
5852 *p++ = *sp++;
5853 }
5854 }
5855 break;
5856 case splashModeRGB8:
5857 case splashModeBGR8:
5858 for (y = 0; y < h; ++y) {
5859 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
5860 sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
5861 for (x = 0; x < w; ++x) {
5862 *p++ = *sp++;
5863 *p++ = *sp++;
5864 *p++ = *sp++;
5865 }
5866 }
5867 break;
5868 case splashModeXBGR8:
5869 for (y = 0; y < h; ++y) {
5870 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5871 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5872 for (x = 0; x < w; ++x) {
5873 *p++ = *sp++;
5874 *p++ = *sp++;
5875 *p++ = *sp++;
5876 *p++ = 255;
5877 sp++;
5878 }
5879 }
5880 break;
5881 #if SPLASH_CMYK
5882 case splashModeCMYK8:
5883 for (y = 0; y < h; ++y) {
5884 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5885 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5886 for (x = 0; x < w; ++x) {
5887 *p++ = *sp++;
5888 *p++ = *sp++;
5889 *p++ = *sp++;
5890 *p++ = *sp++;
5891 }
5892 }
5893 break;
5894 case splashModeDeviceN8:
5895 for (y = 0; y < h; ++y) {
5896 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS+4) * xDest];
5897 sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS+4) * xSrc];
5898 for (x = 0; x < w; ++x) {
5899 for (int cp=0; cp < SPOT_NCOMPS+4; cp++)
5900 *p++ = *sp++;
5901 }
5902 }
5903 break;
5904 #endif
5905 }
5906
5907 if (bitmap->alpha) {
5908 for (y = 0; y < h; ++y) {
5909 q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
5910 memset(q, 0x00, w);
5911 }
5912 }
5913
5914 return splashOk;
5915 }
5916
makeStrokePath(SplashPath * path,SplashCoord w,GBool flatten)5917 SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w,
5918 GBool flatten) {
5919 SplashPath *pathIn, *dashPath, *pathOut;
5920 SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
5921 SplashCoord crossprod, dotprod, miter, m;
5922 GBool first, last, closed, hasangle;
5923 int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
5924 int left0, left1, left2, right0, right1, right2, join0, join1, join2;
5925 int leftFirst, rightFirst, firstPt;
5926
5927 pathOut = new SplashPath();
5928
5929 if (path->length == 0) {
5930 return pathOut;
5931 }
5932
5933 if (flatten) {
5934 pathIn = flattenPath(path, state->matrix, state->flatness);
5935 if (state->lineDashLength > 0) {
5936 dashPath = makeDashedPath(pathIn);
5937 delete pathIn;
5938 pathIn = dashPath;
5939 if (pathIn->length == 0) {
5940 delete pathIn;
5941 return pathOut;
5942 }
5943 }
5944 } else {
5945 pathIn = path;
5946 }
5947
5948 subpathStart0 = subpathStart1 = 0; // make gcc happy
5949 seg = 0; // make gcc happy
5950 closed = gFalse; // make gcc happy
5951 left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
5952 leftFirst = rightFirst = firstPt = 0; // make gcc happy
5953
5954 i0 = 0;
5955 for (i1 = i0;
5956 !(pathIn->flags[i1] & splashPathLast) &&
5957 i1 + 1 < pathIn->length &&
5958 pathIn->pts[i1+1].x == pathIn->pts[i1].x &&
5959 pathIn->pts[i1+1].y == pathIn->pts[i1].y;
5960 ++i1) ;
5961
5962 while (i1 < pathIn->length) {
5963 if ((first = pathIn->flags[i0] & splashPathFirst)) {
5964 subpathStart0 = i0;
5965 subpathStart1 = i1;
5966 seg = 0;
5967 closed = pathIn->flags[i0] & splashPathClosed;
5968 }
5969 j0 = i1 + 1;
5970 if (j0 < pathIn->length) {
5971 for (j1 = j0;
5972 !(pathIn->flags[j1] & splashPathLast) &&
5973 j1 + 1 < pathIn->length &&
5974 pathIn->pts[j1+1].x == pathIn->pts[j1].x &&
5975 pathIn->pts[j1+1].y == pathIn->pts[j1].y;
5976 ++j1) ;
5977 } else {
5978 j1 = j0;
5979 }
5980 if (pathIn->flags[i1] & splashPathLast) {
5981 if (first && state->lineCap == splashLineCapRound) {
5982 // special case: zero-length subpath with round line caps -->
5983 // draw a circle
5984 pathOut->moveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5985 pathIn->pts[i0].y);
5986 pathOut->curveTo(pathIn->pts[i0].x + (SplashCoord)0.5 * w,
5987 pathIn->pts[i0].y + bezierCircle2 * w,
5988 pathIn->pts[i0].x + bezierCircle2 * w,
5989 pathIn->pts[i0].y + (SplashCoord)0.5 * w,
5990 pathIn->pts[i0].x,
5991 pathIn->pts[i0].y + (SplashCoord)0.5 * w);
5992 pathOut->curveTo(pathIn->pts[i0].x - bezierCircle2 * w,
5993 pathIn->pts[i0].y + (SplashCoord)0.5 * w,
5994 pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5995 pathIn->pts[i0].y + bezierCircle2 * w,
5996 pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5997 pathIn->pts[i0].y);
5998 pathOut->curveTo(pathIn->pts[i0].x - (SplashCoord)0.5 * w,
5999 pathIn->pts[i0].y - bezierCircle2 * w,
6000 pathIn->pts[i0].x - bezierCircle2 * w,
6001 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
6002 pathIn->pts[i0].x,
6003 pathIn->pts[i0].y - (SplashCoord)0.5 * w);
6004 pathOut->curveTo(pathIn->pts[i0].x + bezierCircle2 * w,
6005 pathIn->pts[i0].y - (SplashCoord)0.5 * w,
6006 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
6007 pathIn->pts[i0].y - bezierCircle2 * w,
6008 pathIn->pts[i0].x + (SplashCoord)0.5 * w,
6009 pathIn->pts[i0].y);
6010 pathOut->close();
6011 }
6012 i0 = j0;
6013 i1 = j1;
6014 continue;
6015 }
6016 last = pathIn->flags[j1] & splashPathLast;
6017 if (last) {
6018 k0 = subpathStart1 + 1;
6019 } else {
6020 k0 = j1 + 1;
6021 }
6022 for (k1 = k0;
6023 !(pathIn->flags[k1] & splashPathLast) &&
6024 k1 + 1 < pathIn->length &&
6025 pathIn->pts[k1+1].x == pathIn->pts[k1].x &&
6026 pathIn->pts[k1+1].y == pathIn->pts[k1].y;
6027 ++k1) ;
6028
6029 // compute the deltas for segment (i1, j0)
6030 #if USE_FIXEDPOINT
6031 // the 1/d value can be small, which introduces significant
6032 // inaccuracies in fixed point mode
6033 d = splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6034 pathIn->pts[j0].x, pathIn->pts[j0].y);
6035 dx = (pathIn->pts[j0].x - pathIn->pts[i1].x) / d;
6036 dy = (pathIn->pts[j0].y - pathIn->pts[i1].y) / d;
6037 #else
6038 d = (SplashCoord)1 / splashDist(pathIn->pts[i1].x, pathIn->pts[i1].y,
6039 pathIn->pts[j0].x, pathIn->pts[j0].y);
6040 dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
6041 dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
6042 #endif
6043 wdx = (SplashCoord)0.5 * w * dx;
6044 wdy = (SplashCoord)0.5 * w * dy;
6045
6046 // draw the start cap
6047 pathOut->moveTo(pathIn->pts[i0].x - wdy, pathIn->pts[i0].y + wdx);
6048 if (i0 == subpathStart0) {
6049 firstPt = pathOut->length - 1;
6050 }
6051 if (first && !closed) {
6052 switch (state->lineCap) {
6053 case splashLineCapButt:
6054 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6055 break;
6056 case splashLineCapRound:
6057 pathOut->curveTo(pathIn->pts[i0].x - wdy - bezierCircle * wdx,
6058 pathIn->pts[i0].y + wdx - bezierCircle * wdy,
6059 pathIn->pts[i0].x - wdx - bezierCircle * wdy,
6060 pathIn->pts[i0].y - wdy + bezierCircle * wdx,
6061 pathIn->pts[i0].x - wdx,
6062 pathIn->pts[i0].y - wdy);
6063 pathOut->curveTo(pathIn->pts[i0].x - wdx + bezierCircle * wdy,
6064 pathIn->pts[i0].y - wdy - bezierCircle * wdx,
6065 pathIn->pts[i0].x + wdy - bezierCircle * wdx,
6066 pathIn->pts[i0].y - wdx - bezierCircle * wdy,
6067 pathIn->pts[i0].x + wdy,
6068 pathIn->pts[i0].y - wdx);
6069 break;
6070 case splashLineCapProjecting:
6071 pathOut->lineTo(pathIn->pts[i0].x - wdx - wdy,
6072 pathIn->pts[i0].y + wdx - wdy);
6073 pathOut->lineTo(pathIn->pts[i0].x - wdx + wdy,
6074 pathIn->pts[i0].y - wdx - wdy);
6075 pathOut->lineTo(pathIn->pts[i0].x + wdy,
6076 pathIn->pts[i0].y - wdx);
6077 break;
6078 }
6079 } else {
6080 pathOut->lineTo(pathIn->pts[i0].x + wdy, pathIn->pts[i0].y - wdx);
6081 }
6082
6083 // draw the left side of the segment rectangle
6084 left2 = pathOut->length - 1;
6085 pathOut->lineTo(pathIn->pts[j0].x + wdy, pathIn->pts[j0].y - wdx);
6086
6087 // draw the end cap
6088 if (last && !closed) {
6089 switch (state->lineCap) {
6090 case splashLineCapButt:
6091 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6092 break;
6093 case splashLineCapRound:
6094 pathOut->curveTo(pathIn->pts[j0].x + wdy + bezierCircle * wdx,
6095 pathIn->pts[j0].y - wdx + bezierCircle * wdy,
6096 pathIn->pts[j0].x + wdx + bezierCircle * wdy,
6097 pathIn->pts[j0].y + wdy - bezierCircle * wdx,
6098 pathIn->pts[j0].x + wdx,
6099 pathIn->pts[j0].y + wdy);
6100 pathOut->curveTo(pathIn->pts[j0].x + wdx - bezierCircle * wdy,
6101 pathIn->pts[j0].y + wdy + bezierCircle * wdx,
6102 pathIn->pts[j0].x - wdy + bezierCircle * wdx,
6103 pathIn->pts[j0].y + wdx + bezierCircle * wdy,
6104 pathIn->pts[j0].x - wdy,
6105 pathIn->pts[j0].y + wdx);
6106 break;
6107 case splashLineCapProjecting:
6108 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx,
6109 pathIn->pts[j0].y - wdx + wdy);
6110 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx,
6111 pathIn->pts[j0].y + wdx + wdy);
6112 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6113 pathIn->pts[j0].y + wdx);
6114 break;
6115 }
6116 } else {
6117 pathOut->lineTo(pathIn->pts[j0].x - wdy, pathIn->pts[j0].y + wdx);
6118 }
6119
6120 // draw the right side of the segment rectangle
6121 // (NB: if stroke adjustment is enabled, the closepath operation MUST
6122 // add a segment because this segment is used for a hint)
6123 right2 = pathOut->length - 1;
6124 pathOut->close(state->strokeAdjust);
6125
6126 // draw the join
6127 join2 = pathOut->length;
6128 if (!last || closed) {
6129
6130 // compute the deltas for segment (j1, k0)
6131 #if USE_FIXEDPOINT
6132 // the 1/d value can be small, which introduces significant
6133 // inaccuracies in fixed point mode
6134 d = splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6135 pathIn->pts[k0].x, pathIn->pts[k0].y);
6136 dxNext = (pathIn->pts[k0].x - pathIn->pts[j1].x) / d;
6137 dyNext = (pathIn->pts[k0].y - pathIn->pts[j1].y) / d;
6138 #else
6139 d = (SplashCoord)1 / splashDist(pathIn->pts[j1].x, pathIn->pts[j1].y,
6140 pathIn->pts[k0].x, pathIn->pts[k0].y);
6141 dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
6142 dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
6143 #endif
6144 wdxNext = (SplashCoord)0.5 * w * dxNext;
6145 wdyNext = (SplashCoord)0.5 * w * dyNext;
6146
6147 // compute the join parameters
6148 crossprod = dx * dyNext - dy * dxNext;
6149 dotprod = -(dx * dxNext + dy * dyNext);
6150 hasangle = crossprod != 0 || dx * dxNext < 0 || dy * dyNext < 0;
6151 if (dotprod > 0.9999) {
6152 // avoid a divide-by-zero -- set miter to something arbitrary
6153 // such that sqrt(miter) will exceed miterLimit (and m is never
6154 // used in that situation)
6155 // (note: the comparison value (0.9999) has to be less than
6156 // 1-epsilon, where epsilon is the smallest value
6157 // representable in the fixed point format)
6158 miter = (state->miterLimit + 1) * (state->miterLimit + 1);
6159 m = 0;
6160 } else {
6161 miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
6162 if (miter < 1) {
6163 // this can happen because of floating point inaccuracies
6164 miter = 1;
6165 }
6166 m = splashSqrt(miter - 1);
6167 }
6168
6169 // round join
6170 if (hasangle && state->lineJoin == splashLineJoinRound) {
6171 pathOut->moveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6172 pathIn->pts[j0].y);
6173 pathOut->curveTo(pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6174 pathIn->pts[j0].y + bezierCircle2 * w,
6175 pathIn->pts[j0].x + bezierCircle2 * w,
6176 pathIn->pts[j0].y + (SplashCoord)0.5 * w,
6177 pathIn->pts[j0].x,
6178 pathIn->pts[j0].y + (SplashCoord)0.5 * w);
6179 pathOut->curveTo(pathIn->pts[j0].x - bezierCircle2 * w,
6180 pathIn->pts[j0].y + (SplashCoord)0.5 * w,
6181 pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6182 pathIn->pts[j0].y + bezierCircle2 * w,
6183 pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6184 pathIn->pts[j0].y);
6185 pathOut->curveTo(pathIn->pts[j0].x - (SplashCoord)0.5 * w,
6186 pathIn->pts[j0].y - bezierCircle2 * w,
6187 pathIn->pts[j0].x - bezierCircle2 * w,
6188 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6189 pathIn->pts[j0].x,
6190 pathIn->pts[j0].y - (SplashCoord)0.5 * w);
6191 pathOut->curveTo(pathIn->pts[j0].x + bezierCircle2 * w,
6192 pathIn->pts[j0].y - (SplashCoord)0.5 * w,
6193 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6194 pathIn->pts[j0].y - bezierCircle2 * w,
6195 pathIn->pts[j0].x + (SplashCoord)0.5 * w,
6196 pathIn->pts[j0].y);
6197
6198 } else if (hasangle) {
6199 pathOut->moveTo(pathIn->pts[j0].x, pathIn->pts[j0].y);
6200
6201 // angle < 180
6202 if (crossprod < 0) {
6203 pathOut->lineTo(pathIn->pts[j0].x - wdyNext,
6204 pathIn->pts[j0].y + wdxNext);
6205 // miter join inside limit
6206 if (state->lineJoin == splashLineJoinMiter &&
6207 splashSqrt(miter) <= state->miterLimit) {
6208 pathOut->lineTo(pathIn->pts[j0].x - wdy + wdx * m,
6209 pathIn->pts[j0].y + wdx + wdy * m);
6210 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6211 pathIn->pts[j0].y + wdx);
6212 // bevel join or miter join outside limit
6213 } else {
6214 pathOut->lineTo(pathIn->pts[j0].x - wdy,
6215 pathIn->pts[j0].y + wdx);
6216 }
6217
6218 // angle >= 180
6219 } else {
6220 pathOut->lineTo(pathIn->pts[j0].x + wdy,
6221 pathIn->pts[j0].y - wdx);
6222 // miter join inside limit
6223 if (state->lineJoin == splashLineJoinMiter &&
6224 splashSqrt(miter) <= state->miterLimit) {
6225 pathOut->lineTo(pathIn->pts[j0].x + wdy + wdx * m,
6226 pathIn->pts[j0].y - wdx + wdy * m);
6227 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6228 pathIn->pts[j0].y - wdxNext);
6229 // bevel join or miter join outside limit
6230 } else {
6231 pathOut->lineTo(pathIn->pts[j0].x + wdyNext,
6232 pathIn->pts[j0].y - wdxNext);
6233 }
6234 }
6235 }
6236
6237 pathOut->close();
6238 }
6239
6240 // add stroke adjustment hints
6241 if (state->strokeAdjust) {
6242 if (seg == 0 && !closed) {
6243 if (state->lineCap == splashLineCapButt) {
6244 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6245 firstPt, firstPt + 1);
6246 if (last) {
6247 pathOut->addStrokeAdjustHint(firstPt, left2 + 1,
6248 left2 + 1, left2 + 2);
6249 }
6250 } else if (state->lineCap == splashLineCapProjecting) {
6251 if (last) {
6252 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6253 firstPt + 1, firstPt + 2);
6254 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 2,
6255 left2 + 2, left2 + 3);
6256 } else {
6257 pathOut->addStrokeAdjustHint(firstPt + 1, left2 + 1,
6258 firstPt + 1, firstPt + 2);
6259 }
6260 }
6261 }
6262 if (seg >= 1) {
6263 if (seg >= 2) {
6264 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6265 pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
6266 } else {
6267 pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
6268 }
6269 pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
6270 }
6271 left0 = left1;
6272 left1 = left2;
6273 right0 = right1;
6274 right1 = right2;
6275 join0 = join1;
6276 join1 = join2;
6277 if (seg == 0) {
6278 leftFirst = left2;
6279 rightFirst = right2;
6280 }
6281 if (last) {
6282 if (seg >= 2) {
6283 pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
6284 pathOut->addStrokeAdjustHint(left1, right1,
6285 join0, pathOut->length - 1);
6286 } else {
6287 pathOut->addStrokeAdjustHint(left1, right1,
6288 firstPt, pathOut->length - 1);
6289 }
6290 if (closed) {
6291 pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
6292 pathOut->addStrokeAdjustHint(left1, right1,
6293 rightFirst + 1, rightFirst + 1);
6294 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6295 left1 + 1, right1);
6296 pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
6297 join1, pathOut->length - 1);
6298 }
6299 if (!closed && seg > 0) {
6300 if (state->lineCap == splashLineCapButt) {
6301 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 1,
6302 left1 + 1, left1 + 2);
6303 } else if (state->lineCap == splashLineCapProjecting) {
6304 pathOut->addStrokeAdjustHint(left1 - 1, left1 + 2,
6305 left1 + 2, left1 + 3);
6306 }
6307 }
6308 }
6309 }
6310
6311 i0 = j0;
6312 i1 = j1;
6313 ++seg;
6314 }
6315
6316 if (pathIn != path) {
6317 delete pathIn;
6318 }
6319
6320 return pathOut;
6321 }
6322
dumpPath(SplashPath * path)6323 void Splash::dumpPath(SplashPath *path) {
6324 int i;
6325
6326 for (i = 0; i < path->length; ++i) {
6327 printf(" %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
6328 i, (double)path->pts[i].x, (double)path->pts[i].y,
6329 (path->flags[i] & splashPathFirst) ? " first" : "",
6330 (path->flags[i] & splashPathLast) ? " last" : "",
6331 (path->flags[i] & splashPathClosed) ? " closed" : "",
6332 (path->flags[i] & splashPathCurve) ? " curve" : "");
6333 }
6334 }
6335
dumpXPath(SplashXPath * path)6336 void Splash::dumpXPath(SplashXPath *path) {
6337 int i;
6338
6339 for (i = 0; i < path->length; ++i) {
6340 printf(" %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n",
6341 i, (double)path->segs[i].x0, (double)path->segs[i].y0,
6342 (double)path->segs[i].x1, (double)path->segs[i].y1,
6343 (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
6344 (path->segs[i].flags & splashXPathVert) ? "V" : " ",
6345 (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
6346 }
6347 }
6348
shadedFill(SplashPath * path,GBool hasBBox,SplashPattern * pattern)6349 SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox,
6350 SplashPattern *pattern) {
6351 SplashPipe pipe;
6352 SplashXPath *xPath;
6353 SplashXPathScanner *scanner;
6354 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
6355 SplashClipResult clipRes;
6356
6357 if (vectorAntialias && aaBuf == NULL) { // should not happen, but to be secure
6358 return splashErrGeneric;
6359 }
6360 if (path->length == 0) {
6361 return splashErrEmptyPath;
6362 }
6363 xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
6364 if (vectorAntialias) {
6365 xPath->aaScale();
6366 }
6367 xPath->sort();
6368 yMinI = state->clip->getYMinI();
6369 yMaxI = state->clip->getYMaxI();
6370 if (vectorAntialias && !inShading) {
6371 yMinI = yMinI * splashAASize;
6372 yMaxI = (yMaxI + 1) * splashAASize - 1;
6373 }
6374 scanner = new SplashXPathScanner(xPath, gFalse, yMinI, yMaxI);
6375
6376 // get the min and max x and y values
6377 if (vectorAntialias) {
6378 scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
6379 } else {
6380 scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
6381 }
6382
6383 // check clipping
6384 if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI)) != splashClipAllOutside) {
6385 // limit the y range
6386 if (yMinI < state->clip->getYMinI()) {
6387 yMinI = state->clip->getYMinI();
6388 }
6389 if (yMaxI > state->clip->getYMaxI()) {
6390 yMaxI = state->clip->getYMaxI();
6391 }
6392
6393 pipeInit(&pipe, 0, yMinI, pattern, NULL, (Guchar)splashRound(state->fillAlpha * 255), vectorAntialias && !hasBBox, gFalse);
6394
6395 // draw the spans
6396 if (vectorAntialias) {
6397 for (y = yMinI; y <= yMaxI; ++y) {
6398 scanner->renderAALine(aaBuf, &x0, &x1, y);
6399 if (clipRes != splashClipAllInside) {
6400 state->clip->clipAALine(aaBuf, &x0, &x1, y);
6401 }
6402 #if splashAASize == 4
6403 if (!hasBBox && y > yMinI && y < yMaxI) {
6404 // correct shape on left side if clip is
6405 // vertical through the middle of shading:
6406 Guchar *p0, *p1, *p2, *p3;
6407 Guchar c1, c2, c3, c4;
6408 p0 = aaBuf->getDataPtr() + (x0 >> 1);
6409 p1 = p0 + aaBuf->getRowSize();
6410 p2 = p1 + aaBuf->getRowSize();
6411 p3 = p2 + aaBuf->getRowSize();
6412 if (x0 & 1) {
6413 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6414 } else {
6415 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6416 }
6417 if ( (c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03
6418 && c1 == c2 && c2 == c3 && c3 == c4 &&
6419 pattern->testPosition(x0 - 1, y) )
6420 {
6421 Guchar shapeCorrection = (x0 & 1) ? 0x0f : 0xf0;
6422 *p0 |= shapeCorrection;
6423 *p1 |= shapeCorrection;
6424 *p2 |= shapeCorrection;
6425 *p3 |= shapeCorrection;
6426 }
6427 // correct shape on right side if clip is
6428 // through the middle of shading:
6429 p0 = aaBuf->getDataPtr() + (x1 >> 1);
6430 p1 = p0 + aaBuf->getRowSize();
6431 p2 = p1 + aaBuf->getRowSize();
6432 p3 = p2 + aaBuf->getRowSize();
6433 if (x1 & 1) {
6434 c1 = (*p0 & 0x0f); c2 =(*p1 & 0x0f); c3 = (*p2 & 0x0f) ; c4 = (*p3 & 0x0f);
6435 } else {
6436 c1 = (*p0 >> 4); c2 = (*p1 >> 4); c3 = (*p2 >> 4); c4 = (*p3 >> 4);
6437 }
6438
6439 if ( (c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c
6440 && c1 == c2 && c2 == c3 && c3 == c4 &&
6441 pattern->testPosition(x1 + 1, y) )
6442 {
6443 Guchar shapeCorrection = (x1 & 1) ? 0x0f : 0xf0;
6444 *p0 |= shapeCorrection;
6445 *p1 |= shapeCorrection;
6446 *p2 |= shapeCorrection;
6447 *p3 |= shapeCorrection;
6448 }
6449 }
6450 #endif
6451 drawAALine(&pipe, x0, x1, y);
6452 }
6453 } else {
6454 SplashClipResult clipRes2;
6455 for (y = yMinI; y <= yMaxI; ++y) {
6456 while (scanner->getNextSpan(y, &x0, &x1)) {
6457 if (clipRes == splashClipAllInside) {
6458 drawSpan(&pipe, x0, x1, y, gTrue);
6459 } else {
6460 // limit the x range
6461 if (x0 < state->clip->getXMinI()) {
6462 x0 = state->clip->getXMinI();
6463 }
6464 if (x1 > state->clip->getXMaxI()) {
6465 x1 = state->clip->getXMaxI();
6466 }
6467 clipRes2 = state->clip->testSpan(x0, x1, y);
6468 drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
6469 }
6470 }
6471 }
6472 }
6473 }
6474 opClipRes = clipRes;
6475
6476 delete scanner;
6477 delete xPath;
6478 return splashOk;
6479 }
6480