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