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