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