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