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