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