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