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