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