1 //========================================================================
2 //
3 // Splash.cc
4 //
5 //========================================================================
6 
7 #include <aconf.h>
8 
9 #ifdef USE_GCC_PRAGMAS
10 #pragma implementation
11 #endif
12 
13 #include <stdlib.h>
14 #include <string.h>
15 #include "gmem.h"
16 #include "SplashErrorCodes.h"
17 #include "SplashMath.h"
18 #include "SplashBitmap.h"
19 #include "SplashState.h"
20 #include "SplashPath.h"
21 #include "SplashXPath.h"
22 #include "SplashXPathScanner.h"
23 #include "SplashPattern.h"
24 #include "SplashScreen.h"
25 #include "SplashFont.h"
26 #include "SplashGlyphBitmap.h"
27 #include "Splash.h"
28 
29 //------------------------------------------------------------------------
30 
31 // distance of Bezier control point from center for circle approximation
32 // = (4 * (sqrt(2) - 1) / 3) * r
33 #define bezierCircle ((SplashCoord)0.55228475)
34 #define bezierCircle2 ((SplashCoord)(0.5 * 0.55228475))
35 
36 // Divide a 16-bit value (in [0, 255*255]) by 255, returning an 8-bit result.
div255(int x)37 static inline Guchar div255(int x) {
38   return (Guchar)((x + (x >> 8) + 0x80) >> 8);
39 }
40 
41 //------------------------------------------------------------------------
42 // SplashPipe
43 //------------------------------------------------------------------------
44 
45 #define splashPipeMaxStages 9
46 
47 struct SplashPipe {
48   // pixel coordinates
49   int x, y;
50 
51   // source pattern
52   SplashPattern *pattern;
53 
54   // source alpha and color
55   SplashCoord aInput;
56   GBool usesShape;
57   Guchar aSrc;
58   SplashColorPtr cSrc;
59   SplashColor cSrcVal;
60 
61   // non-isolated group alpha0
62   Guchar *alpha0Ptr;
63 
64   // soft mask
65   SplashColorPtr softMaskPtr;
66 
67   // destination alpha and color
68   SplashColorPtr destColorPtr;
69   int destColorMask;
70   Guchar *destAlphaPtr;
71 
72   // shape
73   SplashCoord shape;
74 
75   // result alpha and color
76   GBool noTransparency;
77   SplashPipeResultColorCtrl resultColorCtrl;
78 
79   // non-isolated group correction
80   int nonIsolatedGroup;
81 };
82 
83 SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = {
84   splashPipeResultColorNoAlphaBlendMono,
85   splashPipeResultColorNoAlphaBlendMono,
86   splashPipeResultColorNoAlphaBlendRGB,
87   splashPipeResultColorNoAlphaBlendRGB
88 #if SPLASH_CMYK
89   ,
90   splashPipeResultColorNoAlphaBlendCMYK
91 #endif
92 };
93 
94 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = {
95   splashPipeResultColorAlphaNoBlendMono,
96   splashPipeResultColorAlphaNoBlendMono,
97   splashPipeResultColorAlphaNoBlendRGB,
98   splashPipeResultColorAlphaNoBlendRGB
99 #if SPLASH_CMYK
100   ,
101   splashPipeResultColorAlphaNoBlendCMYK
102 #endif
103 };
104 
105 SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = {
106   splashPipeResultColorAlphaBlendMono,
107   splashPipeResultColorAlphaBlendMono,
108   splashPipeResultColorAlphaBlendRGB,
109   splashPipeResultColorAlphaBlendRGB
110 #if SPLASH_CMYK
111   ,
112   splashPipeResultColorAlphaBlendCMYK
113 #endif
114 };
115 
116 //------------------------------------------------------------------------
117 
blendXor(SplashColorPtr src,SplashColorPtr dest,SplashColorPtr blend,SplashColorMode cm)118 static void blendXor(SplashColorPtr src, SplashColorPtr dest,
119 		     SplashColorPtr blend, SplashColorMode cm) {
120   int i;
121 
122   for (i = 0; i < splashColorModeNComps[cm]; ++i) {
123     blend[i] = src[i] ^ dest[i];
124   }
125 }
126 
127 //------------------------------------------------------------------------
128 // modified region
129 //------------------------------------------------------------------------
130 
clearModRegion()131 void Splash::clearModRegion() {
132   modXMin = bitmap->getWidth();
133   modYMin = bitmap->getHeight();
134   modXMax = -1;
135   modYMax = -1;
136 }
137 
updateModX(int x)138 inline void Splash::updateModX(int x) {
139   if (x < modXMin) {
140     modXMin = x;
141   }
142   if (x > modXMax) {
143     modXMax = x;
144   }
145 }
146 
updateModY(int y)147 inline void Splash::updateModY(int y) {
148   if (y < modYMin) {
149     modYMin = y;
150   }
151   if (y > modYMax) {
152     modYMax = y;
153   }
154 }
155 
156 //------------------------------------------------------------------------
157 // pipeline
158 //------------------------------------------------------------------------
159 
pipeInit(SplashPipe * pipe,int x,int y,SplashPattern * pattern,SplashColorPtr cSrc,SplashCoord aInput,GBool usesShape,GBool nonIsolatedGroup)160 inline void Splash::pipeInit(SplashPipe *pipe, int x, int y,
161 			     SplashPattern *pattern, SplashColorPtr cSrc,
162 			     SplashCoord aInput, GBool usesShape,
163 			     GBool nonIsolatedGroup) {
164   pipeSetXY(pipe, x, y);
165   pipe->pattern = NULL;
166 
167   // source color
168   if (pattern) {
169     if (pattern->isStatic()) {
170       pattern->getColor(x, y, pipe->cSrcVal);
171     } else {
172       pipe->pattern = pattern;
173     }
174     pipe->cSrc = pipe->cSrcVal;
175   } else {
176     pipe->cSrc = cSrc;
177   }
178 
179   // source alpha
180   pipe->aInput = aInput;
181   if (!state->softMask) {
182     if (usesShape) {
183       pipe->aInput *= 255;
184     } else {
185       pipe->aSrc = (Guchar)splashRound(pipe->aInput * 255);
186     }
187   }
188   pipe->usesShape = usesShape;
189 
190   // result alpha
191   if (aInput == 1 && !state->softMask && !usesShape &&
192       !state->inNonIsolatedGroup) {
193     pipe->noTransparency = gTrue;
194   } else {
195     pipe->noTransparency = gFalse;
196   }
197 
198   // result color
199   if (pipe->noTransparency) {
200     // the !state->blendFunc case is handled separately in pipeRun
201     pipe->resultColorCtrl = pipeResultColorNoAlphaBlend[bitmap->mode];
202   } else if (!state->blendFunc) {
203     pipe->resultColorCtrl = pipeResultColorAlphaNoBlend[bitmap->mode];
204   } else {
205     pipe->resultColorCtrl = pipeResultColorAlphaBlend[bitmap->mode];
206   }
207 
208   // non-isolated group correction
209   if (nonIsolatedGroup) {
210     pipe->nonIsolatedGroup = splashColorModeNComps[bitmap->mode];
211   } else {
212     pipe->nonIsolatedGroup = 0;
213   }
214 }
215 
pipeRun(SplashPipe * pipe)216 inline void Splash::pipeRun(SplashPipe *pipe) {
217   Guchar aSrc, aDest, alpha2, alpha0, aResult;
218   SplashColor cDest, cBlend;
219   Guchar cResult0, cResult1, cResult2, cResult3;
220 
221   //----- source color
222 
223   // static pattern: handled in pipeInit
224   // fixed color: handled in pipeInit
225 
226   // dynamic pattern
227   if (pipe->pattern) {
228     pipe->pattern->getColor(pipe->x, pipe->y, pipe->cSrcVal);
229   }
230 
231   if (pipe->noTransparency && !state->blendFunc) {
232 
233     //----- write destination pixel
234 
235     switch (bitmap->mode) {
236     case splashModeMono1:
237       cResult0 = pipe->cSrc[0];
238       if (state->screen->test(pipe->x, pipe->y, cResult0)) {
239 	*pipe->destColorPtr |= pipe->destColorMask;
240       } else {
241 	*pipe->destColorPtr &= ~pipe->destColorMask;
242       }
243       if (!(pipe->destColorMask >>= 1)) {
244 	pipe->destColorMask = 0x80;
245 	++pipe->destColorPtr;
246       }
247       break;
248     case splashModeMono8:
249       *pipe->destColorPtr++ = pipe->cSrc[0];
250       break;
251     case splashModeRGB8:
252       *pipe->destColorPtr++ = pipe->cSrc[0];
253       *pipe->destColorPtr++ = pipe->cSrc[1];
254       *pipe->destColorPtr++ = pipe->cSrc[2];
255       break;
256     case splashModeBGR8:
257       *pipe->destColorPtr++ = pipe->cSrc[2];
258       *pipe->destColorPtr++ = pipe->cSrc[1];
259       *pipe->destColorPtr++ = pipe->cSrc[0];
260       break;
261 #if SPLASH_CMYK
262     case splashModeCMYK8:
263       *pipe->destColorPtr++ = pipe->cSrc[0];
264       *pipe->destColorPtr++ = pipe->cSrc[1];
265       *pipe->destColorPtr++ = pipe->cSrc[2];
266       *pipe->destColorPtr++ = pipe->cSrc[3];
267       break;
268 #endif
269     }
270     if (pipe->destAlphaPtr) {
271       *pipe->destAlphaPtr++ = 255;
272     }
273 
274   } else {
275 
276     //----- read destination pixel
277 
278     switch (bitmap->mode) {
279     case splashModeMono1:
280       cDest[0] = (*pipe->destColorPtr & pipe->destColorMask) ? 0xff : 0x00;
281       break;
282     case splashModeMono8:
283       cDest[0] = *pipe->destColorPtr;
284       break;
285     case splashModeRGB8:
286       cDest[0] = pipe->destColorPtr[0];
287       cDest[1] = pipe->destColorPtr[1];
288       cDest[2] = pipe->destColorPtr[2];
289       break;
290     case splashModeBGR8:
291       cDest[0] = pipe->destColorPtr[2];
292       cDest[1] = pipe->destColorPtr[1];
293       cDest[2] = pipe->destColorPtr[0];
294       break;
295 #if SPLASH_CMYK
296     case splashModeCMYK8:
297       cDest[0] = pipe->destColorPtr[0];
298       cDest[1] = pipe->destColorPtr[1];
299       cDest[2] = pipe->destColorPtr[2];
300       cDest[3] = pipe->destColorPtr[3];
301       break;
302 #endif
303     }
304     if (pipe->destAlphaPtr) {
305       aDest = *pipe->destAlphaPtr;
306     } else {
307       aDest = 0xff;
308     }
309 
310     //----- blend function
311 
312     if (state->blendFunc) {
313       (*state->blendFunc)(pipe->cSrc, cDest, cBlend, bitmap->mode);
314     }
315 
316     //----- source alpha
317 
318     if (state->softMask) {
319       if (pipe->usesShape) {
320 	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++
321 				   * pipe->shape);
322       } else {
323 	aSrc = (Guchar)splashRound(pipe->aInput * *pipe->softMaskPtr++);
324       }
325     } else if (pipe->usesShape) {
326       // pipe->aInput is premultiplied by 255 in pipeInit
327       aSrc = (Guchar)splashRound(pipe->aInput * pipe->shape);
328     } else {
329       // precomputed in pipeInit
330       aSrc = pipe->aSrc;
331     }
332 
333     //----- result alpha and non-isolated group element correction
334 
335     if (pipe->noTransparency) {
336       alpha2 = aResult = 255;
337     } else {
338       aResult = aSrc + aDest - div255(aSrc * aDest);
339 
340       if (pipe->alpha0Ptr) {
341 	alpha0 = *pipe->alpha0Ptr++;
342 	alpha2 = aResult + alpha0 - div255(aResult * alpha0);
343       } else {
344 	alpha2 = aResult;
345       }
346     }
347 
348     //----- result color
349 
350     cResult0 = cResult1 = cResult2 = cResult3 = 0; // make gcc happy
351 
352     switch (pipe->resultColorCtrl) {
353 
354 #if SPLASH_CMYK
355     case splashPipeResultColorNoAlphaBlendCMYK:
356       cResult3 = div255((255 - aDest) * pipe->cSrc[3] + aDest * cBlend[3]);
357 #endif
358     case splashPipeResultColorNoAlphaBlendRGB:
359       cResult2 = div255((255 - aDest) * pipe->cSrc[2] + aDest * cBlend[2]);
360       cResult1 = div255((255 - aDest) * pipe->cSrc[1] + aDest * cBlend[1]);
361     case splashPipeResultColorNoAlphaBlendMono:
362       cResult0 = div255((255 - aDest) * pipe->cSrc[0] + aDest * cBlend[0]);
363       break;
364 
365     case splashPipeResultColorAlphaNoBlendMono:
366       if (alpha2 == 0) {
367 	cResult0 = 0;
368       } else {
369 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
370 			     aSrc * pipe->cSrc[0]) / alpha2);
371       }
372       break;
373     case splashPipeResultColorAlphaNoBlendRGB:
374       if (alpha2 == 0) {
375 	cResult0 = 0;
376 	cResult1 = 0;
377 	cResult2 = 0;
378       } else {
379 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
380 			     aSrc * pipe->cSrc[0]) / alpha2);
381 	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
382 			     aSrc * pipe->cSrc[1]) / alpha2);
383 	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
384 			     aSrc * pipe->cSrc[2]) / alpha2);
385       }
386       break;
387 #if SPLASH_CMYK
388     case splashPipeResultColorAlphaNoBlendCMYK:
389       if (alpha2 == 0) {
390 	cResult0 = 0;
391 	cResult1 = 0;
392 	cResult2 = 0;
393 	cResult3 = 0;
394       } else {
395 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
396 			     aSrc * pipe->cSrc[0]) / alpha2);
397 	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
398 			     aSrc * pipe->cSrc[1]) / alpha2);
399 	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
400 			     aSrc * pipe->cSrc[2]) / alpha2);
401 	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
402 			     aSrc * pipe->cSrc[3]) / alpha2);
403       }
404       break;
405 #endif
406 
407     case splashPipeResultColorAlphaBlendMono:
408       if (alpha2 == 0) {
409 	cResult0 = 0;
410       } else {
411 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
412 			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
413 				     aDest * cBlend[0]) / 255) /
414 			    alpha2);
415       }
416       break;
417     case splashPipeResultColorAlphaBlendRGB:
418       if (alpha2 == 0) {
419 	cResult0 = 0;
420 	cResult1 = 0;
421 	cResult2 = 0;
422       } else {
423 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
424 			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
425 				     aDest * cBlend[0]) / 255) /
426 			    alpha2);
427 	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
428 			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
429 				     aDest * cBlend[1]) / 255) /
430 			    alpha2);
431 	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
432 			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
433 				     aDest * cBlend[2]) / 255) /
434 			    alpha2);
435       }
436       break;
437 #if SPLASH_CMYK
438     case splashPipeResultColorAlphaBlendCMYK:
439       if (alpha2 == 0) {
440 	cResult0 = 0;
441 	cResult1 = 0;
442 	cResult2 = 0;
443 	cResult3 = 0;
444       } else {
445 	cResult0 = (Guchar)(((alpha2 - aSrc) * cDest[0] +
446 			     aSrc * ((255 - aDest) * pipe->cSrc[0] +
447 				     aDest * cBlend[0]) / 255) /
448 			    alpha2);
449 	cResult1 = (Guchar)(((alpha2 - aSrc) * cDest[1] +
450 			     aSrc * ((255 - aDest) * pipe->cSrc[1] +
451 				     aDest * cBlend[1]) / 255) /
452 			    alpha2);
453 	cResult2 = (Guchar)(((alpha2 - aSrc) * cDest[2] +
454 			     aSrc * ((255 - aDest) * pipe->cSrc[2] +
455 				     aDest * cBlend[2]) / 255) /
456 			    alpha2);
457 	cResult3 = (Guchar)(((alpha2 - aSrc) * cDest[3] +
458 			     aSrc * ((255 - aDest) * pipe->cSrc[3] +
459 				     aDest * cBlend[3]) / 255) /
460 			    alpha2);
461       }
462       break;
463 #endif
464     }
465 
466     //----- non-isolated group correction
467 
468     if (aResult != 0) {
469       switch (pipe->nonIsolatedGroup) {
470 #if SPLASH_CMYK
471       case 4:
472 	cResult3 += (cResult3 - cDest[3]) * aDest *
473 	            (255 - aResult) / (255 * aResult);
474 #endif
475       case 3:
476 	cResult2 += (cResult2 - cDest[2]) * aDest *
477 	            (255 - aResult) / (255 * aResult);
478 	cResult1 += (cResult1 - cDest[1]) * aDest *
479 	            (255 - aResult) / (255 * aResult);
480       case 1:
481 	cResult0 += (cResult0 - cDest[0]) * aDest *
482 	            (255 - aResult) / (255 * aResult);
483       case 0:
484 	break;
485       }
486     }
487 
488     //----- write destination pixel
489 
490     switch (bitmap->mode) {
491     case splashModeMono1:
492       if (state->screen->test(pipe->x, pipe->y, cResult0)) {
493 	*pipe->destColorPtr |= pipe->destColorMask;
494       } else {
495 	*pipe->destColorPtr &= ~pipe->destColorMask;
496       }
497       if (!(pipe->destColorMask >>= 1)) {
498 	pipe->destColorMask = 0x80;
499 	++pipe->destColorPtr;
500       }
501       break;
502     case splashModeMono8:
503       *pipe->destColorPtr++ = cResult0;
504       break;
505     case splashModeRGB8:
506       *pipe->destColorPtr++ = cResult0;
507       *pipe->destColorPtr++ = cResult1;
508       *pipe->destColorPtr++ = cResult2;
509       break;
510     case splashModeBGR8:
511       *pipe->destColorPtr++ = cResult2;
512       *pipe->destColorPtr++ = cResult1;
513       *pipe->destColorPtr++ = cResult0;
514       break;
515 #if SPLASH_CMYK
516     case splashModeCMYK8:
517       *pipe->destColorPtr++ = cResult0;
518       *pipe->destColorPtr++ = cResult1;
519       *pipe->destColorPtr++ = cResult2;
520       *pipe->destColorPtr++ = cResult3;
521       break;
522 #endif
523     }
524     if (pipe->destAlphaPtr) {
525       *pipe->destAlphaPtr++ = aResult;
526     }
527 
528   }
529 
530   ++pipe->x;
531 }
532 
pipeSetXY(SplashPipe * pipe,int x,int y)533 inline void Splash::pipeSetXY(SplashPipe *pipe, int x, int y) {
534   pipe->x = x;
535   pipe->y = y;
536   if (state->softMask) {
537     pipe->softMaskPtr =
538         &state->softMask->data[y * state->softMask->rowSize + x];
539   }
540   switch (bitmap->mode) {
541   case splashModeMono1:
542     pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + (x >> 3)];
543     pipe->destColorMask = 0x80 >> (x & 7);
544     break;
545   case splashModeMono8:
546     pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + x];
547     break;
548   case splashModeRGB8:
549   case splashModeBGR8:
550     pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 3 * x];
551     break;
552 #if SPLASH_CMYK
553   case splashModeCMYK8:
554     pipe->destColorPtr = &bitmap->data[y * bitmap->rowSize + 4 * x];
555     break;
556 #endif
557   }
558   if (bitmap->alpha) {
559     pipe->destAlphaPtr = &bitmap->alpha[y * bitmap->width + x];
560   } else {
561     pipe->destAlphaPtr = NULL;
562   }
563   if (state->inNonIsolatedGroup && alpha0Bitmap->alpha) {
564     pipe->alpha0Ptr =
565         &alpha0Bitmap->alpha[(alpha0Y + y) * alpha0Bitmap->width +
566 			     (alpha0X + x)];
567   } else {
568     pipe->alpha0Ptr = NULL;
569   }
570 }
571 
pipeIncX(SplashPipe * pipe)572 inline void Splash::pipeIncX(SplashPipe *pipe) {
573   ++pipe->x;
574   if (state->softMask) {
575     ++pipe->softMaskPtr;
576   }
577   switch (bitmap->mode) {
578   case splashModeMono1:
579     if (!(pipe->destColorMask >>= 1)) {
580       pipe->destColorMask = 0x80;
581       ++pipe->destColorPtr;
582     }
583     break;
584   case splashModeMono8:
585     ++pipe->destColorPtr;
586     break;
587   case splashModeRGB8:
588   case splashModeBGR8:
589     pipe->destColorPtr += 3;
590     break;
591 #if SPLASH_CMYK
592   case splashModeCMYK8:
593     pipe->destColorPtr += 4;
594     break;
595 #endif
596   }
597   if (pipe->destAlphaPtr) {
598     ++pipe->destAlphaPtr;
599   }
600   if (pipe->alpha0Ptr) {
601     ++pipe->alpha0Ptr;
602   }
603 }
604 
drawPixel(SplashPipe * pipe,int x,int y,GBool noClip)605 inline void Splash::drawPixel(SplashPipe *pipe, int x, int y, GBool noClip) {
606   if (noClip || state->clip->test(x, y)) {
607     pipeSetXY(pipe, x, y);
608     pipeRun(pipe);
609     updateModX(x);
610     updateModY(y);
611   }
612 }
613 
drawAAPixelInit()614 inline void Splash::drawAAPixelInit() {
615   aaBufY = -1;
616 }
617 
drawAAPixel(SplashPipe * pipe,int x,int y)618 inline void Splash::drawAAPixel(SplashPipe *pipe, int x, int y) {
619 #if splashAASize == 4
620   static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
621 			       1, 2, 2, 3, 2, 3, 3, 4 };
622   int w;
623 #else
624   int xx, yy;
625 #endif
626   SplashColorPtr p;
627   int x0, x1, t;
628 
629   if (x < 0 || x >= bitmap->width ||
630       y < state->clip->getYMinI() || y > state->clip->getYMaxI()) {
631     return;
632   }
633 
634   // update aaBuf
635   if (y != aaBufY) {
636     memset(aaBuf->getDataPtr(), 0xff,
637 	   aaBuf->getRowSize() * aaBuf->getHeight());
638     x0 = 0;
639     x1 = bitmap->width - 1;
640     state->clip->clipAALine(aaBuf, &x0, &x1, y);
641     aaBufY = y;
642   }
643 
644   // compute the shape value
645 #if splashAASize == 4
646   p = aaBuf->getDataPtr() + (x >> 1);
647   w = aaBuf->getRowSize();
648   if (x & 1) {
649     t = bitCount4[*p & 0x0f] + bitCount4[p[w] & 0x0f] +
650         bitCount4[p[2*w] & 0x0f] + bitCount4[p[3*w] & 0x0f];
651   } else {
652     t = bitCount4[*p >> 4] + bitCount4[p[w] >> 4] +
653         bitCount4[p[2*w] >> 4] + bitCount4[p[3*w] >> 4];
654   }
655 #else
656   t = 0;
657   for (yy = 0; yy < splashAASize; ++yy) {
658     for (xx = 0; xx < splashAASize; ++xx) {
659       p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
660 	  ((x * splashAASize + xx) >> 3);
661       t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
662     }
663   }
664 #endif
665 
666   // draw the pixel
667   if (t != 0) {
668     pipeSetXY(pipe, x, y);
669     pipe->shape *= aaGamma[t];
670     pipeRun(pipe);
671     updateModX(x);
672     updateModY(y);
673   }
674 }
675 
drawSpan(SplashPipe * pipe,int x0,int x1,int y,GBool noClip)676 inline void Splash::drawSpan(SplashPipe *pipe, int x0, int x1, int y,
677 			     GBool noClip) {
678   int x;
679 
680   pipeSetXY(pipe, x0, y);
681   if (noClip) {
682     for (x = x0; x <= x1; ++x) {
683       pipeRun(pipe);
684     }
685     updateModX(x0);
686     updateModX(x1);
687     updateModY(y);
688   } else {
689     for (x = x0; x <= x1; ++x) {
690       if (state->clip->test(x, y)) {
691 	pipeRun(pipe);
692 	updateModX(x);
693 	updateModY(y);
694       } else {
695 	pipeIncX(pipe);
696       }
697     }
698   }
699 }
700 
drawAALine(SplashPipe * pipe,int x0,int x1,int y)701 inline void Splash::drawAALine(SplashPipe *pipe, int x0, int x1, int y) {
702 #if splashAASize == 4
703   static int bitCount4[16] = { 0, 1, 1, 2, 1, 2, 2, 3,
704 			       1, 2, 2, 3, 2, 3, 3, 4 };
705   SplashColorPtr p0, p1, p2, p3;
706   int t;
707 #else
708   SplashColorPtr p;
709   int xx, yy, t;
710 #endif
711   int x;
712 
713 #if splashAASize == 4
714   p0 = aaBuf->getDataPtr() + (x0 >> 1);
715   p1 = p0 + aaBuf->getRowSize();
716   p2 = p1 + aaBuf->getRowSize();
717   p3 = p2 + aaBuf->getRowSize();
718 #endif
719   pipeSetXY(pipe, x0, y);
720   for (x = x0; x <= x1; ++x) {
721 
722     // compute the shape value
723 #if splashAASize == 4
724     if (x & 1) {
725       t = bitCount4[*p0 & 0x0f] + bitCount4[*p1 & 0x0f] +
726 	  bitCount4[*p2 & 0x0f] + bitCount4[*p3 & 0x0f];
727       ++p0; ++p1; ++p2; ++p3;
728     } else {
729       t = bitCount4[*p0 >> 4] + bitCount4[*p1 >> 4] +
730 	  bitCount4[*p2 >> 4] + bitCount4[*p3 >> 4];
731     }
732 #else
733     t = 0;
734     for (yy = 0; yy < splashAASize; ++yy) {
735       for (xx = 0; xx < splashAASize; ++xx) {
736 	p = aaBuf->getDataPtr() + yy * aaBuf->getRowSize() +
737 	    ((x * splashAASize + xx) >> 3);
738 	t += (*p >> (7 - ((x * splashAASize + xx) & 7))) & 1;
739       }
740     }
741 #endif
742 
743     if (t != 0) {
744       pipe->shape = aaGamma[t];
745       pipeRun(pipe);
746       updateModX(x);
747       updateModY(y);
748     } else {
749       pipeIncX(pipe);
750     }
751   }
752 }
753 
754 //------------------------------------------------------------------------
755 
756 // Transform a point from user space to device space.
transform(SplashCoord * matrix,SplashCoord xi,SplashCoord yi,SplashCoord * xo,SplashCoord * yo)757 inline void Splash::transform(SplashCoord *matrix,
758 			      SplashCoord xi, SplashCoord yi,
759 			      SplashCoord *xo, SplashCoord *yo) {
760   //                          [ m[0] m[1] 0 ]
761   // [xo yo 1] = [xi yi 1] *  [ m[2] m[3] 0 ]
762   //                          [ m[4] m[5] 1 ]
763   *xo = xi * matrix[0] + yi * matrix[2] + matrix[4];
764   *yo = xi * matrix[1] + yi * matrix[3] + matrix[5];
765 }
766 
767 //------------------------------------------------------------------------
768 // Splash
769 //------------------------------------------------------------------------
770 
Splash(SplashBitmap * bitmapA,GBool vectorAntialiasA,SplashScreenParams * screenParams)771 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
772 	       SplashScreenParams *screenParams) {
773   int i;
774 
775   bitmap = bitmapA;
776   vectorAntialias = vectorAntialiasA;
777   state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
778 			  screenParams);
779   if (vectorAntialias) {
780     aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
781 			     1, splashModeMono1, gFalse);
782     for (i = 0; i <= splashAASize * splashAASize; ++i) {
783       aaGamma[i] = splashPow((SplashCoord)i /
784 			       (SplashCoord)(splashAASize * splashAASize),
785 			     1.5);
786     }
787   } else {
788     aaBuf = NULL;
789   }
790   clearModRegion();
791   debugMode = gFalse;
792 }
793 
Splash(SplashBitmap * bitmapA,GBool vectorAntialiasA,SplashScreen * screenA)794 Splash::Splash(SplashBitmap *bitmapA, GBool vectorAntialiasA,
795 	       SplashScreen *screenA) {
796   int i;
797 
798   bitmap = bitmapA;
799   vectorAntialias = vectorAntialiasA;
800   state = new SplashState(bitmap->width, bitmap->height, vectorAntialias,
801 			  screenA);
802   if (vectorAntialias) {
803     aaBuf = new SplashBitmap(splashAASize * bitmap->width, splashAASize,
804 			     1, splashModeMono1, gFalse);
805     for (i = 0; i <= splashAASize * splashAASize; ++i) {
806       aaGamma[i] = splashPow((SplashCoord)i /
807 			       (SplashCoord)(splashAASize * splashAASize),
808 			     1.5);
809     }
810   } else {
811     aaBuf = NULL;
812   }
813   clearModRegion();
814   debugMode = gFalse;
815 }
816 
~Splash()817 Splash::~Splash() {
818   while (state->next) {
819     restoreState();
820   }
821   delete state;
822   if (vectorAntialias) {
823     delete aaBuf;
824   }
825 }
826 
827 //------------------------------------------------------------------------
828 // state read
829 //------------------------------------------------------------------------
830 
getMatrix()831 SplashCoord *Splash::getMatrix() {
832   return state->matrix;
833 }
834 
getStrokePattern()835 SplashPattern *Splash::getStrokePattern() {
836   return state->strokePattern;
837 }
838 
getFillPattern()839 SplashPattern *Splash::getFillPattern() {
840   return state->fillPattern;
841 }
842 
getScreen()843 SplashScreen *Splash::getScreen() {
844   return state->screen;
845 }
846 
getBlendFunc()847 SplashBlendFunc Splash::getBlendFunc() {
848   return state->blendFunc;
849 }
850 
getStrokeAlpha()851 SplashCoord Splash::getStrokeAlpha() {
852   return state->strokeAlpha;
853 }
854 
getFillAlpha()855 SplashCoord Splash::getFillAlpha() {
856   return state->fillAlpha;
857 }
858 
getLineWidth()859 SplashCoord Splash::getLineWidth() {
860   return state->lineWidth;
861 }
862 
getLineCap()863 int Splash::getLineCap() {
864   return state->lineCap;
865 }
866 
getLineJoin()867 int Splash::getLineJoin() {
868   return state->lineJoin;
869 }
870 
getMiterLimit()871 SplashCoord Splash::getMiterLimit() {
872   return state->miterLimit;
873 }
874 
getFlatness()875 SplashCoord Splash::getFlatness() {
876   return state->flatness;
877 }
878 
getLineDash()879 SplashCoord *Splash::getLineDash() {
880   return state->lineDash;
881 }
882 
getLineDashLength()883 int Splash::getLineDashLength() {
884   return state->lineDashLength;
885 }
886 
getLineDashPhase()887 SplashCoord Splash::getLineDashPhase() {
888   return state->lineDashPhase;
889 }
890 
getClip()891 SplashClip *Splash::getClip() {
892   return state->clip;
893 }
894 
getSoftMask()895 SplashBitmap *Splash::getSoftMask() {
896   return state->softMask;
897 }
898 
getInNonIsolatedGroup()899 GBool Splash::getInNonIsolatedGroup() {
900   return state->inNonIsolatedGroup;
901 }
902 
903 //------------------------------------------------------------------------
904 // state write
905 //------------------------------------------------------------------------
906 
setMatrix(SplashCoord * matrix)907 void Splash::setMatrix(SplashCoord *matrix) {
908   memcpy(state->matrix, matrix, 6 * sizeof(SplashCoord));
909 }
910 
setStrokePattern(SplashPattern * strokePattern)911 void Splash::setStrokePattern(SplashPattern *strokePattern) {
912   state->setStrokePattern(strokePattern);
913 }
914 
setFillPattern(SplashPattern * fillPattern)915 void Splash::setFillPattern(SplashPattern *fillPattern) {
916   state->setFillPattern(fillPattern);
917 }
918 
setScreen(SplashScreen * screen)919 void Splash::setScreen(SplashScreen *screen) {
920   state->setScreen(screen);
921 }
922 
setBlendFunc(SplashBlendFunc func)923 void Splash::setBlendFunc(SplashBlendFunc func) {
924   state->blendFunc = func;
925 }
926 
setStrokeAlpha(SplashCoord alpha)927 void Splash::setStrokeAlpha(SplashCoord alpha) {
928   state->strokeAlpha = alpha;
929 }
930 
setFillAlpha(SplashCoord alpha)931 void Splash::setFillAlpha(SplashCoord alpha) {
932   state->fillAlpha = alpha;
933 }
934 
setLineWidth(SplashCoord lineWidth)935 void Splash::setLineWidth(SplashCoord lineWidth) {
936   state->lineWidth = lineWidth;
937 }
938 
setLineCap(int lineCap)939 void Splash::setLineCap(int lineCap) {
940   state->lineCap = lineCap;
941 }
942 
setLineJoin(int lineJoin)943 void Splash::setLineJoin(int lineJoin) {
944   state->lineJoin = lineJoin;
945 }
946 
setMiterLimit(SplashCoord miterLimit)947 void Splash::setMiterLimit(SplashCoord miterLimit) {
948   state->miterLimit = miterLimit;
949 }
950 
setFlatness(SplashCoord flatness)951 void Splash::setFlatness(SplashCoord flatness) {
952   if (flatness < 1) {
953     state->flatness = 1;
954   } else {
955     state->flatness = flatness;
956   }
957 }
958 
setLineDash(SplashCoord * lineDash,int lineDashLength,SplashCoord lineDashPhase)959 void Splash::setLineDash(SplashCoord *lineDash, int lineDashLength,
960 			 SplashCoord lineDashPhase) {
961   state->setLineDash(lineDash, lineDashLength, lineDashPhase);
962 }
963 
setStrokeAdjust(GBool strokeAdjust)964 void Splash::setStrokeAdjust(GBool strokeAdjust) {
965   state->strokeAdjust = strokeAdjust;
966 }
967 
clipResetToRect(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1)968 void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0,
969 			     SplashCoord x1, SplashCoord y1) {
970   state->clip->resetToRect(x0, y0, x1, y1);
971 }
972 
clipToRect(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1)973 SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0,
974 			       SplashCoord x1, SplashCoord y1) {
975   return state->clip->clipToRect(x0, y0, x1, y1);
976 }
977 
clipToPath(SplashPath * path,GBool eo)978 SplashError Splash::clipToPath(SplashPath *path, GBool eo) {
979   return state->clip->clipToPath(path, state->matrix, state->flatness, eo);
980 }
981 
setSoftMask(SplashBitmap * softMask)982 void Splash::setSoftMask(SplashBitmap *softMask) {
983   state->setSoftMask(softMask);
984 }
985 
setInNonIsolatedGroup(SplashBitmap * alpha0BitmapA,int alpha0XA,int alpha0YA)986 void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA,
987 				   int alpha0XA, int alpha0YA) {
988   alpha0Bitmap = alpha0BitmapA;
989   alpha0X = alpha0XA;
990   alpha0Y = alpha0YA;
991   state->inNonIsolatedGroup = gTrue;
992 }
993 
994 //------------------------------------------------------------------------
995 // state save/restore
996 //------------------------------------------------------------------------
997 
saveState()998 void Splash::saveState() {
999   SplashState *newState;
1000 
1001   newState = state->copy();
1002   newState->next = state;
1003   state = newState;
1004 }
1005 
restoreState()1006 SplashError Splash::restoreState() {
1007   SplashState *oldState;
1008 
1009   if (!state->next) {
1010     return splashErrNoSave;
1011   }
1012   oldState = state;
1013   state = state->next;
1014   delete oldState;
1015   return splashOk;
1016 }
1017 
1018 //------------------------------------------------------------------------
1019 // drawing operations
1020 //------------------------------------------------------------------------
1021 
clear(SplashColorPtr color,Guchar alpha)1022 void Splash::clear(SplashColorPtr color, Guchar alpha) {
1023   SplashColorPtr row, p;
1024   Guchar mono;
1025   int x, y;
1026 
1027   switch (bitmap->mode) {
1028   case splashModeMono1:
1029     mono = (color[0] & 0x80) ? 0xff : 0x00;
1030     if (bitmap->rowSize < 0) {
1031       memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1032 	     mono, -bitmap->rowSize * bitmap->height);
1033     } else {
1034       memset(bitmap->data, mono, bitmap->rowSize * bitmap->height);
1035     }
1036     break;
1037   case splashModeMono8:
1038     if (bitmap->rowSize < 0) {
1039       memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1040 	     color[0], -bitmap->rowSize * bitmap->height);
1041     } else {
1042       memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1043     }
1044     break;
1045   case splashModeRGB8:
1046     if (color[0] == color[1] && color[1] == color[2]) {
1047       if (bitmap->rowSize < 0) {
1048 	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1049 	       color[0], -bitmap->rowSize * bitmap->height);
1050       } else {
1051 	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1052       }
1053     } else {
1054       row = bitmap->data;
1055       for (y = 0; y < bitmap->height; ++y) {
1056 	p = row;
1057 	for (x = 0; x < bitmap->width; ++x) {
1058 	  *p++ = color[2];
1059 	  *p++ = color[1];
1060 	  *p++ = color[0];
1061 	}
1062 	row += bitmap->rowSize;
1063       }
1064     }
1065     break;
1066   case splashModeBGR8:
1067     if (color[0] == color[1] && color[1] == color[2]) {
1068       if (bitmap->rowSize < 0) {
1069 	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1070 	       color[0], -bitmap->rowSize * bitmap->height);
1071       } else {
1072 	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1073       }
1074     } else {
1075       row = bitmap->data;
1076       for (y = 0; y < bitmap->height; ++y) {
1077 	p = row;
1078 	for (x = 0; x < bitmap->width; ++x) {
1079 	  *p++ = color[0];
1080 	  *p++ = color[1];
1081 	  *p++ = color[2];
1082 	}
1083 	row += bitmap->rowSize;
1084       }
1085     }
1086     break;
1087 #if SPLASH_CMYK
1088   case splashModeCMYK8:
1089     if (color[0] == color[1] && color[1] == color[2] && color[2] == color[3]) {
1090       if (bitmap->rowSize < 0) {
1091 	memset(bitmap->data + bitmap->rowSize * (bitmap->height - 1),
1092 	       color[0], -bitmap->rowSize * bitmap->height);
1093       } else {
1094 	memset(bitmap->data, color[0], bitmap->rowSize * bitmap->height);
1095       }
1096     } else {
1097       row = bitmap->data;
1098       for (y = 0; y < bitmap->height; ++y) {
1099 	p = row;
1100 	for (x = 0; x < bitmap->width; ++x) {
1101 	  *p++ = color[0];
1102 	  *p++ = color[1];
1103 	  *p++ = color[2];
1104 	  *p++ = color[3];
1105 	}
1106 	row += bitmap->rowSize;
1107       }
1108     }
1109     break;
1110 #endif
1111   }
1112 
1113   if (bitmap->alpha) {
1114     memset(bitmap->alpha, alpha, bitmap->width * bitmap->height);
1115   }
1116 
1117   updateModX(0);
1118   updateModY(0);
1119   updateModX(bitmap->width - 1);
1120   updateModY(bitmap->height - 1);
1121 }
1122 
stroke(SplashPath * path)1123 SplashError Splash::stroke(SplashPath *path) {
1124   SplashPath *path2, *dPath;
1125 
1126   if (debugMode) {
1127     printf("stroke [dash:%d] [width:%.2f]:\n",
1128 	   state->lineDashLength, (double)state->lineWidth);
1129     dumpPath(path);
1130   }
1131   opClipRes = splashClipAllOutside;
1132   if (path->length == 0) {
1133     return splashErrEmptyPath;
1134   }
1135   path2 = flattenPath(path, state->matrix, state->flatness);
1136   if (state->lineDashLength > 0) {
1137     dPath = makeDashedPath(path2);
1138     delete path2;
1139     path2 = dPath;
1140   }
1141   if (state->lineWidth == 0) {
1142     strokeNarrow(path2);
1143   } else {
1144     strokeWide(path2);
1145   }
1146   delete path2;
1147   return splashOk;
1148 }
1149 
strokeNarrow(SplashPath * path)1150 void Splash::strokeNarrow(SplashPath *path) {
1151   SplashPipe pipe;
1152   SplashXPath *xPath;
1153   SplashXPathSeg *seg;
1154   int x0, x1, x2, x3, y0, y1, x, y, t;
1155   SplashCoord dx, dy, dxdy;
1156   SplashClipResult clipRes;
1157   int nClipRes[3];
1158   int i;
1159 
1160   nClipRes[0] = nClipRes[1] = nClipRes[2] = 0;
1161 
1162   xPath = new SplashXPath(path, state->matrix, state->flatness, gFalse);
1163 
1164   pipeInit(&pipe, 0, 0, state->strokePattern, NULL, state->strokeAlpha,
1165 	   gFalse, gFalse);
1166 
1167   for (i = 0, seg = xPath->segs; i < xPath->length; ++i, ++seg) {
1168 
1169     x0 = splashFloor(seg->x0);
1170     x1 = splashFloor(seg->x1);
1171     y0 = splashFloor(seg->y0);
1172     y1 = splashFloor(seg->y1);
1173 
1174     // horizontal segment
1175     if (y0 == y1) {
1176       if (x0 > x1) {
1177 	t = x0; x0 = x1; x1 = t;
1178       }
1179       if ((clipRes = state->clip->testSpan(x0, x1, y0))
1180 	  != splashClipAllOutside) {
1181 	drawSpan(&pipe, x0, x1, y0, clipRes == splashClipAllInside);
1182       }
1183 
1184     // segment with |dx| > |dy|
1185     } else if (splashAbs(seg->dxdy) > 1) {
1186       dx = seg->x1 - seg->x0;
1187       dy = seg->y1 - seg->y0;
1188       dxdy = seg->dxdy;
1189       if (y0 > y1) {
1190 	t = y0; y0 = y1; y1 = t;
1191 	t = x0; x0 = x1; x1 = t;
1192 	dx = -dx;
1193 	dy = -dy;
1194       }
1195       if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1196 					   x0 <= x1 ? x1 : x0, y1))
1197 	  != splashClipAllOutside) {
1198 	if (dx > 0) {
1199 	  x2 = x0;
1200 	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1201 	  drawSpan(&pipe, x2, (x2 <= x3 - 1) ? x3 - 1 : x2, y0,
1202 		   clipRes == splashClipAllInside);
1203 	  x2 = x3;
1204 	  for (y = y0 + 1; y <= y1 - 1; ++y) {
1205 	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1206 	    drawSpan(&pipe, x2, x3 - 1, y, clipRes == splashClipAllInside);
1207 	    x2 = x3;
1208 	  }
1209 	  drawSpan(&pipe, x2, x2 <= x1 ? x1 : x2, y1,
1210 		   clipRes == splashClipAllInside);
1211 	} else {
1212 	  x2 = x0;
1213 	  x3 = splashFloor(seg->x0 + ((SplashCoord)y0 + 1 - seg->y0) * dxdy);
1214 	  drawSpan(&pipe, (x3 + 1 <= x2) ? x3 + 1 : x2, x2, y0,
1215 		   clipRes == splashClipAllInside);
1216 	  x2 = x3;
1217 	  for (y = y0 + 1; y <= y1 - 1; ++y) {
1218 	    x3 = splashFloor(seg->x0 + ((SplashCoord)y + 1 - seg->y0) * dxdy);
1219 	    drawSpan(&pipe, x3 + 1, x2, y, clipRes == splashClipAllInside);
1220 	    x2 = x3;
1221 	  }
1222 	  drawSpan(&pipe, x1, (x1 <= x2) ? x2 : x1, y1,
1223 		   clipRes == splashClipAllInside);
1224 	}
1225       }
1226 
1227     // segment with |dy| > |dx|
1228     } else {
1229       dxdy = seg->dxdy;
1230       if (y0 > y1) {
1231 	t = x0; x0 = x1; x1 = t;
1232 	t = y0; y0 = y1; y1 = t;
1233       }
1234       if ((clipRes = state->clip->testRect(x0 <= x1 ? x0 : x1, y0,
1235 					   x0 <= x1 ? x1 : x0, y1))
1236 	  != splashClipAllOutside) {
1237 	drawPixel(&pipe, x0, y0, clipRes == splashClipAllInside);
1238 	for (y = y0 + 1; y <= y1 - 1; ++y) {
1239 	  x = splashFloor(seg->x0 + ((SplashCoord)y - seg->y0) * dxdy);
1240 	  drawPixel(&pipe, x, y, clipRes == splashClipAllInside);
1241 	}
1242 	drawPixel(&pipe, x1, y1, clipRes == splashClipAllInside);
1243       }
1244     }
1245     ++nClipRes[clipRes];
1246   }
1247   if (nClipRes[splashClipPartial] ||
1248       (nClipRes[splashClipAllInside] && nClipRes[splashClipAllOutside])) {
1249     opClipRes = splashClipPartial;
1250   } else if (nClipRes[splashClipAllInside]) {
1251     opClipRes = splashClipAllInside;
1252   } else {
1253     opClipRes = splashClipAllOutside;
1254   }
1255 
1256   delete xPath;
1257 }
1258 
strokeWide(SplashPath * path)1259 void Splash::strokeWide(SplashPath *path) {
1260   SplashPath *path2;
1261 
1262   path2 = makeStrokePath(path, gFalse);
1263   fillWithPattern(path2, gFalse, state->strokePattern, state->strokeAlpha);
1264   delete path2;
1265 }
1266 
flattenPath(SplashPath * path,SplashCoord * matrix,SplashCoord flatness)1267 SplashPath *Splash::flattenPath(SplashPath *path, SplashCoord *matrix,
1268 				SplashCoord flatness) {
1269   SplashPath *fPath;
1270   SplashCoord flatness2;
1271   Guchar flag;
1272   int i;
1273 
1274   fPath = new SplashPath();
1275   flatness2 = flatness * flatness;
1276   i = 0;
1277   while (i < path->length) {
1278     flag = path->flags[i];
1279     if (flag & splashPathFirst) {
1280       fPath->moveTo(path->pts[i].x, path->pts[i].y);
1281       ++i;
1282     } else {
1283       if (flag & splashPathCurve) {
1284 	flattenCurve(path->pts[i-1].x, path->pts[i-1].y,
1285 		     path->pts[i  ].x, path->pts[i  ].y,
1286 		     path->pts[i+1].x, path->pts[i+1].y,
1287 		     path->pts[i+2].x, path->pts[i+2].y,
1288 		     matrix, flatness2, fPath);
1289 	i += 3;
1290       } else {
1291 	fPath->lineTo(path->pts[i].x, path->pts[i].y);
1292 	++i;
1293       }
1294       if (path->flags[i-1] & splashPathClosed) {
1295 	fPath->close();
1296       }
1297     }
1298   }
1299   return fPath;
1300 }
1301 
flattenCurve(SplashCoord x0,SplashCoord y0,SplashCoord x1,SplashCoord y1,SplashCoord x2,SplashCoord y2,SplashCoord x3,SplashCoord y3,SplashCoord * matrix,SplashCoord flatness2,SplashPath * fPath)1302 void Splash::flattenCurve(SplashCoord x0, SplashCoord y0,
1303 			  SplashCoord x1, SplashCoord y1,
1304 			  SplashCoord x2, SplashCoord y2,
1305 			  SplashCoord x3, SplashCoord y3,
1306 			  SplashCoord *matrix, SplashCoord flatness2,
1307 			  SplashPath *fPath) {
1308   SplashCoord cx[splashMaxCurveSplits + 1][3];
1309   SplashCoord cy[splashMaxCurveSplits + 1][3];
1310   int cNext[splashMaxCurveSplits + 1];
1311   SplashCoord xl0, xl1, xl2, xr0, xr1, xr2, xr3, xx1, xx2, xh;
1312   SplashCoord yl0, yl1, yl2, yr0, yr1, yr2, yr3, yy1, yy2, yh;
1313   SplashCoord dx, dy, mx, my, tx, ty, d1, d2;
1314   int p1, p2, p3;
1315 
1316   // initial segment
1317   p1 = 0;
1318   p2 = splashMaxCurveSplits;
1319   cx[p1][0] = x0;  cy[p1][0] = y0;
1320   cx[p1][1] = x1;  cy[p1][1] = y1;
1321   cx[p1][2] = x2;  cy[p1][2] = y2;
1322   cx[p2][0] = x3;  cy[p2][0] = y3;
1323   cNext[p1] = p2;
1324 
1325   while (p1 < splashMaxCurveSplits) {
1326 
1327     // get the next segment
1328     xl0 = cx[p1][0];  yl0 = cy[p1][0];
1329     xx1 = cx[p1][1];  yy1 = cy[p1][1];
1330     xx2 = cx[p1][2];  yy2 = cy[p1][2];
1331     p2 = cNext[p1];
1332     xr3 = cx[p2][0];  yr3 = cy[p2][0];
1333 
1334     // compute the distances (in device space) from the control points
1335     // to the midpoint of the straight line (this is a bit of a hack,
1336     // but it's much faster than computing the actual distances to the
1337     // line)
1338     transform(matrix, (xl0 + xr3) * 0.5, (yl0 + yr3) * 0.5, &mx, &my);
1339     transform(matrix, xx1, yy1, &tx, &ty);
1340     dx = tx - mx;
1341     dy = ty - my;
1342     d1 = dx*dx + dy*dy;
1343     transform(matrix, xx2, yy2, &tx, &ty);
1344     dx = tx - mx;
1345     dy = ty - my;
1346     d2 = dx*dx + dy*dy;
1347 
1348     // if the curve is flat enough, or no more subdivisions are
1349     // allowed, add the straight line segment
1350     if (p2 - p1 == 1 || (d1 <= flatness2 && d2 <= flatness2)) {
1351       fPath->lineTo(xr3, yr3);
1352       p1 = p2;
1353 
1354     // otherwise, subdivide the curve
1355     } else {
1356       xl1 = (xl0 + xx1) * 0.5;
1357       yl1 = (yl0 + yy1) * 0.5;
1358       xh = (xx1 + xx2) * 0.5;
1359       yh = (yy1 + yy2) * 0.5;
1360       xl2 = (xl1 + xh) * 0.5;
1361       yl2 = (yl1 + yh) * 0.5;
1362       xr2 = (xx2 + xr3) * 0.5;
1363       yr2 = (yy2 + yr3) * 0.5;
1364       xr1 = (xh + xr2) * 0.5;
1365       yr1 = (yh + yr2) * 0.5;
1366       xr0 = (xl2 + xr1) * 0.5;
1367       yr0 = (yl2 + yr1) * 0.5;
1368       // add the new subdivision points
1369       p3 = (p1 + p2) / 2;
1370       cx[p1][1] = xl1;  cy[p1][1] = yl1;
1371       cx[p1][2] = xl2;  cy[p1][2] = yl2;
1372       cNext[p1] = p3;
1373       cx[p3][0] = xr0;  cy[p3][0] = yr0;
1374       cx[p3][1] = xr1;  cy[p3][1] = yr1;
1375       cx[p3][2] = xr2;  cy[p3][2] = yr2;
1376       cNext[p3] = p2;
1377     }
1378   }
1379 }
1380 
makeDashedPath(SplashPath * path)1381 SplashPath *Splash::makeDashedPath(SplashPath *path) {
1382   SplashPath *dPath;
1383   SplashCoord lineDashTotal;
1384   SplashCoord lineDashStartPhase, lineDashDist, segLen;
1385   SplashCoord x0, y0, x1, y1, xa, ya;
1386   GBool lineDashStartOn, lineDashOn, newPath;
1387   int lineDashStartIdx, lineDashIdx;
1388   int i, j, k;
1389 
1390   lineDashTotal = 0;
1391   for (i = 0; i < state->lineDashLength; ++i) {
1392     lineDashTotal += state->lineDash[i];
1393   }
1394   lineDashStartPhase = state->lineDashPhase;
1395   i = splashFloor(lineDashStartPhase / lineDashTotal);
1396   lineDashStartPhase -= (SplashCoord)i * lineDashTotal;
1397   lineDashStartOn = gTrue;
1398   lineDashStartIdx = 0;
1399   while (lineDashStartPhase >= state->lineDash[lineDashStartIdx]) {
1400     lineDashStartOn = !lineDashStartOn;
1401     lineDashStartPhase -= state->lineDash[lineDashStartIdx];
1402     ++lineDashStartIdx;
1403   }
1404 
1405   dPath = new SplashPath();
1406 
1407   // process each subpath
1408   i = 0;
1409   while (i < path->length) {
1410 
1411     // find the end of the subpath
1412     for (j = i;
1413 	 j < path->length - 1 && !(path->flags[j] & splashPathLast);
1414 	 ++j) ;
1415 
1416     // initialize the dash parameters
1417     lineDashOn = lineDashStartOn;
1418     lineDashIdx = lineDashStartIdx;
1419     lineDashDist = state->lineDash[lineDashIdx] - lineDashStartPhase;
1420 
1421     // process each segment of the subpath
1422     newPath = gTrue;
1423     for (k = i; k < j; ++k) {
1424 
1425       // grab the segment
1426       x0 = path->pts[k].x;
1427       y0 = path->pts[k].y;
1428       x1 = path->pts[k+1].x;
1429       y1 = path->pts[k+1].y;
1430       segLen = splashDist(x0, y0, x1, y1);
1431 
1432       // process the segment
1433       while (segLen > 0) {
1434 
1435 	if (lineDashDist >= segLen) {
1436 	  if (lineDashOn) {
1437 	    if (newPath) {
1438 	      dPath->moveTo(x0, y0);
1439 	      newPath = gFalse;
1440 	    }
1441 	    dPath->lineTo(x1, y1);
1442 	  }
1443 	  lineDashDist -= segLen;
1444 	  segLen = 0;
1445 
1446 	} else {
1447 	  xa = x0 + (lineDashDist / segLen) * (x1 - x0);
1448 	  ya = y0 + (lineDashDist / segLen) * (y1 - y0);
1449 	  if (lineDashOn) {
1450 	    if (newPath) {
1451 	      dPath->moveTo(x0, y0);
1452 	      newPath = gFalse;
1453 	    }
1454 	    dPath->lineTo(xa, ya);
1455 	  }
1456 	  x0 = xa;
1457 	  y0 = ya;
1458 	  segLen -= lineDashDist;
1459 	  lineDashDist = 0;
1460 	}
1461 
1462 	// get the next entry in the dash array
1463 	if (lineDashDist <= 0) {
1464 	  lineDashOn = !lineDashOn;
1465 	  if (++lineDashIdx == state->lineDashLength) {
1466 	    lineDashIdx = 0;
1467 	  }
1468 	  lineDashDist = state->lineDash[lineDashIdx];
1469 	  newPath = gTrue;
1470 	}
1471       }
1472     }
1473     i = j + 1;
1474   }
1475 
1476   return dPath;
1477 }
1478 
fill(SplashPath * path,GBool eo)1479 SplashError Splash::fill(SplashPath *path, GBool eo) {
1480   if (debugMode) {
1481     printf("fill [eo:%d]:\n", eo);
1482     dumpPath(path);
1483   }
1484   return fillWithPattern(path, eo, state->fillPattern, state->fillAlpha);
1485 }
1486 
fillWithPattern(SplashPath * path,GBool eo,SplashPattern * pattern,SplashCoord alpha)1487 SplashError Splash::fillWithPattern(SplashPath *path, GBool eo,
1488 				    SplashPattern *pattern,
1489 				    SplashCoord alpha) {
1490   SplashPipe pipe;
1491   SplashXPath *xPath;
1492   SplashXPathScanner *scanner;
1493   int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1494   SplashClipResult clipRes, clipRes2;
1495 
1496   if (path->length == 0) {
1497     return splashErrEmptyPath;
1498   }
1499   xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1500   if (vectorAntialias) {
1501     xPath->aaScale();
1502   }
1503   xPath->sort();
1504   scanner = new SplashXPathScanner(xPath, eo);
1505 
1506   // get the min and max x and y values
1507   if (vectorAntialias) {
1508     scanner->getBBoxAA(&xMinI, &yMinI, &xMaxI, &yMaxI);
1509   } else {
1510     scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1511   }
1512 
1513   // check clipping
1514   if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1515       != splashClipAllOutside) {
1516 
1517     // limit the y range
1518     if (yMinI < state->clip->getYMinI()) {
1519       yMinI = state->clip->getYMinI();
1520     }
1521     if (yMaxI > state->clip->getYMaxI()) {
1522       yMaxI = state->clip->getYMaxI();
1523     }
1524 
1525     pipeInit(&pipe, 0, yMinI, pattern, NULL, alpha, vectorAntialias, gFalse);
1526 
1527     // draw the spans
1528     if (vectorAntialias) {
1529       for (y = yMinI; y <= yMaxI; ++y) {
1530 	scanner->renderAALine(aaBuf, &x0, &x1, y);
1531 	if (clipRes != splashClipAllInside) {
1532 	  state->clip->clipAALine(aaBuf, &x0, &x1, y);
1533 	}
1534 	drawAALine(&pipe, x0, x1, y);
1535       }
1536     } else {
1537       for (y = yMinI; y <= yMaxI; ++y) {
1538 	while (scanner->getNextSpan(y, &x0, &x1)) {
1539 	  if (clipRes == splashClipAllInside) {
1540 	    drawSpan(&pipe, x0, x1, y, gTrue);
1541 	  } else {
1542 	    // limit the x range
1543 	    if (x0 < state->clip->getXMinI()) {
1544 	      x0 = state->clip->getXMinI();
1545 	    }
1546 	    if (x1 > state->clip->getXMaxI()) {
1547 	      x1 = state->clip->getXMaxI();
1548 	    }
1549 	    clipRes2 = state->clip->testSpan(x0, x1, y);
1550 	    drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1551 	  }
1552 	}
1553       }
1554     }
1555   }
1556   opClipRes = clipRes;
1557 
1558   delete scanner;
1559   delete xPath;
1560   return splashOk;
1561 }
1562 
xorFill(SplashPath * path,GBool eo)1563 SplashError Splash::xorFill(SplashPath *path, GBool eo) {
1564   SplashPipe pipe;
1565   SplashXPath *xPath;
1566   SplashXPathScanner *scanner;
1567   int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
1568   SplashClipResult clipRes, clipRes2;
1569   SplashBlendFunc origBlendFunc;
1570 
1571   if (path->length == 0) {
1572     return splashErrEmptyPath;
1573   }
1574   xPath = new SplashXPath(path, state->matrix, state->flatness, gTrue);
1575   xPath->sort();
1576   scanner = new SplashXPathScanner(xPath, eo);
1577 
1578   // get the min and max x and y values
1579   scanner->getBBox(&xMinI, &yMinI, &xMaxI, &yMaxI);
1580 
1581   // check clipping
1582   if ((clipRes = state->clip->testRect(xMinI, yMinI, xMaxI, yMaxI))
1583       != splashClipAllOutside) {
1584 
1585     // limit the y range
1586     if (yMinI < state->clip->getYMinI()) {
1587       yMinI = state->clip->getYMinI();
1588     }
1589     if (yMaxI > state->clip->getYMaxI()) {
1590       yMaxI = state->clip->getYMaxI();
1591     }
1592 
1593     origBlendFunc = state->blendFunc;
1594     state->blendFunc = &blendXor;
1595     pipeInit(&pipe, 0, yMinI, state->fillPattern, NULL, 1, gFalse, gFalse);
1596 
1597     // draw the spans
1598     for (y = yMinI; y <= yMaxI; ++y) {
1599       while (scanner->getNextSpan(y, &x0, &x1)) {
1600 	if (clipRes == splashClipAllInside) {
1601 	  drawSpan(&pipe, x0, x1, y, gTrue);
1602 	} else {
1603 	  // limit the x range
1604 	  if (x0 < state->clip->getXMinI()) {
1605 	    x0 = state->clip->getXMinI();
1606 	  }
1607 	  if (x1 > state->clip->getXMaxI()) {
1608 	    x1 = state->clip->getXMaxI();
1609 	  }
1610 	  clipRes2 = state->clip->testSpan(x0, x1, y);
1611 	  drawSpan(&pipe, x0, x1, y, clipRes2 == splashClipAllInside);
1612 	}
1613       }
1614     }
1615     state->blendFunc = origBlendFunc;
1616   }
1617   opClipRes = clipRes;
1618 
1619   delete scanner;
1620   delete xPath;
1621   return splashOk;
1622 }
1623 
fillChar(SplashCoord x,SplashCoord y,int c,SplashFont * font)1624 SplashError Splash::fillChar(SplashCoord x, SplashCoord y,
1625 			     int c, SplashFont *font) {
1626   SplashGlyphBitmap glyph;
1627   SplashCoord xt, yt;
1628   int x0, y0, xFrac, yFrac;
1629   SplashError err;
1630 
1631   if (debugMode) {
1632     printf("fillChar: x=%.2f y=%.2f c=%3d=0x%02x='%c'\n",
1633 	   (double)x, (double)y, c, c, c);
1634   }
1635   transform(state->matrix, x, y, &xt, &yt);
1636   x0 = splashFloor(xt);
1637   xFrac = splashFloor((xt - x0) * splashFontFraction);
1638   y0 = splashFloor(yt);
1639   yFrac = splashFloor((yt - y0) * splashFontFraction);
1640   if (!font->getGlyph(c, xFrac, yFrac, &glyph)) {
1641     return splashErrNoGlyph;
1642   }
1643   err = fillGlyph2(x0, y0, &glyph);
1644   if (glyph.freeData) {
1645     gfree(glyph.data);
1646   }
1647   return err;
1648 }
1649 
fillGlyph(SplashCoord x,SplashCoord y,SplashGlyphBitmap * glyph)1650 SplashError Splash::fillGlyph(SplashCoord x, SplashCoord y,
1651 			      SplashGlyphBitmap *glyph) {
1652   SplashCoord xt, yt;
1653   int x0, y0;
1654 
1655   transform(state->matrix, x, y, &xt, &yt);
1656   x0 = splashFloor(xt);
1657   y0 = splashFloor(yt);
1658   return fillGlyph2(x0, y0, glyph);
1659 }
1660 
fillGlyph2(int x0,int y0,SplashGlyphBitmap * glyph)1661 SplashError Splash::fillGlyph2(int x0, int y0, SplashGlyphBitmap *glyph) {
1662   SplashPipe pipe;
1663   SplashClipResult clipRes;
1664   GBool noClip;
1665   int alpha0, alpha;
1666   Guchar *p;
1667   int x1, y1, xx, xx1, yy;
1668 
1669   if ((clipRes = state->clip->testRect(x0 - glyph->x,
1670 				       y0 - glyph->y,
1671 				       x0 - glyph->x + glyph->w - 1,
1672 				       y0 - glyph->y + glyph->h - 1))
1673       != splashClipAllOutside) {
1674     noClip = clipRes == splashClipAllInside;
1675 
1676     if (noClip) {
1677       if (glyph->aa) {
1678 	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1679 		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1680 	p = glyph->data;
1681 	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1682 	  pipeSetXY(&pipe, x0 - glyph->x, y1);
1683 	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1684 	    alpha = *p++;
1685 	    if (alpha != 0) {
1686 	      pipe.shape = (SplashCoord)(alpha / 255.0);
1687 	      pipeRun(&pipe);
1688 	      updateModX(x1);
1689 	      updateModY(y1);
1690 	    } else {
1691 	      pipeIncX(&pipe);
1692 	    }
1693 	  }
1694 	}
1695       } else {
1696 	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1697 		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1698 	p = glyph->data;
1699 	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1700 	  pipeSetXY(&pipe, x0 - glyph->x, y1);
1701 	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1702 	    alpha0 = *p++;
1703 	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1704 	      if (alpha0 & 0x80) {
1705 		pipeRun(&pipe);
1706 		updateModX(x1);
1707 		updateModY(y1);
1708 	      } else {
1709 		pipeIncX(&pipe);
1710 	      }
1711 	      alpha0 <<= 1;
1712 	    }
1713 	  }
1714 	}
1715       }
1716     } else {
1717       if (glyph->aa) {
1718 	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1719 		 state->fillPattern, NULL, state->fillAlpha, gTrue, gFalse);
1720 	p = glyph->data;
1721 	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1722 	  pipeSetXY(&pipe, x0 - glyph->x, y1);
1723 	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; ++xx, ++x1) {
1724 	    if (state->clip->test(x1, y1)) {
1725 	      alpha = *p++;
1726 	      if (alpha != 0) {
1727 		pipe.shape = (SplashCoord)(alpha / 255.0);
1728 		pipeRun(&pipe);
1729 		updateModX(x1);
1730 		updateModY(y1);
1731 	      } else {
1732 		pipeIncX(&pipe);
1733 	      }
1734 	    } else {
1735 	      pipeIncX(&pipe);
1736 	      ++p;
1737 	    }
1738 	  }
1739 	}
1740       } else {
1741 	pipeInit(&pipe, x0 - glyph->x, y0 - glyph->y,
1742 		 state->fillPattern, NULL, state->fillAlpha, gFalse, gFalse);
1743 	p = glyph->data;
1744 	for (yy = 0, y1 = y0 - glyph->y; yy < glyph->h; ++yy, ++y1) {
1745 	  pipeSetXY(&pipe, x0 - glyph->x, y1);
1746 	  for (xx = 0, x1 = x0 - glyph->x; xx < glyph->w; xx += 8) {
1747 	    alpha0 = *p++;
1748 	    for (xx1 = 0; xx1 < 8 && xx + xx1 < glyph->w; ++xx1, ++x1) {
1749 	      if (state->clip->test(x1, y1)) {
1750 		if (alpha0 & 0x80) {
1751 		  pipeRun(&pipe);
1752 		  updateModX(x1);
1753 		  updateModY(y1);
1754 		} else {
1755 		  pipeIncX(&pipe);
1756 		}
1757 	      } else {
1758 		pipeIncX(&pipe);
1759 	      }
1760 	      alpha0 <<= 1;
1761 	    }
1762 	  }
1763 	}
1764       }
1765     }
1766   }
1767   opClipRes = clipRes;
1768 
1769   return splashOk;
1770 }
1771 
fillImageMask(SplashImageMaskSource src,void * srcData,int w,int h,SplashCoord * mat,GBool glyphMode)1772 SplashError Splash::fillImageMask(SplashImageMaskSource src, void *srcData,
1773 				  int w, int h, SplashCoord *mat,
1774 				  GBool glyphMode) {
1775   SplashPipe pipe;
1776   GBool rot;
1777   SplashCoord xScale, yScale, xShear, yShear, yShear1;
1778   int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
1779   int ulx, uly, llx, lly, urx, ury, lrx, lry;
1780   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
1781   int xMin, xMax, yMin, yMax;
1782   SplashClipResult clipRes, clipRes2;
1783   int yp, yq, yt, yStep, lastYStep;
1784   int xp, xq, xt, xStep, xSrc;
1785   int k1, spanXMin, spanXMax, spanY;
1786   SplashColorPtr pixBuf, p;
1787   int pixAcc;
1788   int x, y, x1, x2, y2;
1789   SplashCoord y1;
1790   int n, m, i, j;
1791 
1792   if (debugMode) {
1793     printf("fillImageMask: w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
1794 	   w, h, (double)mat[0], (double)mat[1], (double)mat[2],
1795 	   (double)mat[3], (double)mat[4], (double)mat[5]);
1796   }
1797 
1798   // check for singular matrix
1799   if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
1800     return splashErrSingularMatrix;
1801   }
1802 
1803   // compute scale, shear, rotation, translation parameters
1804   rot = splashAbs(mat[1]) > splashAbs(mat[0]);
1805   if (rot) {
1806     xScale = -mat[1];
1807     yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
1808     xShear = -mat[3] / yScale;
1809     yShear = -mat[0] / mat[1];
1810   } else {
1811     xScale = mat[0];
1812     yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
1813     xShear = mat[2] / yScale;
1814     yShear = mat[1] / mat[0];
1815   }
1816   // Note 1: The PDF spec says that all pixels whose *centers* lie
1817   // within the region get painted -- but that doesn't seem to match
1818   // up with what Acrobat actually does: it ends up leaving gaps
1819   // between image stripes.  So we use the same rule here as for
1820   // fills: any pixel that overlaps the region gets painted.
1821   // Note 2: The "glyphMode" flag is a kludge: it switches back to
1822   // "correct" behavior (matching the spec), for use in rendering Type
1823   // 3 fonts.
1824   // Note 3: The +/-0.01 in these computations is to avoid floating
1825   // point precision problems which can lead to gaps between image
1826   // stripes (it can cause image stripes to overlap, but that's a much
1827   // less visible problem).
1828   if (glyphMode) {
1829     if (xScale >= 0) {
1830       tx = splashRound(mat[4]);
1831       tx2 = splashRound(mat[4] + xScale) - 1;
1832     } else {
1833       tx = splashRound(mat[4]) - 1;
1834       tx2 = splashRound(mat[4] + xScale);
1835     }
1836   } else {
1837     if (xScale >= 0) {
1838       tx = splashFloor(mat[4] - 0.01);
1839       tx2 = splashFloor(mat[4] + xScale + 0.01);
1840     } else {
1841       tx = splashFloor(mat[4] + 0.01);
1842       tx2 = splashFloor(mat[4] + xScale - 0.01);
1843     }
1844   }
1845   scaledWidth = abs(tx2 - tx) + 1;
1846   if (glyphMode) {
1847     if (yScale >= 0) {
1848       ty = splashRound(mat[5]);
1849       ty2 = splashRound(mat[5] + yScale) - 1;
1850     } else {
1851       ty = splashRound(mat[5]) - 1;
1852       ty2 = splashRound(mat[5] + yScale);
1853     }
1854   } else {
1855     if (yScale >= 0) {
1856       ty = splashFloor(mat[5] - 0.01);
1857       ty2 = splashFloor(mat[5] + yScale + 0.01);
1858     } else {
1859       ty = splashFloor(mat[5] + 0.01);
1860       ty2 = splashFloor(mat[5] + yScale - 0.01);
1861     }
1862   }
1863   scaledHeight = abs(ty2 - ty) + 1;
1864   xSign = (xScale < 0) ? -1 : 1;
1865   ySign = (yScale < 0) ? -1 : 1;
1866   yShear1 = (SplashCoord)xSign * yShear;
1867 
1868   // clipping
1869   ulx1 = 0;
1870   uly1 = 0;
1871   urx1 = xSign * (scaledWidth - 1);
1872   ury1 = (int)(yShear * urx1);
1873   llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
1874   lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
1875   lrx1 = xSign * (scaledWidth - 1) +
1876            splashRound(xShear * ySign * (scaledHeight - 1));
1877   lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
1878   if (rot) {
1879     ulx = tx + uly1;    uly = ty - ulx1;
1880     urx = tx + ury1;    ury = ty - urx1;
1881     llx = tx + lly1;    lly = ty - llx1;
1882     lrx = tx + lry1;    lry = ty - lrx1;
1883   } else {
1884     ulx = tx + ulx1;    uly = ty + uly1;
1885     urx = tx + urx1;    ury = ty + ury1;
1886     llx = tx + llx1;    lly = ty + lly1;
1887     lrx = tx + lrx1;    lry = ty + lry1;
1888   }
1889   xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
1890                                    : (llx < lrx) ? llx : lrx
1891 		     : (urx < llx) ? (urx < lrx) ? urx : lrx
1892                                    : (llx < lrx) ? llx : lrx;
1893   xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
1894                                    : (llx > lrx) ? llx : lrx
1895 		     : (urx > llx) ? (urx > lrx) ? urx : lrx
1896                                    : (llx > lrx) ? llx : lrx;
1897   yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
1898                                    : (lly < lry) ? lly : lry
1899 		     : (ury < lly) ? (ury < lry) ? ury : lry
1900                                    : (lly < lry) ? lly : lry;
1901   yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
1902                                    : (lly > lry) ? lly : lry
1903 		     : (ury > lly) ? (ury > lry) ? ury : lry
1904                                    : (lly > lry) ? lly : lry;
1905   clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
1906   opClipRes = clipRes;
1907 
1908   // compute Bresenham parameters for x and y scaling
1909   yp = h / scaledHeight;
1910   yq = h % scaledHeight;
1911   xp = w / scaledWidth;
1912   xq = w % scaledWidth;
1913 
1914   // allocate pixel buffer
1915   pixBuf = (SplashColorPtr)gmalloc((yp + 1) * w);
1916 
1917   // initialize the pixel pipe
1918   pipeInit(&pipe, 0, 0, state->fillPattern, NULL, state->fillAlpha,
1919 	   gTrue, gFalse);
1920   if (vectorAntialias) {
1921     drawAAPixelInit();
1922   }
1923 
1924   // init y scale Bresenham
1925   yt = 0;
1926   lastYStep = 1;
1927 
1928   for (y = 0; y < scaledHeight; ++y) {
1929 
1930     // y scale Bresenham
1931     yStep = yp;
1932     yt += yq;
1933     if (yt >= scaledHeight) {
1934       yt -= scaledHeight;
1935       ++yStep;
1936     }
1937 
1938     // read row(s) from image
1939     n = (yp > 0) ? yStep : lastYStep;
1940     if (n > 0) {
1941       p = pixBuf;
1942       for (i = 0; i < n; ++i) {
1943 	(*src)(srcData, p);
1944 	p += w;
1945       }
1946     }
1947     lastYStep = yStep;
1948 
1949     // loop-invariant constants
1950     k1 = splashRound(xShear * ySign * y);
1951 
1952     // clipping test
1953     if (clipRes != splashClipAllInside &&
1954 	!rot &&
1955 	(int)(yShear * k1) ==
1956 	  (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
1957       if (xSign > 0) {
1958 	spanXMin = tx + k1;
1959 	spanXMax = spanXMin + (scaledWidth - 1);
1960       } else {
1961 	spanXMax = tx + k1;
1962 	spanXMin = spanXMax - (scaledWidth - 1);
1963       }
1964       spanY = ty + ySign * y + (int)(yShear * k1);
1965       clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
1966       if (clipRes2 == splashClipAllOutside) {
1967 	continue;
1968       }
1969     } else {
1970       clipRes2 = clipRes;
1971     }
1972 
1973     // init x scale Bresenham
1974     xt = 0;
1975     xSrc = 0;
1976 
1977     // x shear
1978     x1 = k1;
1979 
1980     // y shear
1981     y1 = (SplashCoord)ySign * y + yShear * x1;
1982     // this is a kludge: if yShear1 is negative, then (int)y1 would
1983     // change immediately after the first pixel, which is not what we
1984     // want
1985     if (yShear1 < 0) {
1986       y1 += 0.999;
1987     }
1988 
1989     // loop-invariant constants
1990     n = yStep > 0 ? yStep : 1;
1991 
1992     for (x = 0; x < scaledWidth; ++x) {
1993 
1994       // x scale Bresenham
1995       xStep = xp;
1996       xt += xq;
1997       if (xt >= scaledWidth) {
1998 	xt -= scaledWidth;
1999 	++xStep;
2000       }
2001 
2002       // rotation
2003       if (rot) {
2004 	x2 = (int)y1;
2005 	y2 = -x1;
2006       } else {
2007 	x2 = x1;
2008 	y2 = (int)y1;
2009       }
2010 
2011       // compute the alpha value for (x,y) after the x and y scaling
2012       // operations
2013       m = xStep > 0 ? xStep : 1;
2014       p = pixBuf + xSrc;
2015       pixAcc = 0;
2016       for (i = 0; i < n; ++i) {
2017 	for (j = 0; j < m; ++j) {
2018 	  pixAcc += *p++;
2019 	}
2020 	p += w - m;
2021       }
2022 
2023       // blend fill color with background
2024       if (pixAcc != 0) {
2025 	pipe.shape = (pixAcc == n * m)
2026 	                 ? (SplashCoord)1
2027 	                 : (SplashCoord)pixAcc / (SplashCoord)(n * m);
2028 	if (vectorAntialias && clipRes2 != splashClipAllInside) {
2029 	  drawAAPixel(&pipe, tx + x2, ty + y2);
2030 	} else {
2031 	  drawPixel(&pipe, tx + x2, ty + y2, clipRes2 == splashClipAllInside);
2032 	}
2033       }
2034 
2035       // x scale Bresenham
2036       xSrc += xStep;
2037 
2038       // x shear
2039       x1 += xSign;
2040 
2041       // y shear
2042       y1 += yShear1;
2043     }
2044   }
2045 
2046   // free memory
2047   gfree(pixBuf);
2048 
2049   return splashOk;
2050 }
2051 
drawImage(SplashImageSource src,void * srcData,SplashColorMode srcMode,GBool srcAlpha,int w,int h,SplashCoord * mat)2052 SplashError Splash::drawImage(SplashImageSource src, void *srcData,
2053 			      SplashColorMode srcMode, GBool srcAlpha,
2054 			      int w, int h, SplashCoord *mat) {
2055   SplashPipe pipe;
2056   GBool ok, rot;
2057   SplashCoord xScale, yScale, xShear, yShear, yShear1;
2058   int tx, tx2, ty, ty2, scaledWidth, scaledHeight, xSign, ySign;
2059   int ulx, uly, llx, lly, urx, ury, lrx, lry;
2060   int ulx1, uly1, llx1, lly1, urx1, ury1, lrx1, lry1;
2061   int xMin, xMax, yMin, yMax;
2062   SplashClipResult clipRes, clipRes2;
2063   int yp, yq, yt, yStep, lastYStep;
2064   int xp, xq, xt, xStep, xSrc;
2065   int k1, spanXMin, spanXMax, spanY;
2066   SplashColorPtr colorBuf, p;
2067   SplashColor pix;
2068   Guchar *alphaBuf, *q;
2069 #if SPLASH_CMYK
2070   int pixAcc0, pixAcc1, pixAcc2, pixAcc3;
2071 #else
2072   int pixAcc0, pixAcc1, pixAcc2;
2073 #endif
2074   int alphaAcc;
2075   SplashCoord pixMul, alphaMul, alpha;
2076   int x, y, x1, x2, y2;
2077   SplashCoord y1;
2078   int nComps, n, m, i, j;
2079 
2080   if (debugMode) {
2081     printf("drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n",
2082 	   srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2],
2083 	   (double)mat[3], (double)mat[4], (double)mat[5]);
2084   }
2085 
2086   // check color modes
2087   ok = gFalse; // make gcc happy
2088   nComps = 0; // make gcc happy
2089   switch (bitmap->mode) {
2090   case splashModeMono1:
2091   case splashModeMono8:
2092     ok = srcMode == splashModeMono8;
2093     nComps = 1;
2094     break;
2095   case splashModeRGB8:
2096     ok = srcMode == splashModeRGB8;
2097     nComps = 3;
2098     break;
2099   case splashModeBGR8:
2100     ok = srcMode == splashModeBGR8;
2101     nComps = 3;
2102     break;
2103 #if SPLASH_CMYK
2104   case splashModeCMYK8:
2105     ok = srcMode == splashModeCMYK8;
2106     nComps = 4;
2107     break;
2108 #endif
2109   }
2110   if (!ok) {
2111     return splashErrModeMismatch;
2112   }
2113 
2114   // check for singular matrix
2115   if (splashAbs(mat[0] * mat[3] - mat[1] * mat[2]) < 0.000001) {
2116     return splashErrSingularMatrix;
2117   }
2118 
2119   // compute scale, shear, rotation, translation parameters
2120   rot = splashAbs(mat[1]) > splashAbs(mat[0]);
2121   if (rot) {
2122     xScale = -mat[1];
2123     yScale = mat[2] - (mat[0] * mat[3]) / mat[1];
2124     xShear = -mat[3] / yScale;
2125     yShear = -mat[0] / mat[1];
2126   } else {
2127     xScale = mat[0];
2128     yScale = mat[3] - (mat[1] * mat[2]) / mat[0];
2129     xShear = mat[2] / yScale;
2130     yShear = mat[1] / mat[0];
2131   }
2132   // Note 1: The PDF spec says that all pixels whose *centers* lie
2133   // within the region get painted -- but that doesn't seem to match
2134   // up with what Acrobat actually does: it ends up leaving gaps
2135   // between image stripes.  So we use the same rule here as for
2136   // fills: any pixel that overlaps the region gets painted.
2137   // Note 2: The +/-0.01 in these computations is to avoid floating
2138   // point precision problems which can lead to gaps between image
2139   // stripes (it can cause image stripes to overlap, but that's a much
2140   // less visible problem).
2141   if (xScale >= 0) {
2142     tx = splashFloor(mat[4] - 0.01);
2143     tx2 = splashFloor(mat[4] + xScale + 0.01);
2144   } else {
2145     tx = splashFloor(mat[4] + 0.01);
2146     tx2 = splashFloor(mat[4] + xScale - 0.01);
2147   }
2148   scaledWidth = abs(tx2 - tx) + 1;
2149   if (yScale >= 0) {
2150     ty = splashFloor(mat[5] - 0.01);
2151     ty2 = splashFloor(mat[5] + yScale + 0.01);
2152   } else {
2153     ty = splashFloor(mat[5] + 0.01);
2154     ty2 = splashFloor(mat[5] + yScale - 0.01);
2155   }
2156   scaledHeight = abs(ty2 - ty) + 1;
2157   xSign = (xScale < 0) ? -1 : 1;
2158   ySign = (yScale < 0) ? -1 : 1;
2159   yShear1 = (SplashCoord)xSign * yShear;
2160 
2161   // clipping
2162   ulx1 = 0;
2163   uly1 = 0;
2164   urx1 = xSign * (scaledWidth - 1);
2165   ury1 = (int)(yShear * urx1);
2166   llx1 = splashRound(xShear * ySign * (scaledHeight - 1));
2167   lly1 = ySign * (scaledHeight - 1) + (int)(yShear * llx1);
2168   lrx1 = xSign * (scaledWidth - 1) +
2169            splashRound(xShear * ySign * (scaledHeight - 1));
2170   lry1 = ySign * (scaledHeight - 1) + (int)(yShear * lrx1);
2171   if (rot) {
2172     ulx = tx + uly1;    uly = ty - ulx1;
2173     urx = tx + ury1;    ury = ty - urx1;
2174     llx = tx + lly1;    lly = ty - llx1;
2175     lrx = tx + lry1;    lry = ty - lrx1;
2176   } else {
2177     ulx = tx + ulx1;    uly = ty + uly1;
2178     urx = tx + urx1;    ury = ty + ury1;
2179     llx = tx + llx1;    lly = ty + lly1;
2180     lrx = tx + lrx1;    lry = ty + lry1;
2181   }
2182   xMin = (ulx < urx) ? (ulx < llx) ? (ulx < lrx) ? ulx : lrx
2183                                    : (llx < lrx) ? llx : lrx
2184 		     : (urx < llx) ? (urx < lrx) ? urx : lrx
2185                                    : (llx < lrx) ? llx : lrx;
2186   xMax = (ulx > urx) ? (ulx > llx) ? (ulx > lrx) ? ulx : lrx
2187                                    : (llx > lrx) ? llx : lrx
2188 		     : (urx > llx) ? (urx > lrx) ? urx : lrx
2189                                    : (llx > lrx) ? llx : lrx;
2190   yMin = (uly < ury) ? (uly < lly) ? (uly < lry) ? uly : lry
2191                                    : (lly < lry) ? lly : lry
2192 		     : (ury < lly) ? (ury < lry) ? ury : lry
2193                                    : (lly < lry) ? lly : lry;
2194   yMax = (uly > ury) ? (uly > lly) ? (uly > lry) ? uly : lry
2195                                    : (lly > lry) ? lly : lry
2196 		     : (ury > lly) ? (ury > lry) ? ury : lry
2197                                    : (lly > lry) ? lly : lry;
2198   clipRes = state->clip->testRect(xMin, yMin, xMax, yMax);
2199   opClipRes = clipRes;
2200   if (clipRes == splashClipAllOutside) {
2201     return splashOk;
2202   }
2203 
2204   // compute Bresenham parameters for x and y scaling
2205   yp = h / scaledHeight;
2206   yq = h % scaledHeight;
2207   xp = w / scaledWidth;
2208   xq = w % scaledWidth;
2209 
2210   // allocate pixel buffers
2211   colorBuf = (SplashColorPtr)gmalloc((yp + 1) * w * nComps);
2212   if (srcAlpha) {
2213     alphaBuf = (Guchar *)gmalloc((yp + 1) * w);
2214   } else {
2215     alphaBuf = NULL;
2216   }
2217 
2218   pixAcc0 = pixAcc1 = pixAcc2 = 0; // make gcc happy
2219 #if SPLASH_CMYK
2220   pixAcc3 = 0; // make gcc happy
2221 #endif
2222 
2223   // initialize the pixel pipe
2224   pipeInit(&pipe, 0, 0, NULL, pix, state->fillAlpha,
2225 	   srcAlpha || (vectorAntialias && clipRes != splashClipAllInside),
2226 	   gFalse);
2227   if (vectorAntialias) {
2228     drawAAPixelInit();
2229   }
2230 
2231   if (srcAlpha) {
2232 
2233     // init y scale Bresenham
2234     yt = 0;
2235     lastYStep = 1;
2236 
2237     for (y = 0; y < scaledHeight; ++y) {
2238 
2239       // y scale Bresenham
2240       yStep = yp;
2241       yt += yq;
2242       if (yt >= scaledHeight) {
2243 	yt -= scaledHeight;
2244 	++yStep;
2245       }
2246 
2247       // read row(s) from image
2248       n = (yp > 0) ? yStep : lastYStep;
2249       if (n > 0) {
2250 	p = colorBuf;
2251 	q = alphaBuf;
2252 	for (i = 0; i < n; ++i) {
2253 	  (*src)(srcData, p, q);
2254 	  p += w * nComps;
2255 	  q += w;
2256 	}
2257       }
2258       lastYStep = yStep;
2259 
2260       // loop-invariant constants
2261       k1 = splashRound(xShear * ySign * y);
2262 
2263       // clipping test
2264       if (clipRes != splashClipAllInside &&
2265 	  !rot &&
2266 	  (int)(yShear * k1) ==
2267 	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2268 	if (xSign > 0) {
2269 	  spanXMin = tx + k1;
2270 	  spanXMax = spanXMin + (scaledWidth - 1);
2271 	} else {
2272 	  spanXMax = tx + k1;
2273 	  spanXMin = spanXMax - (scaledWidth - 1);
2274 	}
2275 	spanY = ty + ySign * y + (int)(yShear * k1);
2276 	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2277 	if (clipRes2 == splashClipAllOutside) {
2278 	  continue;
2279 	}
2280       } else {
2281 	clipRes2 = clipRes;
2282       }
2283 
2284       // init x scale Bresenham
2285       xt = 0;
2286       xSrc = 0;
2287 
2288       // x shear
2289       x1 = k1;
2290 
2291       // y shear
2292       y1 = (SplashCoord)ySign * y + yShear * x1;
2293       // this is a kludge: if yShear1 is negative, then (int)y1 would
2294       // change immediately after the first pixel, which is not what
2295       // we want
2296       if (yShear1 < 0) {
2297 	y1 += 0.999;
2298       }
2299 
2300       // loop-invariant constants
2301       n = yStep > 0 ? yStep : 1;
2302 
2303       switch (srcMode) {
2304 
2305       case splashModeMono1:
2306       case splashModeMono8:
2307 	for (x = 0; x < scaledWidth; ++x) {
2308 
2309 	  // x scale Bresenham
2310 	  xStep = xp;
2311 	  xt += xq;
2312 	  if (xt >= scaledWidth) {
2313 	    xt -= scaledWidth;
2314 	    ++xStep;
2315 	  }
2316 
2317 	  // rotation
2318 	  if (rot) {
2319 	    x2 = (int)y1;
2320 	    y2 = -x1;
2321 	  } else {
2322 	    x2 = x1;
2323 	    y2 = (int)y1;
2324 	  }
2325 
2326 	  // compute the filtered pixel at (x,y) after the x and y scaling
2327 	  // operations
2328 	  m = xStep > 0 ? xStep : 1;
2329 	  alphaAcc = 0;
2330 	  p = colorBuf + xSrc;
2331 	  q = alphaBuf + xSrc;
2332 	  pixAcc0 = 0;
2333 	  for (i = 0; i < n; ++i) {
2334 	    for (j = 0; j < m; ++j) {
2335 	      pixAcc0 += *p++;
2336 	      alphaAcc += *q++;
2337 	    }
2338 	    p += w - m;
2339 	    q += w - m;
2340 	  }
2341 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2342 	  alphaMul = pixMul * (1.0 / 255.0);
2343 	  alpha = (SplashCoord)alphaAcc * alphaMul;
2344 
2345 	  if (alpha > 0) {
2346 	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2347 
2348 	    // set pixel
2349 	    pipe.shape = alpha;
2350 	    if (vectorAntialias && clipRes != splashClipAllInside) {
2351 	      drawAAPixel(&pipe, tx + x2, ty + y2);
2352 	    } else {
2353 	      drawPixel(&pipe, tx + x2, ty + y2,
2354 			clipRes2 == splashClipAllInside);
2355 	    }
2356 	  }
2357 
2358 	  // x scale Bresenham
2359 	  xSrc += xStep;
2360 
2361 	  // x shear
2362 	  x1 += xSign;
2363 
2364 	  // y shear
2365 	  y1 += yShear1;
2366 	}
2367 	break;
2368 
2369       case splashModeRGB8:
2370       case splashModeBGR8:
2371 	for (x = 0; x < scaledWidth; ++x) {
2372 
2373 	  // x scale Bresenham
2374 	  xStep = xp;
2375 	  xt += xq;
2376 	  if (xt >= scaledWidth) {
2377 	    xt -= scaledWidth;
2378 	    ++xStep;
2379 	  }
2380 
2381 	  // rotation
2382 	  if (rot) {
2383 	    x2 = (int)y1;
2384 	    y2 = -x1;
2385 	  } else {
2386 	    x2 = x1;
2387 	    y2 = (int)y1;
2388 	  }
2389 
2390 	  // compute the filtered pixel at (x,y) after the x and y scaling
2391 	  // operations
2392 	  m = xStep > 0 ? xStep : 1;
2393 	  alphaAcc = 0;
2394 	  p = colorBuf + xSrc * 3;
2395 	  q = alphaBuf + xSrc;
2396 	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
2397 	  for (i = 0; i < n; ++i) {
2398 	    for (j = 0; j < m; ++j) {
2399 	      pixAcc0 += *p++;
2400 	      pixAcc1 += *p++;
2401 	      pixAcc2 += *p++;
2402 	      alphaAcc += *q++;
2403 	    }
2404 	    p += 3 * (w - m);
2405 	    q += w - m;
2406 	  }
2407 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2408 	  alphaMul = pixMul * (1.0 / 255.0);
2409 	  alpha = (SplashCoord)alphaAcc * alphaMul;
2410 
2411 	  if (alpha > 0) {
2412 	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2413 	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2414 	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2415 
2416 	    // set pixel
2417 	    pipe.shape = alpha;
2418 	    if (vectorAntialias && clipRes != splashClipAllInside) {
2419 	      drawAAPixel(&pipe, tx + x2, ty + y2);
2420 	    } else {
2421 	      drawPixel(&pipe, tx + x2, ty + y2,
2422 			clipRes2 == splashClipAllInside);
2423 	    }
2424 	  }
2425 
2426 	  // x scale Bresenham
2427 	  xSrc += xStep;
2428 
2429 	  // x shear
2430 	  x1 += xSign;
2431 
2432 	  // y shear
2433 	  y1 += yShear1;
2434 	}
2435 	break;
2436 
2437 #if SPLASH_CMYK
2438       case splashModeCMYK8:
2439 	for (x = 0; x < scaledWidth; ++x) {
2440 
2441 	  // x scale Bresenham
2442 	  xStep = xp;
2443 	  xt += xq;
2444 	  if (xt >= scaledWidth) {
2445 	    xt -= scaledWidth;
2446 	    ++xStep;
2447 	  }
2448 
2449 	  // rotation
2450 	  if (rot) {
2451 	    x2 = (int)y1;
2452 	    y2 = -x1;
2453 	  } else {
2454 	    x2 = x1;
2455 	    y2 = (int)y1;
2456 	  }
2457 
2458 	  // compute the filtered pixel at (x,y) after the x and y scaling
2459 	  // operations
2460 	  m = xStep > 0 ? xStep : 1;
2461 	  alphaAcc = 0;
2462 	  p = colorBuf + xSrc * 4;
2463 	  q = alphaBuf + xSrc;
2464 	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2465 	  for (i = 0; i < n; ++i) {
2466 	    for (j = 0; j < m; ++j) {
2467 	      pixAcc0 += *p++;
2468 	      pixAcc1 += *p++;
2469 	      pixAcc2 += *p++;
2470 	      pixAcc3 += *p++;
2471 	      alphaAcc += *q++;
2472 	    }
2473 	    p += 4 * (w - m);
2474 	    q += w - m;
2475 	  }
2476 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2477 	  alphaMul = pixMul * (1.0 / 255.0);
2478 	  alpha = (SplashCoord)alphaAcc * alphaMul;
2479 
2480 	  if (alpha > 0) {
2481 	    pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2482 	    pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2483 	    pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2484 	    pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2485 
2486 	    // set pixel
2487 	    pipe.shape = alpha;
2488 	    if (vectorAntialias && clipRes != splashClipAllInside) {
2489 	      drawAAPixel(&pipe, tx + x2, ty + y2);
2490 	    } else {
2491 	      drawPixel(&pipe, tx + x2, ty + y2,
2492 			clipRes2 == splashClipAllInside);
2493 	    }
2494 	  }
2495 
2496 	  // x scale Bresenham
2497 	  xSrc += xStep;
2498 
2499 	  // x shear
2500 	  x1 += xSign;
2501 
2502 	  // y shear
2503 	  y1 += yShear1;
2504 	}
2505 	break;
2506 #endif // SPLASH_CMYK
2507       }
2508     }
2509 
2510   } else {
2511 
2512     // init y scale Bresenham
2513     yt = 0;
2514     lastYStep = 1;
2515 
2516     for (y = 0; y < scaledHeight; ++y) {
2517 
2518       // y scale Bresenham
2519       yStep = yp;
2520       yt += yq;
2521       if (yt >= scaledHeight) {
2522 	yt -= scaledHeight;
2523 	++yStep;
2524       }
2525 
2526       // read row(s) from image
2527       n = (yp > 0) ? yStep : lastYStep;
2528       if (n > 0) {
2529 	p = colorBuf;
2530 	for (i = 0; i < n; ++i) {
2531 	  (*src)(srcData, p, NULL);
2532 	  p += w * nComps;
2533 	}
2534       }
2535       lastYStep = yStep;
2536 
2537       // loop-invariant constants
2538       k1 = splashRound(xShear * ySign * y);
2539 
2540       // clipping test
2541       if (clipRes != splashClipAllInside &&
2542 	  !rot &&
2543 	  (int)(yShear * k1) ==
2544 	    (int)(yShear * (xSign * (scaledWidth - 1) + k1))) {
2545 	if (xSign > 0) {
2546 	  spanXMin = tx + k1;
2547 	  spanXMax = spanXMin + (scaledWidth - 1);
2548 	} else {
2549 	  spanXMax = tx + k1;
2550 	  spanXMin = spanXMax - (scaledWidth - 1);
2551 	}
2552 	spanY = ty + ySign * y + (int)(yShear * k1);
2553 	clipRes2 = state->clip->testSpan(spanXMin, spanXMax, spanY);
2554 	if (clipRes2 == splashClipAllOutside) {
2555 	  continue;
2556 	}
2557       } else {
2558 	clipRes2 = clipRes;
2559       }
2560 
2561       // init x scale Bresenham
2562       xt = 0;
2563       xSrc = 0;
2564 
2565       // x shear
2566       x1 = k1;
2567 
2568       // y shear
2569       y1 = (SplashCoord)ySign * y + yShear * x1;
2570       // this is a kludge: if yShear1 is negative, then (int)y1 would
2571       // change immediately after the first pixel, which is not what
2572       // we want
2573       if (yShear1 < 0) {
2574 	y1 += 0.999;
2575       }
2576 
2577       // loop-invariant constants
2578       n = yStep > 0 ? yStep : 1;
2579 
2580       switch (srcMode) {
2581 
2582       case splashModeMono1:
2583       case splashModeMono8:
2584 	for (x = 0; x < scaledWidth; ++x) {
2585 
2586 	  // x scale Bresenham
2587 	  xStep = xp;
2588 	  xt += xq;
2589 	  if (xt >= scaledWidth) {
2590 	    xt -= scaledWidth;
2591 	    ++xStep;
2592 	  }
2593 
2594 	  // rotation
2595 	  if (rot) {
2596 	    x2 = (int)y1;
2597 	    y2 = -x1;
2598 	  } else {
2599 	    x2 = x1;
2600 	    y2 = (int)y1;
2601 	  }
2602 
2603 	  // compute the filtered pixel at (x,y) after the x and y scaling
2604 	  // operations
2605 	  m = xStep > 0 ? xStep : 1;
2606 	  p = colorBuf + xSrc;
2607 	  pixAcc0 = 0;
2608 	  for (i = 0; i < n; ++i) {
2609 	    for (j = 0; j < m; ++j) {
2610 	      pixAcc0 += *p++;
2611 	    }
2612 	    p += w - m;
2613 	  }
2614 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2615 
2616 	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2617 
2618 	  // set pixel
2619 	  if (vectorAntialias && clipRes != splashClipAllInside) {
2620 	    pipe.shape = (SplashCoord)1;
2621 	    drawAAPixel(&pipe, tx + x2, ty + y2);
2622 	  } else {
2623 	    drawPixel(&pipe, tx + x2, ty + y2,
2624 		      clipRes2 == splashClipAllInside);
2625 	  }
2626 
2627 	  // x scale Bresenham
2628 	  xSrc += xStep;
2629 
2630 	  // x shear
2631 	  x1 += xSign;
2632 
2633 	  // y shear
2634 	  y1 += yShear1;
2635 	}
2636 	break;
2637 
2638       case splashModeRGB8:
2639       case splashModeBGR8:
2640 	for (x = 0; x < scaledWidth; ++x) {
2641 
2642 	  // x scale Bresenham
2643 	  xStep = xp;
2644 	  xt += xq;
2645 	  if (xt >= scaledWidth) {
2646 	    xt -= scaledWidth;
2647 	    ++xStep;
2648 	  }
2649 
2650 	  // rotation
2651 	  if (rot) {
2652 	    x2 = (int)y1;
2653 	    y2 = -x1;
2654 	  } else {
2655 	    x2 = x1;
2656 	    y2 = (int)y1;
2657 	  }
2658 
2659 	  // compute the filtered pixel at (x,y) after the x and y scaling
2660 	  // operations
2661 	  m = xStep > 0 ? xStep : 1;
2662 	  p = colorBuf + xSrc * 3;
2663 	  pixAcc0 = pixAcc1 = pixAcc2 = 0;
2664 	  for (i = 0; i < n; ++i) {
2665 	    for (j = 0; j < m; ++j) {
2666 	      pixAcc0 += *p++;
2667 	      pixAcc1 += *p++;
2668 	      pixAcc2 += *p++;
2669 	    }
2670 	    p += 3 * (w - m);
2671 	  }
2672 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2673 
2674 	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2675 	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2676 	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2677 
2678 	  // set pixel
2679 	  if (vectorAntialias && clipRes != splashClipAllInside) {
2680 	    pipe.shape = (SplashCoord)1;
2681 	    drawAAPixel(&pipe, tx + x2, ty + y2);
2682 	  } else {
2683 	    drawPixel(&pipe, tx + x2, ty + y2,
2684 		      clipRes2 == splashClipAllInside);
2685 	  }
2686 
2687 	  // x scale Bresenham
2688 	  xSrc += xStep;
2689 
2690 	  // x shear
2691 	  x1 += xSign;
2692 
2693 	  // y shear
2694 	  y1 += yShear1;
2695 	}
2696 	break;
2697 
2698 #if SPLASH_CMYK
2699       case splashModeCMYK8:
2700 	for (x = 0; x < scaledWidth; ++x) {
2701 
2702 	  // x scale Bresenham
2703 	  xStep = xp;
2704 	  xt += xq;
2705 	  if (xt >= scaledWidth) {
2706 	    xt -= scaledWidth;
2707 	    ++xStep;
2708 	  }
2709 
2710 	  // rotation
2711 	  if (rot) {
2712 	    x2 = (int)y1;
2713 	    y2 = -x1;
2714 	  } else {
2715 	    x2 = x1;
2716 	    y2 = (int)y1;
2717 	  }
2718 
2719 	  // compute the filtered pixel at (x,y) after the x and y scaling
2720 	  // operations
2721 	  m = xStep > 0 ? xStep : 1;
2722 	  p = colorBuf + xSrc * 4;
2723 	  pixAcc0 = pixAcc1 = pixAcc2 = pixAcc3 = 0;
2724 	  for (i = 0; i < n; ++i) {
2725 	    for (j = 0; j < m; ++j) {
2726 	      pixAcc0 += *p++;
2727 	      pixAcc1 += *p++;
2728 	      pixAcc2 += *p++;
2729 	      pixAcc3 += *p++;
2730 	    }
2731 	    p += 4 * (w - m);
2732 	  }
2733 	  pixMul = (SplashCoord)1 / (SplashCoord)(n * m);
2734 
2735 	  pix[0] = (int)((SplashCoord)pixAcc0 * pixMul);
2736 	  pix[1] = (int)((SplashCoord)pixAcc1 * pixMul);
2737 	  pix[2] = (int)((SplashCoord)pixAcc2 * pixMul);
2738 	  pix[3] = (int)((SplashCoord)pixAcc3 * pixMul);
2739 
2740 	  // set pixel
2741 	  if (vectorAntialias && clipRes != splashClipAllInside) {
2742 	    pipe.shape = (SplashCoord)1;
2743 	    drawAAPixel(&pipe, tx + x2, ty + y2);
2744 	  } else {
2745 	    drawPixel(&pipe, tx + x2, ty + y2,
2746 		      clipRes2 == splashClipAllInside);
2747 	  }
2748 
2749 	  // x scale Bresenham
2750 	  xSrc += xStep;
2751 
2752 	  // x shear
2753 	  x1 += xSign;
2754 
2755 	  // y shear
2756 	  y1 += yShear1;
2757 	}
2758 	break;
2759 #endif // SPLASH_CMYK
2760       }
2761     }
2762 
2763   }
2764 
2765   gfree(colorBuf);
2766   gfree(alphaBuf);
2767 
2768   return splashOk;
2769 }
2770 
composite(SplashBitmap * src,int xSrc,int ySrc,int xDest,int yDest,int w,int h,GBool noClip,GBool nonIsolated)2771 SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc,
2772 			      int xDest, int yDest, int w, int h,
2773 			      GBool noClip, GBool nonIsolated) {
2774   SplashPipe pipe;
2775   SplashColor pixel;
2776   Guchar alpha;
2777   Guchar *ap;
2778   int x, y;
2779 
2780   if (src->mode != bitmap->mode) {
2781     return splashErrModeMismatch;
2782   }
2783 
2784   if (src->alpha) {
2785     pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
2786 	     gTrue, nonIsolated);
2787     for (y = 0; y < h; ++y) {
2788       pipeSetXY(&pipe, xDest, yDest + y);
2789       ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
2790       for (x = 0; x < w; ++x) {
2791 	src->getPixel(xSrc + x, ySrc + y, pixel);
2792 	alpha = *ap++;
2793 	if (noClip || state->clip->test(xDest + x, yDest + y)) {
2794 	  // this uses shape instead of alpha, which isn't technically
2795 	  // correct, but works out the same
2796 	  pipe.shape = (SplashCoord)(alpha / 255.0);
2797 	  pipeRun(&pipe);
2798 	  updateModX(xDest + x);
2799 	  updateModY(yDest + y);
2800 	} else {
2801 	  pipeIncX(&pipe);
2802 	}
2803       }
2804     }
2805   } else {
2806     pipeInit(&pipe, xDest, yDest, NULL, pixel, state->fillAlpha,
2807 	     gFalse, nonIsolated);
2808     for (y = 0; y < h; ++y) {
2809       pipeSetXY(&pipe, xDest, yDest + y);
2810       for (x = 0; x < w; ++x) {
2811 	src->getPixel(xSrc + x, ySrc + y, pixel);
2812 	if (noClip || state->clip->test(xDest + x, yDest + y)) {
2813 	  pipeRun(&pipe);
2814 	  updateModX(xDest + x);
2815 	  updateModY(yDest + y);
2816 	} else {
2817 	  pipeIncX(&pipe);
2818 	}
2819       }
2820     }
2821   }
2822 
2823   return splashOk;
2824 }
2825 
compositeBackground(SplashColorPtr color)2826 void Splash::compositeBackground(SplashColorPtr color) {
2827   SplashColorPtr p;
2828   Guchar *q;
2829   Guchar alpha, alpha1, c, color0, color1, color2, color3;
2830   int x, y, mask;
2831 
2832   switch (bitmap->mode) {
2833   case splashModeMono1:
2834     color0 = color[0];
2835     for (y = 0; y < bitmap->height; ++y) {
2836       p = &bitmap->data[y * bitmap->rowSize];
2837       q = &bitmap->alpha[y * bitmap->width];
2838       mask = 0x80;
2839       for (x = 0; x < bitmap->width; ++x) {
2840 	alpha = *q++;
2841 	alpha1 = 255 - alpha;
2842 	c = (*p & mask) ? 0xff : 0x00;
2843 	c = div255(alpha1 * color0 + alpha * c);
2844 	if (c & 0x80) {
2845 	  *p |= mask;
2846 	} else {
2847 	  *p &= ~mask;
2848 	}
2849 	if (!(mask >>= 1)) {
2850 	  mask = 0x80;
2851 	  ++p;
2852 	}
2853       }
2854     }
2855     break;
2856   case splashModeMono8:
2857     color0 = color[0];
2858     for (y = 0; y < bitmap->height; ++y) {
2859       p = &bitmap->data[y * bitmap->rowSize];
2860       q = &bitmap->alpha[y * bitmap->width];
2861       for (x = 0; x < bitmap->width; ++x) {
2862 	alpha = *q++;
2863 	alpha1 = 255 - alpha;
2864 	p[0] = div255(alpha1 * color0 + alpha * p[0]);
2865 	++p;
2866       }
2867     }
2868     break;
2869   case splashModeRGB8:
2870   case splashModeBGR8:
2871     color0 = color[0];
2872     color1 = color[1];
2873     color2 = color[2];
2874     for (y = 0; y < bitmap->height; ++y) {
2875       p = &bitmap->data[y * bitmap->rowSize];
2876       q = &bitmap->alpha[y * bitmap->width];
2877       for (x = 0; x < bitmap->width; ++x) {
2878 	alpha = *q++;
2879 	alpha1 = 255 - alpha;
2880 	p[0] = div255(alpha1 * color0 + alpha * p[0]);
2881 	p[1] = div255(alpha1 * color1 + alpha * p[1]);
2882 	p[2] = div255(alpha1 * color2 + alpha * p[2]);
2883 	p += 3;
2884       }
2885     }
2886     break;
2887 #if SPLASH_CMYK
2888   case splashModeCMYK8:
2889     color0 = color[0];
2890     color1 = color[1];
2891     color2 = color[2];
2892     color3 = color[3];
2893     for (y = 0; y < bitmap->height; ++y) {
2894       p = &bitmap->data[y * bitmap->rowSize];
2895       q = &bitmap->alpha[y * bitmap->width];
2896       for (x = 0; x < bitmap->width; ++x) {
2897 	alpha = *q++;
2898 	alpha1 = 255 - alpha;
2899 	p[0] = div255(alpha1 * color0 + alpha * p[0]);
2900 	p[1] = div255(alpha1 * color1 + alpha * p[1]);
2901 	p[2] = div255(alpha1 * color2 + alpha * p[2]);
2902 	p[3] = div255(alpha1 * color3 + alpha * p[3]);
2903 	p += 4;
2904       }
2905     }
2906     break;
2907 #endif
2908   }
2909   memset(bitmap->alpha, 255, bitmap->width * bitmap->height);
2910 }
2911 
blitTransparent(SplashBitmap * src,int xSrc,int ySrc,int xDest,int yDest,int w,int h)2912 SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc,
2913 				    int xDest, int yDest, int w, int h) {
2914   SplashColor pixel;
2915   SplashColorPtr p;
2916   Guchar *q;
2917   int x, y, mask;
2918 
2919   if (src->mode != bitmap->mode) {
2920     return splashErrModeMismatch;
2921   }
2922 
2923   switch (bitmap->mode) {
2924   case splashModeMono1:
2925     for (y = 0; y < h; ++y) {
2926       p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
2927       mask = 0x80 >> (xDest & 7);
2928       for (x = 0; x < w; ++x) {
2929 	src->getPixel(xSrc + x, ySrc + y, pixel);
2930 	if (pixel[0]) {
2931 	  *p |= mask;
2932 	} else {
2933 	  *p &= ~mask;
2934 	}
2935 	if (!(mask >>= 1)) {
2936 	  mask = 0x80;
2937 	  ++p;
2938 	}
2939       }
2940     }
2941     break;
2942   case splashModeMono8:
2943     for (y = 0; y < h; ++y) {
2944       p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
2945       for (x = 0; x < w; ++x) {
2946 	src->getPixel(xSrc + x, ySrc + y, pixel);
2947 	*p++ = pixel[0];
2948       }
2949     }
2950     break;
2951   case splashModeRGB8:
2952   case splashModeBGR8:
2953     for (y = 0; y < h; ++y) {
2954       p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
2955       for (x = 0; x < w; ++x) {
2956 	src->getPixel(xSrc + x, ySrc + y, pixel);
2957 	*p++ = pixel[0];
2958 	*p++ = pixel[1];
2959 	*p++ = pixel[2];
2960       }
2961     }
2962     break;
2963 #if SPLASH_CMYK
2964   case splashModeCMYK8:
2965     for (y = 0; y < h; ++y) {
2966       p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
2967       for (x = 0; x < w; ++x) {
2968 	src->getPixel(xSrc + x, ySrc + y, pixel);
2969 	*p++ = pixel[0];
2970 	*p++ = pixel[1];
2971 	*p++ = pixel[2];
2972 	*p++ = pixel[3];
2973       }
2974     }
2975     break;
2976 #endif
2977   }
2978 
2979   if (bitmap->alpha) {
2980     for (y = 0; y < h; ++y) {
2981       q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
2982       for (x = 0; x < w; ++x) {
2983 	*q++ = 0x00;
2984       }
2985     }
2986   }
2987 
2988   return splashOk;
2989 }
2990 
makeStrokePath(SplashPath * path,GBool flatten)2991 SplashPath *Splash::makeStrokePath(SplashPath *path, GBool flatten) {
2992   SplashPath *pathIn, *pathOut;
2993   SplashCoord w, d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
2994   SplashCoord crossprod, dotprod, miter, m;
2995   GBool first, last, closed;
2996   int subpathStart, next, i;
2997   int left0, left1, left2, right0, right1, right2, join0, join1, join2;
2998   int leftFirst, rightFirst, firstPt;
2999 
3000   if (flatten) {
3001     pathIn = flattenPath(path, state->matrix, state->flatness);
3002     if (state->lineDashLength > 0) {
3003       pathOut = makeDashedPath(pathIn);
3004       delete pathIn;
3005       pathIn = pathOut;
3006     }
3007   } else {
3008     pathIn = path;
3009   }
3010 
3011   subpathStart = 0; // make gcc happy
3012   closed = gFalse; // make gcc happy
3013   left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
3014   leftFirst = rightFirst = firstPt = 0; // make gcc happy
3015 
3016   pathOut = new SplashPath();
3017   w = state->lineWidth;
3018 
3019   for (i = 0; i < pathIn->length - 1; ++i) {
3020     if (pathIn->flags[i] & splashPathLast) {
3021       continue;
3022     }
3023     if ((first = pathIn->flags[i] & splashPathFirst)) {
3024       subpathStart = i;
3025       closed = pathIn->flags[i] & splashPathClosed;
3026     }
3027     last = pathIn->flags[i+1] & splashPathLast;
3028 
3029     // compute the deltas for segment (i, i+1)
3030     d = splashDist(pathIn->pts[i].x, pathIn->pts[i].y,
3031 		   pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3032     if (d == 0) {
3033       // we need to draw end caps on zero-length lines
3034       //~ not clear what the behavior should be for splashLineCapButt
3035       //~   with d==0
3036       dx = 0;
3037       dy = 1;
3038     } else {
3039       d = (SplashCoord)1 / d;
3040       dx = d * (pathIn->pts[i+1].x - pathIn->pts[i].x);
3041       dy = d * (pathIn->pts[i+1].y - pathIn->pts[i].y);
3042     }
3043     wdx = (SplashCoord)0.5 * w * dx;
3044     wdy = (SplashCoord)0.5 * w * dy;
3045 
3046     // compute the deltas for segment (i+1, next)
3047     next = last ? subpathStart + 1 : i + 2;
3048     d = splashDist(pathIn->pts[i+1].x, pathIn->pts[i+1].y,
3049 		   pathIn->pts[next].x, pathIn->pts[next].y);
3050     if (d == 0) {
3051       // we need to draw end caps on zero-length lines
3052       //~ not clear what the behavior should be for splashLineCapButt
3053       //~   with d==0
3054       dxNext = 0;
3055       dyNext = 1;
3056     } else {
3057       d = (SplashCoord)1 / d;
3058       dxNext = d * (pathIn->pts[next].x - pathIn->pts[i+1].x);
3059       dyNext = d * (pathIn->pts[next].y - pathIn->pts[i+1].y);
3060     }
3061     wdxNext = (SplashCoord)0.5 * w * dxNext;
3062     wdyNext = (SplashCoord)0.5 * w * dyNext;
3063 
3064     // draw the start cap
3065     pathOut->moveTo(pathIn->pts[i].x - wdy, pathIn->pts[i].y + wdx);
3066     if (i == subpathStart) {
3067       firstPt = pathOut->length - 1;
3068     }
3069     if (first && !closed) {
3070       switch (state->lineCap) {
3071       case splashLineCapButt:
3072 	pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3073 	break;
3074       case splashLineCapRound:
3075 	pathOut->curveTo(pathIn->pts[i].x - wdy - bezierCircle * wdx,
3076 			 pathIn->pts[i].y + wdx - bezierCircle * wdy,
3077 			 pathIn->pts[i].x - wdx - bezierCircle * wdy,
3078 			 pathIn->pts[i].y - wdy + bezierCircle * wdx,
3079 			 pathIn->pts[i].x - wdx,
3080 			 pathIn->pts[i].y - wdy);
3081 	pathOut->curveTo(pathIn->pts[i].x - wdx + bezierCircle * wdy,
3082 			 pathIn->pts[i].y - wdy - bezierCircle * wdx,
3083 			 pathIn->pts[i].x + wdy - bezierCircle * wdx,
3084 			 pathIn->pts[i].y - wdx - bezierCircle * wdy,
3085 			 pathIn->pts[i].x + wdy,
3086 			 pathIn->pts[i].y - wdx);
3087 	break;
3088       case splashLineCapProjecting:
3089 	pathOut->lineTo(pathIn->pts[i].x - wdx - wdy,
3090 			pathIn->pts[i].y + wdx - wdy);
3091 	pathOut->lineTo(pathIn->pts[i].x - wdx + wdy,
3092 			pathIn->pts[i].y - wdx - wdy);
3093 	pathOut->lineTo(pathIn->pts[i].x + wdy,
3094 			pathIn->pts[i].y - wdx);
3095 	break;
3096       }
3097     } else {
3098       pathOut->lineTo(pathIn->pts[i].x + wdy, pathIn->pts[i].y - wdx);
3099     }
3100 
3101     // draw the left side of the segment rectangle
3102     left2 = pathOut->length - 1;
3103     pathOut->lineTo(pathIn->pts[i+1].x + wdy, pathIn->pts[i+1].y - wdx);
3104 
3105     // draw the end cap
3106     if (last && !closed) {
3107       switch (state->lineCap) {
3108       case splashLineCapButt:
3109 	pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3110 	break;
3111       case splashLineCapRound:
3112 	pathOut->curveTo(pathIn->pts[i+1].x + wdy + bezierCircle * wdx,
3113 			 pathIn->pts[i+1].y - wdx + bezierCircle * wdy,
3114 			 pathIn->pts[i+1].x + wdx + bezierCircle * wdy,
3115 			 pathIn->pts[i+1].y + wdy - bezierCircle * wdx,
3116 			 pathIn->pts[i+1].x + wdx,
3117 			 pathIn->pts[i+1].y + wdy);
3118 	pathOut->curveTo(pathIn->pts[i+1].x + wdx - bezierCircle * wdy,
3119 			 pathIn->pts[i+1].y + wdy + bezierCircle * wdx,
3120 			 pathIn->pts[i+1].x - wdy + bezierCircle * wdx,
3121 			 pathIn->pts[i+1].y + wdx + bezierCircle * wdy,
3122 			 pathIn->pts[i+1].x - wdy,
3123 			 pathIn->pts[i+1].y + wdx);
3124 	break;
3125       case splashLineCapProjecting:
3126 	pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx,
3127 			pathIn->pts[i+1].y - wdx + wdy);
3128 	pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx,
3129 			pathIn->pts[i+1].y + wdx + wdy);
3130 	pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3131 			pathIn->pts[i+1].y + wdx);
3132 	break;
3133       }
3134     } else {
3135       pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3136     }
3137 
3138     // draw the right side of the segment rectangle
3139     right2 = pathOut->length - 1;
3140     pathOut->close();
3141 
3142     // draw the join
3143     join2 = pathOut->length;
3144     if (!last || closed) {
3145       crossprod = dx * dyNext - dy * dxNext;
3146       dotprod = -(dx * dxNext + dy * dyNext);
3147       if (dotprod > 0.99999) {
3148 	// avoid a divide-by-zero -- set miter to something arbitrary
3149 	// such that sqrt(miter) will exceed miterLimit (and m is never
3150 	// used in that situation)
3151 	miter = (state->miterLimit + 1) * (state->miterLimit + 1);
3152 	m = 0;
3153       } else {
3154 	miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
3155 	if (miter < 1) {
3156 	  // this can happen because of floating point inaccuracies
3157 	  miter = 1;
3158 	}
3159 	m = splashSqrt(miter - 1);
3160       }
3161 
3162       // round join
3163       if (state->lineJoin == splashLineJoinRound) {
3164 	pathOut->moveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3165 			pathIn->pts[i+1].y);
3166 	pathOut->curveTo(pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3167 			 pathIn->pts[i+1].y + bezierCircle2 * w,
3168 			 pathIn->pts[i+1].x + bezierCircle2 * w,
3169 			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3170 			 pathIn->pts[i+1].x,
3171 			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w);
3172 	pathOut->curveTo(pathIn->pts[i+1].x - bezierCircle2 * w,
3173 			 pathIn->pts[i+1].y + (SplashCoord)0.5 * w,
3174 			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3175 			 pathIn->pts[i+1].y + bezierCircle2 * w,
3176 			 pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3177 			 pathIn->pts[i+1].y);
3178 	pathOut->curveTo(pathIn->pts[i+1].x - (SplashCoord)0.5 * w,
3179 			 pathIn->pts[i+1].y - bezierCircle2 * w,
3180 			 pathIn->pts[i+1].x - bezierCircle2 * w,
3181 			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3182 			 pathIn->pts[i+1].x,
3183 			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w);
3184 	pathOut->curveTo(pathIn->pts[i+1].x + bezierCircle2 * w,
3185 			 pathIn->pts[i+1].y - (SplashCoord)0.5 * w,
3186 			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3187 			 pathIn->pts[i+1].y - bezierCircle2 * w,
3188 			 pathIn->pts[i+1].x + (SplashCoord)0.5 * w,
3189 			 pathIn->pts[i+1].y);
3190 
3191       } else {
3192 	pathOut->moveTo(pathIn->pts[i+1].x, pathIn->pts[i+1].y);
3193 
3194 	// angle < 180
3195 	if (crossprod < 0) {
3196 	  pathOut->lineTo(pathIn->pts[i+1].x - wdyNext,
3197 			  pathIn->pts[i+1].y + wdxNext);
3198 	  // miter join inside limit
3199 	  if (state->lineJoin == splashLineJoinMiter &&
3200 	      splashSqrt(miter) <= state->miterLimit) {
3201 	    pathOut->lineTo(pathIn->pts[i+1].x - wdy + wdx * m,
3202 			    pathIn->pts[i+1].y + wdx + wdy * m);
3203 	    pathOut->lineTo(pathIn->pts[i+1].x - wdy,
3204 			    pathIn->pts[i+1].y + wdx);
3205 	  // bevel join or miter join outside limit
3206 	  } else {
3207 	    pathOut->lineTo(pathIn->pts[i+1].x - wdy, pathIn->pts[i+1].y + wdx);
3208 	  }
3209 
3210 	// angle >= 180
3211 	} else {
3212 	  pathOut->lineTo(pathIn->pts[i+1].x + wdy,
3213 			  pathIn->pts[i+1].y - wdx);
3214 	  // miter join inside limit
3215 	  if (state->lineJoin == splashLineJoinMiter &&
3216 	      splashSqrt(miter) <= state->miterLimit) {
3217 	    pathOut->lineTo(pathIn->pts[i+1].x + wdy + wdx * m,
3218 			    pathIn->pts[i+1].y - wdx + wdy * m);
3219 	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3220 			    pathIn->pts[i+1].y - wdxNext);
3221 	  // bevel join or miter join outside limit
3222 	  } else {
3223 	    pathOut->lineTo(pathIn->pts[i+1].x + wdyNext,
3224 			    pathIn->pts[i+1].y - wdxNext);
3225 	  }
3226 	}
3227       }
3228 
3229       pathOut->close();
3230     }
3231 
3232     // add stroke adjustment hints
3233     if (state->strokeAdjust) {
3234       if (i >= subpathStart + 1) {
3235 	if (i >= subpathStart + 2) {
3236 	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3237 	  pathOut->addStrokeAdjustHint(left1, right1, join0, left2);
3238 	} else {
3239 	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, left2);
3240 	}
3241 	pathOut->addStrokeAdjustHint(left1, right1, right2 + 1, right2 + 1);
3242       }
3243       left0 = left1;
3244       left1 = left2;
3245       right0 = right1;
3246       right1 = right2;
3247       join0 = join1;
3248       join1 = join2;
3249       if (i == subpathStart) {
3250 	leftFirst = left2;
3251 	rightFirst = right2;
3252       }
3253       if (last) {
3254 	if (i >= subpathStart + 2) {
3255 	  pathOut->addStrokeAdjustHint(left1, right1, left0 + 1, right0);
3256 	  pathOut->addStrokeAdjustHint(left1, right1,
3257 				       join0, pathOut->length - 1);
3258 	} else {
3259 	  pathOut->addStrokeAdjustHint(left1, right1,
3260 				       firstPt, pathOut->length - 1);
3261 	}
3262 	if (closed) {
3263 	  pathOut->addStrokeAdjustHint(left1, right1, firstPt, leftFirst);
3264 	  pathOut->addStrokeAdjustHint(left1, right1,
3265 				       rightFirst + 1, rightFirst + 1);
3266 	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3267 				       left1 + 1, right1);
3268 	  pathOut->addStrokeAdjustHint(leftFirst, rightFirst,
3269 				       join1, pathOut->length - 1);
3270 	}
3271       }
3272     }
3273   }
3274 
3275   if (pathIn != path) {
3276     delete pathIn;
3277   }
3278 
3279   return pathOut;
3280 }
3281 
dumpPath(SplashPath * path)3282 void Splash::dumpPath(SplashPath *path) {
3283   int i;
3284 
3285   for (i = 0; i < path->length; ++i) {
3286     printf("  %3d: x=%8.2f y=%8.2f%s%s%s%s\n",
3287 	   i, (double)path->pts[i].x, (double)path->pts[i].y,
3288 	   (path->flags[i] & splashPathFirst) ? " first" : "",
3289 	   (path->flags[i] & splashPathLast) ? " last" : "",
3290 	   (path->flags[i] & splashPathClosed) ? " closed" : "",
3291 	   (path->flags[i] & splashPathCurve) ? " curve" : "");
3292   }
3293 }
3294 
dumpXPath(SplashXPath * path)3295 void Splash::dumpXPath(SplashXPath *path) {
3296   int i;
3297 
3298   for (i = 0; i < path->length; ++i) {
3299     printf("  %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s%s%s%s%s\n",
3300 	   i, (double)path->segs[i].x0, (double)path->segs[i].y0,
3301 	   (double)path->segs[i].x1, (double)path->segs[i].y1,
3302 	   (path->segs[i].flags	& splashXPathFirst) ? "F" : " ",
3303 	   (path->segs[i].flags	& splashXPathLast) ? "L" : " ",
3304 	   (path->segs[i].flags	& splashXPathEnd0) ? "0" : " ",
3305 	   (path->segs[i].flags	& splashXPathEnd1) ? "1" : " ",
3306 	   (path->segs[i].flags	& splashXPathHoriz) ? "H" : " ",
3307 	   (path->segs[i].flags	& splashXPathVert) ? "V" : " ",
3308 	   (path->segs[i].flags	& splashXPathFlip) ? "P" : " ");
3309   }
3310 }
3311