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