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