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