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 yp;
3521
3522 if (debugMode) {
3523 printf(format: "drawImage: srcMode=%d srcAlpha=%d w=%d h=%d mat=[%.2f %.2f %.2f %.2f %.2f %.2f]\n", srcMode, srcAlpha, w, h, (double)mat[0], (double)mat[1], (double)mat[2], (double)mat[3], (double)mat[4], (double)mat[5]);
3524 }
3525
3526 // check color modes
3527 ok = false; // make gcc happy
3528 nComps = 0; // make gcc happy
3529 switch (bitmap->mode) {
3530 case splashModeMono1:
3531 case splashModeMono8:
3532 ok = srcMode == splashModeMono8;
3533 nComps = 1;
3534 break;
3535 case splashModeRGB8:
3536 ok = srcMode == splashModeRGB8;
3537 nComps = 3;
3538 break;
3539 case splashModeXBGR8:
3540 ok = srcMode == splashModeXBGR8;
3541 nComps = 4;
3542 break;
3543 case splashModeBGR8:
3544 ok = srcMode == splashModeBGR8;
3545 nComps = 3;
3546 break;
3547 case splashModeCMYK8:
3548 ok = srcMode == splashModeCMYK8;
3549 nComps = 4;
3550 break;
3551 case splashModeDeviceN8:
3552 ok = srcMode == splashModeDeviceN8;
3553 nComps = SPOT_NCOMPS + 4;
3554 break;
3555 default:
3556 ok = false;
3557 break;
3558 }
3559 if (!ok) {
3560 return splashErrModeMismatch;
3561 }
3562
3563 // check for singular matrix
3564 if (!splashCheckDet(m11: mat[0], m12: mat[1], m21: mat[2], m22: mat[3], epsilon: 0.000001)) {
3565 return splashErrSingularMatrix;
3566 }
3567
3568 minorAxisZero = mat[1] == 0 && mat[2] == 0;
3569
3570 // scaling only
3571 if (mat[0] > 0 && minorAxisZero && mat[3] > 0) {
3572 x0 = imgCoordMungeLower(x: mat[4]);
3573 y0 = imgCoordMungeLower(x: mat[5]);
3574 x1 = imgCoordMungeUpper(x: mat[0] + mat[4]);
3575 y1 = imgCoordMungeUpper(x: mat[3] + mat[5]);
3576 // make sure narrow images cover at least one pixel
3577 if (x0 == x1) {
3578 ++x1;
3579 }
3580 if (y0 == y1) {
3581 ++y1;
3582 }
3583 clipRes = state->clip->testRect(rectXMin: x0, rectYMin: y0, rectXMax: x1 - 1, rectYMax: y1 - 1);
3584 opClipRes = clipRes;
3585 if (clipRes != splashClipAllOutside) {
3586 scaledWidth = x1 - x0;
3587 scaledHeight = y1 - y0;
3588 yp = h / scaledHeight;
3589 if (yp < 0 || yp > INT_MAX - 1) {
3590 return splashErrBadArg;
3591 }
3592 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth: w, srcHeight: h, scaledWidth, scaledHeight, interpolate, tilingPattern);
3593 if (scaledImg == nullptr) {
3594 return splashErrBadArg;
3595 }
3596 if (tf != nullptr) {
3597 (*tf)(srcData, scaledImg);
3598 }
3599 blitImage(src: scaledImg, srcAlpha, xDest: x0, yDest: y0, clipRes);
3600 delete scaledImg;
3601 }
3602
3603 // scaling plus vertical flip
3604 } else if (mat[0] > 0 && minorAxisZero && mat[3] < 0) {
3605 x0 = imgCoordMungeLower(x: mat[4]);
3606 y0 = imgCoordMungeLower(x: mat[3] + mat[5]);
3607 x1 = imgCoordMungeUpper(x: mat[0] + mat[4]);
3608 y1 = imgCoordMungeUpper(x: mat[5]);
3609 if (x0 == x1) {
3610 if (mat[4] + mat[0] * 0.5 < x0) {
3611 --x0;
3612 } else {
3613 ++x1;
3614 }
3615 }
3616 if (y0 == y1) {
3617 if (mat[5] + mat[1] * 0.5 < y0) {
3618 --y0;
3619 } else {
3620 ++y1;
3621 }
3622 }
3623 clipRes = state->clip->testRect(rectXMin: x0, rectYMin: y0, rectXMax: x1 - 1, rectYMax: y1 - 1);
3624 opClipRes = clipRes;
3625 if (clipRes != splashClipAllOutside) {
3626 scaledWidth = x1 - x0;
3627 scaledHeight = y1 - y0;
3628 yp = h / scaledHeight;
3629 if (yp < 0 || yp > INT_MAX - 1) {
3630 return splashErrBadArg;
3631 }
3632 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth: w, srcHeight: h, scaledWidth, scaledHeight, interpolate, tilingPattern);
3633 if (scaledImg == nullptr) {
3634 return splashErrBadArg;
3635 }
3636 if (tf != nullptr) {
3637 (*tf)(srcData, scaledImg);
3638 }
3639 vertFlipImage(img: scaledImg, width: scaledWidth, height: scaledHeight, nComps);
3640 blitImage(src: scaledImg, srcAlpha, xDest: x0, yDest: y0, clipRes);
3641 delete scaledImg;
3642 }
3643
3644 // all other cases
3645 } else {
3646 return arbitraryTransformImage(src, tf, srcData, srcMode, nComps, srcAlpha, srcWidth: w, srcHeight: h, mat, interpolate, tilingPattern);
3647 }
3648
3649 return splashOk;
3650}
3651
3652SplashError Splash::arbitraryTransformImage(SplashImageSource src, SplashICCTransform tf, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, SplashCoord *mat, bool interpolate,
3653 bool tilingPattern)
3654{
3655 SplashBitmap *scaledImg;
3656 SplashClipResult clipRes, clipRes2;
3657 SplashPipe pipe;
3658 SplashColor pixel = {};
3659 int scaledWidth, scaledHeight, t0, t1, th;
3660 SplashCoord r00, r01, r10, r11, det, ir00, ir01, ir10, ir11;
3661 SplashCoord vx[4], vy[4];
3662 int xMin, yMin, xMax, yMax;
3663 ImageSection section[3];
3664 int nSections;
3665 int y, xa, xb, x, i, xx, yy, yp;
3666
3667 // compute the four vertices of the target quadrilateral
3668 vx[0] = mat[4];
3669 vy[0] = mat[5];
3670 vx[1] = mat[2] + mat[4];
3671 vy[1] = mat[3] + mat[5];
3672 vx[2] = mat[0] + mat[2] + mat[4];
3673 vy[2] = mat[1] + mat[3] + mat[5];
3674 vx[3] = mat[0] + mat[4];
3675 vy[3] = mat[1] + mat[5];
3676
3677 // clipping
3678 xMin = imgCoordMungeLower(x: vx[0]);
3679 xMax = imgCoordMungeUpper(x: vx[0]);
3680 yMin = imgCoordMungeLower(x: vy[0]);
3681 yMax = imgCoordMungeUpper(x: vy[0]);
3682 for (i = 1; i < 4; ++i) {
3683 t0 = imgCoordMungeLower(x: vx[i]);
3684 if (t0 < xMin) {
3685 xMin = t0;
3686 }
3687 t0 = imgCoordMungeUpper(x: vx[i]);
3688 if (t0 > xMax) {
3689 xMax = t0;
3690 }
3691 t1 = imgCoordMungeLower(x: vy[i]);
3692 if (t1 < yMin) {
3693 yMin = t1;
3694 }
3695 t1 = imgCoordMungeUpper(x: vy[i]);
3696 if (t1 > yMax) {
3697 yMax = t1;
3698 }
3699 }
3700 clipRes = state->clip->testRect(rectXMin: xMin, rectYMin: yMin, rectXMax: xMax, rectYMax: yMax);
3701 opClipRes = clipRes;
3702 if (clipRes == splashClipAllOutside) {
3703 return splashOk;
3704 }
3705
3706 // compute the scale factors
3707 if (splashAbs(x: mat[0]) >= splashAbs(x: mat[1])) {
3708 scaledWidth = xMax - xMin;
3709 scaledHeight = yMax - yMin;
3710 } else {
3711 scaledWidth = yMax - yMin;
3712 scaledHeight = xMax - xMin;
3713 }
3714 if (scaledHeight <= 1 || scaledWidth <= 1 || tilingPattern) {
3715 if (mat[0] >= 0) {
3716 t0 = imgCoordMungeUpper(x: mat[0] + mat[4]) - imgCoordMungeLower(x: mat[4]);
3717 } else {
3718 t0 = imgCoordMungeUpper(x: mat[4]) - imgCoordMungeLower(x: mat[0] + mat[4]);
3719 }
3720 if (mat[1] >= 0) {
3721 t1 = imgCoordMungeUpper(x: mat[1] + mat[5]) - imgCoordMungeLower(x: mat[5]);
3722 } else {
3723 t1 = imgCoordMungeUpper(x: mat[5]) - imgCoordMungeLower(x: mat[1] + mat[5]);
3724 }
3725 scaledWidth = t0 > t1 ? t0 : t1;
3726 if (mat[2] >= 0) {
3727 t0 = imgCoordMungeUpper(x: mat[2] + mat[4]) - imgCoordMungeLower(x: mat[4]);
3728 if (splashAbs(x: mat[1]) >= 1) {
3729 th = imgCoordMungeUpper(x: mat[2]) - imgCoordMungeLower(x: mat[0] * mat[3] / mat[1]);
3730 if (th > t0) {
3731 t0 = th;
3732 }
3733 }
3734 } else {
3735 t0 = imgCoordMungeUpper(x: mat[4]) - imgCoordMungeLower(x: mat[2] + mat[4]);
3736 if (splashAbs(x: mat[1]) >= 1) {
3737 th = imgCoordMungeUpper(x: mat[0] * mat[3] / mat[1]) - imgCoordMungeLower(x: mat[2]);
3738 if (th > t0) {
3739 t0 = th;
3740 }
3741 }
3742 }
3743 if (mat[3] >= 0) {
3744 t1 = imgCoordMungeUpper(x: mat[3] + mat[5]) - imgCoordMungeLower(x: mat[5]);
3745 if (splashAbs(x: mat[0]) >= 1) {
3746 th = imgCoordMungeUpper(x: mat[3]) - imgCoordMungeLower(x: mat[1] * mat[2] / mat[0]);
3747 if (th > t1) {
3748 t1 = th;
3749 }
3750 }
3751 } else {
3752 t1 = imgCoordMungeUpper(x: mat[5]) - imgCoordMungeLower(x: mat[3] + mat[5]);
3753 if (splashAbs(x: mat[0]) >= 1) {
3754 th = imgCoordMungeUpper(x: mat[1] * mat[2] / mat[0]) - imgCoordMungeLower(x: mat[3]);
3755 if (th > t1) {
3756 t1 = th;
3757 }
3758 }
3759 }
3760 scaledHeight = t0 > t1 ? t0 : t1;
3761 }
3762 if (scaledWidth == 0) {
3763 scaledWidth = 1;
3764 }
3765 if (scaledHeight == 0) {
3766 scaledHeight = 1;
3767 }
3768
3769 // compute the inverse transform (after scaling) matrix
3770 r00 = mat[0] / scaledWidth;
3771 r01 = mat[1] / scaledWidth;
3772 r10 = mat[2] / scaledHeight;
3773 r11 = mat[3] / scaledHeight;
3774 det = r00 * r11 - r01 * r10;
3775 if (splashAbs(x: det) < 1e-6) {
3776 // this should be caught by the singular matrix check in drawImage
3777 return splashErrBadArg;
3778 }
3779 ir00 = r11 / det;
3780 ir01 = -r01 / det;
3781 ir10 = -r10 / det;
3782 ir11 = r00 / det;
3783
3784 // scale the input image
3785 yp = srcHeight / scaledHeight;
3786 if (yp < 0 || yp > INT_MAX - 1) {
3787 return splashErrBadArg;
3788 }
3789 scaledImg = scaleImage(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate);
3790
3791 if (scaledImg == nullptr) {
3792 return splashErrBadArg;
3793 }
3794
3795 if (tf != nullptr) {
3796 (*tf)(srcData, scaledImg);
3797 }
3798 // construct the three sections
3799 i = 0;
3800 if (vy[1] < vy[i]) {
3801 i = 1;
3802 }
3803 if (vy[2] < vy[i]) {
3804 i = 2;
3805 }
3806 if (vy[3] < vy[i]) {
3807 i = 3;
3808 }
3809 // NB: if using fixed point, 0.000001 will be truncated to zero,
3810 // so these two comparisons must be <=, not <
3811 if (splashAbs(x: vy[i] - vy[(i - 1) & 3]) <= 0.000001 && vy[(i - 1) & 3] < vy[(i + 1) & 3]) {
3812 i = (i - 1) & 3;
3813 }
3814 if (splashAbs(x: vy[i] - vy[(i + 1) & 3]) <= 0.000001) {
3815 section[0].y0 = imgCoordMungeLower(x: vy[i]);
3816 section[0].y1 = imgCoordMungeUpper(x: vy[(i + 2) & 3]) - 1;
3817 if (vx[i] < vx[(i + 1) & 3]) {
3818 section[0].ia0 = i;
3819 section[0].ia1 = (i + 3) & 3;
3820 section[0].ib0 = (i + 1) & 3;
3821 section[0].ib1 = (i + 2) & 3;
3822 } else {
3823 section[0].ia0 = (i + 1) & 3;
3824 section[0].ia1 = (i + 2) & 3;
3825 section[0].ib0 = i;
3826 section[0].ib1 = (i + 3) & 3;
3827 }
3828 nSections = 1;
3829 } else {
3830 section[0].y0 = imgCoordMungeLower(x: vy[i]);
3831 section[2].y1 = imgCoordMungeUpper(x: vy[(i + 2) & 3]) - 1;
3832 section[0].ia0 = section[0].ib0 = i;
3833 section[2].ia1 = section[2].ib1 = (i + 2) & 3;
3834 if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3835 section[0].ia1 = section[2].ia0 = (i + 1) & 3;
3836 section[0].ib1 = section[2].ib0 = (i + 3) & 3;
3837 } else {
3838 section[0].ia1 = section[2].ia0 = (i + 3) & 3;
3839 section[0].ib1 = section[2].ib0 = (i + 1) & 3;
3840 }
3841 if (vy[(i + 1) & 3] < vy[(i + 3) & 3]) {
3842 section[1].y0 = imgCoordMungeLower(x: vy[(i + 1) & 3]);
3843 section[2].y0 = imgCoordMungeUpper(x: vy[(i + 3) & 3]);
3844 if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3845 section[1].ia0 = (i + 1) & 3;
3846 section[1].ia1 = (i + 2) & 3;
3847 section[1].ib0 = i;
3848 section[1].ib1 = (i + 3) & 3;
3849 } else {
3850 section[1].ia0 = i;
3851 section[1].ia1 = (i + 3) & 3;
3852 section[1].ib0 = (i + 1) & 3;
3853 section[1].ib1 = (i + 2) & 3;
3854 }
3855 } else {
3856 section[1].y0 = imgCoordMungeLower(x: vy[(i + 3) & 3]);
3857 section[2].y0 = imgCoordMungeUpper(x: vy[(i + 1) & 3]);
3858 if (vx[(i + 1) & 3] < vx[(i + 3) & 3]) {
3859 section[1].ia0 = i;
3860 section[1].ia1 = (i + 1) & 3;
3861 section[1].ib0 = (i + 3) & 3;
3862 section[1].ib1 = (i + 2) & 3;
3863 } else {
3864 section[1].ia0 = (i + 3) & 3;
3865 section[1].ia1 = (i + 2) & 3;
3866 section[1].ib0 = i;
3867 section[1].ib1 = (i + 1) & 3;
3868 }
3869 }
3870 section[0].y1 = section[1].y0 - 1;
3871 section[1].y1 = section[2].y0 - 1;
3872 nSections = 3;
3873 }
3874 for (i = 0; i < nSections; ++i) {
3875 section[i].xa0 = vx[section[i].ia0];
3876 section[i].ya0 = vy[section[i].ia0];
3877 section[i].xa1 = vx[section[i].ia1];
3878 section[i].ya1 = vy[section[i].ia1];
3879 section[i].xb0 = vx[section[i].ib0];
3880 section[i].yb0 = vy[section[i].ib0];
3881 section[i].xb1 = vx[section[i].ib1];
3882 section[i].yb1 = vy[section[i].ib1];
3883 section[i].dxdya = (section[i].xa1 - section[i].xa0) / (section[i].ya1 - section[i].ya0);
3884 section[i].dxdyb = (section[i].xb1 - section[i].xb0) / (section[i].yb1 - section[i].yb0);
3885 }
3886
3887 // initialize the pixel pipe
3888 pipeInit(pipe: &pipe, x: 0, y: 0, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: srcAlpha || (vectorAntialias && clipRes != splashClipAllInside), nonIsolatedGroup: false);
3889 if (vectorAntialias) {
3890 drawAAPixelInit();
3891 }
3892
3893 // make sure narrow images cover at least one pixel
3894 if (nSections == 1) {
3895 if (section[0].y0 == section[0].y1) {
3896 ++section[0].y1;
3897 clipRes = opClipRes = splashClipPartial;
3898 }
3899 } else {
3900 if (section[0].y0 == section[2].y1) {
3901 ++section[1].y1;
3902 clipRes = opClipRes = splashClipPartial;
3903 }
3904 }
3905
3906 // scan all pixels inside the target region
3907 for (i = 0; i < nSections; ++i) {
3908 for (y = section[i].y0; y <= section[i].y1; ++y) {
3909 xa = imgCoordMungeLower(x: section[i].xa0 + ((SplashCoord)y + 0.5 - section[i].ya0) * section[i].dxdya);
3910 if (unlikely(xa < 0)) {
3911 xa = 0;
3912 }
3913 xb = imgCoordMungeUpper(x: section[i].xb0 + ((SplashCoord)y + 0.5 - section[i].yb0) * section[i].dxdyb);
3914 // make sure narrow images cover at least one pixel
3915 if (xa == xb) {
3916 ++xb;
3917 }
3918 if (unlikely(clipRes == splashClipAllInside && xb > bitmap->getWidth())) {
3919 xb = bitmap->getWidth();
3920 }
3921 if (clipRes != splashClipAllInside) {
3922 clipRes2 = state->clip->testSpan(spanXMin: xa, spanXMax: xb - 1, spanY: y);
3923 } else {
3924 clipRes2 = clipRes;
3925 }
3926 for (x = xa; x < xb; ++x) {
3927 // map (x+0.5, y+0.5) back to the scaled image
3928 xx = splashFloor(x: ((SplashCoord)x + 0.5 - mat[4]) * ir00 + ((SplashCoord)y + 0.5 - mat[5]) * ir10);
3929 yy = splashFloor(x: ((SplashCoord)x + 0.5 - mat[4]) * ir01 + ((SplashCoord)y + 0.5 - mat[5]) * ir11);
3930 // xx should always be within bounds, but floating point
3931 // inaccuracy can cause problems
3932 if (xx < 0) {
3933 xx = 0;
3934 } else if (xx >= scaledWidth) {
3935 xx = scaledWidth - 1;
3936 }
3937 if (yy < 0) {
3938 yy = 0;
3939 } else if (yy >= scaledHeight) {
3940 yy = scaledHeight - 1;
3941 }
3942 scaledImg->getPixel(x: xx, y: yy, pixel);
3943 if (srcAlpha) {
3944 pipe.shape = scaledImg->alpha[yy * scaledWidth + xx];
3945 } else {
3946 pipe.shape = 255;
3947 }
3948 if (vectorAntialias && clipRes2 != splashClipAllInside) {
3949 drawAAPixel(pipe: &pipe, x, y);
3950 } else {
3951 drawPixel(pipe: &pipe, x, y, noClip: clipRes2 == splashClipAllInside);
3952 }
3953 }
3954 }
3955 }
3956
3957 delete scaledImg;
3958 return splashOk;
3959}
3960
3961// determine if a scaled image requires interpolation based on the scale and
3962// the interpolate flag from the image dictionary
3963static bool isImageInterpolationRequired(int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate)
3964{
3965 if (interpolate || srcWidth == 0 || srcHeight == 0) {
3966 return true;
3967 }
3968
3969 /* When scale factor is >= 400% we don't interpolate. See bugs #25268, #9860 */
3970 if (scaledWidth / srcWidth >= 4 || scaledHeight / srcHeight >= 4) {
3971 return false;
3972 }
3973
3974 return true;
3975}
3976
3977// Scale an image into a SplashBitmap.
3978SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern)
3979{
3980 SplashBitmap *dest;
3981
3982 dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha, true, bitmap->getSeparationList());
3983 if (dest->getDataPtr() != nullptr && srcHeight > 0 && srcWidth > 0) {
3984 bool success = true;
3985 if (scaledHeight < srcHeight) {
3986 if (scaledWidth < srcWidth) {
3987 success = scaleImageYdownXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3988 } else {
3989 success = scaleImageYdownXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3990 }
3991 } else {
3992 if (scaledWidth < srcWidth) {
3993 success = scaleImageYupXdown(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3994 } else {
3995 if (!tilingPattern && isImageInterpolationRequired(srcWidth, srcHeight, scaledWidth, scaledHeight, interpolate)) {
3996 success = scaleImageYupXupBilinear(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3997 } else {
3998 success = scaleImageYupXup(src, srcData, srcMode, nComps, srcAlpha, srcWidth, srcHeight, scaledWidth, scaledHeight, dest);
3999 }
4000 }
4001 }
4002 if (unlikely(!success)) {
4003 delete dest;
4004 dest = nullptr;
4005 }
4006 } else {
4007 delete dest;
4008 dest = nullptr;
4009 }
4010 return dest;
4011}
4012
4013bool Splash::scaleImageYdownXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4014{
4015 unsigned char *lineBuf, *alphaLineBuf;
4016 unsigned int *pixBuf, *alphaPixBuf;
4017 unsigned int pix0, pix1, pix2;
4018 unsigned int pix3;
4019 unsigned int pix[SPOT_NCOMPS + 4], cp;
4020 unsigned int alpha;
4021 unsigned char *destPtr, *destAlphaPtr;
4022 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4023 int i, j;
4024
4025 // Bresenham parameters for y scale
4026 yp = srcHeight / scaledHeight;
4027 yq = srcHeight % scaledHeight;
4028
4029 // Bresenham parameters for x scale
4030 xp = srcWidth / scaledWidth;
4031 xq = srcWidth % scaledWidth;
4032
4033 // allocate buffers
4034 lineBuf = (unsigned char *)gmallocn_checkoverflow(count: srcWidth, size: nComps);
4035 if (unlikely(!lineBuf)) {
4036 return false;
4037 }
4038 pixBuf = (unsigned int *)gmallocn_checkoverflow(count: srcWidth, size: nComps * sizeof(int));
4039 if (unlikely(!pixBuf)) {
4040 gfree(p: lineBuf);
4041 return false;
4042 }
4043 if (srcAlpha) {
4044 alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(size: srcWidth);
4045 if (unlikely(!alphaLineBuf)) {
4046 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYdownXdown");
4047 gfree(p: lineBuf);
4048 gfree(p: pixBuf);
4049 return false;
4050 }
4051 alphaPixBuf = (unsigned int *)gmallocn_checkoverflow(count: srcWidth, size: sizeof(int));
4052 if (unlikely(!alphaPixBuf)) {
4053 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaPixBuf in Splash::scaleImageYdownXdown");
4054 gfree(p: lineBuf);
4055 gfree(p: pixBuf);
4056 gfree(p: alphaLineBuf);
4057 return false;
4058 }
4059 } else {
4060 alphaLineBuf = nullptr;
4061 alphaPixBuf = nullptr;
4062 }
4063
4064 // init y scale Bresenham
4065 yt = 0;
4066
4067 destPtr = dest->data;
4068 destAlphaPtr = dest->alpha;
4069 for (y = 0; y < scaledHeight; ++y) {
4070
4071 // y scale Bresenham
4072 if ((yt += yq) >= scaledHeight) {
4073 yt -= scaledHeight;
4074 yStep = yp + 1;
4075 } else {
4076 yStep = yp;
4077 }
4078
4079 // read rows from image
4080 memset(s: pixBuf, c: 0, n: srcWidth * nComps * sizeof(int));
4081 if (srcAlpha) {
4082 memset(s: alphaPixBuf, c: 0, n: srcWidth * sizeof(int));
4083 }
4084 for (i = 0; i < yStep; ++i) {
4085 (*src)(srcData, lineBuf, alphaLineBuf);
4086 for (j = 0; j < srcWidth * nComps; ++j) {
4087 pixBuf[j] += lineBuf[j];
4088 }
4089 if (srcAlpha) {
4090 for (j = 0; j < srcWidth; ++j) {
4091 alphaPixBuf[j] += alphaLineBuf[j];
4092 }
4093 }
4094 }
4095
4096 // init x scale Bresenham
4097 xt = 0;
4098 d0 = (1 << 23) / (yStep * xp);
4099 d1 = (1 << 23) / (yStep * (xp + 1));
4100
4101 xx = xxa = 0;
4102 for (x = 0; x < scaledWidth; ++x) {
4103
4104 // x scale Bresenham
4105 if ((xt += xq) >= scaledWidth) {
4106 xt -= scaledWidth;
4107 xStep = xp + 1;
4108 d = d1;
4109 } else {
4110 xStep = xp;
4111 d = d0;
4112 }
4113
4114 switch (srcMode) {
4115
4116 case splashModeMono8:
4117
4118 // compute the final pixel
4119 pix0 = 0;
4120 for (i = 0; i < xStep; ++i) {
4121 pix0 += pixBuf[xx++];
4122 }
4123 // pix / xStep * yStep
4124 pix0 = (pix0 * d) >> 23;
4125
4126 // store the pixel
4127 *destPtr++ = (unsigned char)pix0;
4128 break;
4129
4130 case splashModeRGB8:
4131
4132 // compute the final pixel
4133 pix0 = pix1 = pix2 = 0;
4134 for (i = 0; i < xStep; ++i) {
4135 pix0 += pixBuf[xx];
4136 pix1 += pixBuf[xx + 1];
4137 pix2 += pixBuf[xx + 2];
4138 xx += 3;
4139 }
4140 // pix / xStep * yStep
4141 pix0 = (pix0 * d) >> 23;
4142 pix1 = (pix1 * d) >> 23;
4143 pix2 = (pix2 * d) >> 23;
4144
4145 // store the pixel
4146 *destPtr++ = (unsigned char)pix0;
4147 *destPtr++ = (unsigned char)pix1;
4148 *destPtr++ = (unsigned char)pix2;
4149 break;
4150
4151 case splashModeXBGR8:
4152
4153 // compute the final pixel
4154 pix0 = pix1 = pix2 = 0;
4155 for (i = 0; i < xStep; ++i) {
4156 pix0 += pixBuf[xx];
4157 pix1 += pixBuf[xx + 1];
4158 pix2 += pixBuf[xx + 2];
4159 xx += 4;
4160 }
4161 // pix / xStep * yStep
4162 pix0 = (pix0 * d) >> 23;
4163 pix1 = (pix1 * d) >> 23;
4164 pix2 = (pix2 * d) >> 23;
4165
4166 // store the pixel
4167 *destPtr++ = (unsigned char)pix2;
4168 *destPtr++ = (unsigned char)pix1;
4169 *destPtr++ = (unsigned char)pix0;
4170 *destPtr++ = (unsigned char)255;
4171 break;
4172
4173 case splashModeBGR8:
4174
4175 // compute the final pixel
4176 pix0 = pix1 = pix2 = 0;
4177 for (i = 0; i < xStep; ++i) {
4178 pix0 += pixBuf[xx];
4179 pix1 += pixBuf[xx + 1];
4180 pix2 += pixBuf[xx + 2];
4181 xx += 3;
4182 }
4183 // pix / xStep * yStep
4184 pix0 = (pix0 * d) >> 23;
4185 pix1 = (pix1 * d) >> 23;
4186 pix2 = (pix2 * d) >> 23;
4187
4188 // store the pixel
4189 *destPtr++ = (unsigned char)pix2;
4190 *destPtr++ = (unsigned char)pix1;
4191 *destPtr++ = (unsigned char)pix0;
4192 break;
4193
4194 case splashModeCMYK8:
4195
4196 // compute the final pixel
4197 pix0 = pix1 = pix2 = pix3 = 0;
4198 for (i = 0; i < xStep; ++i) {
4199 pix0 += pixBuf[xx];
4200 pix1 += pixBuf[xx + 1];
4201 pix2 += pixBuf[xx + 2];
4202 pix3 += pixBuf[xx + 3];
4203 xx += 4;
4204 }
4205 // pix / xStep * yStep
4206 pix0 = (pix0 * d) >> 23;
4207 pix1 = (pix1 * d) >> 23;
4208 pix2 = (pix2 * d) >> 23;
4209 pix3 = (pix3 * d) >> 23;
4210
4211 // store the pixel
4212 *destPtr++ = (unsigned char)pix0;
4213 *destPtr++ = (unsigned char)pix1;
4214 *destPtr++ = (unsigned char)pix2;
4215 *destPtr++ = (unsigned char)pix3;
4216 break;
4217 case splashModeDeviceN8:
4218
4219 // compute the final pixel
4220 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
4221 pix[cp] = 0;
4222 }
4223 for (i = 0; i < xStep; ++i) {
4224 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
4225 pix[cp] += pixBuf[xx + cp];
4226 }
4227 xx += (SPOT_NCOMPS + 4);
4228 }
4229 // pix / xStep * yStep
4230 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
4231 pix[cp] = (pix[cp] * d) >> 23;
4232 }
4233
4234 // store the pixel
4235 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
4236 *destPtr++ = (unsigned char)pix[cp];
4237 }
4238 break;
4239
4240 case splashModeMono1: // mono1 is not allowed
4241 default:
4242 break;
4243 }
4244
4245 // process alpha
4246 if (srcAlpha) {
4247 alpha = 0;
4248 for (i = 0; i < xStep; ++i, ++xxa) {
4249 alpha += alphaPixBuf[xxa];
4250 }
4251 // alpha / xStep * yStep
4252 alpha = (alpha * d) >> 23;
4253 *destAlphaPtr++ = (unsigned char)alpha;
4254 }
4255 }
4256 }
4257
4258 gfree(p: alphaPixBuf);
4259 gfree(p: alphaLineBuf);
4260 gfree(p: pixBuf);
4261 gfree(p: lineBuf);
4262
4263 return true;
4264}
4265
4266bool Splash::scaleImageYdownXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4267{
4268 unsigned char *lineBuf, *alphaLineBuf;
4269 unsigned int *pixBuf, *alphaPixBuf;
4270 unsigned int pix[splashMaxColorComps];
4271 unsigned int alpha;
4272 unsigned char *destPtr, *destAlphaPtr;
4273 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, d;
4274 int i, j;
4275
4276 // Bresenham parameters for y scale
4277 yp = srcHeight / scaledHeight;
4278 yq = srcHeight % scaledHeight;
4279
4280 // Bresenham parameters for x scale
4281 xp = scaledWidth / srcWidth;
4282 xq = scaledWidth % srcWidth;
4283
4284 // allocate buffers
4285 pixBuf = (unsigned int *)gmallocn_checkoverflow(count: srcWidth, size: nComps * sizeof(int));
4286 if (unlikely(!pixBuf)) {
4287 error(category: errInternal, pos: -1, msg: "Splash::scaleImageYdownXup. Couldn't allocate pixBuf memory");
4288 return false;
4289 }
4290 lineBuf = (unsigned char *)gmallocn_checkoverflow(count: srcWidth, size: nComps);
4291 if (unlikely(!lineBuf)) {
4292 error(category: errInternal, pos: -1, msg: "Splash::scaleImageYdownXup. Couldn't allocate lineBuf memory");
4293 gfree(p: pixBuf);
4294 return false;
4295 }
4296 if (srcAlpha) {
4297 alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(size: srcWidth);
4298 if (unlikely(!alphaLineBuf)) {
4299 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYdownXup");
4300 gfree(p: lineBuf);
4301 gfree(p: pixBuf);
4302 return false;
4303 }
4304 alphaPixBuf = (unsigned int *)gmallocn_checkoverflow(count: srcWidth, size: sizeof(int));
4305 if (unlikely(!alphaPixBuf)) {
4306 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaPixBuf in Splash::scaleImageYdownXup");
4307 gfree(p: lineBuf);
4308 gfree(p: pixBuf);
4309 gfree(p: alphaLineBuf);
4310 return false;
4311 }
4312 } else {
4313 alphaLineBuf = nullptr;
4314 alphaPixBuf = nullptr;
4315 }
4316
4317 // init y scale Bresenham
4318 yt = 0;
4319
4320 destPtr = dest->data;
4321 destAlphaPtr = dest->alpha;
4322 for (y = 0; y < scaledHeight; ++y) {
4323
4324 // y scale Bresenham
4325 if ((yt += yq) >= scaledHeight) {
4326 yt -= scaledHeight;
4327 yStep = yp + 1;
4328 } else {
4329 yStep = yp;
4330 }
4331
4332 // read rows from image
4333 memset(s: pixBuf, c: 0, n: srcWidth * nComps * sizeof(int));
4334 if (srcAlpha) {
4335 memset(s: alphaPixBuf, c: 0, n: srcWidth * sizeof(int));
4336 }
4337 for (i = 0; i < yStep; ++i) {
4338 (*src)(srcData, lineBuf, alphaLineBuf);
4339 for (j = 0; j < srcWidth * nComps; ++j) {
4340 pixBuf[j] += lineBuf[j];
4341 }
4342 if (srcAlpha) {
4343 for (j = 0; j < srcWidth; ++j) {
4344 alphaPixBuf[j] += alphaLineBuf[j];
4345 }
4346 }
4347 }
4348
4349 // init x scale Bresenham
4350 xt = 0;
4351 d = (1 << 23) / yStep;
4352
4353 for (x = 0; x < srcWidth; ++x) {
4354
4355 // x scale Bresenham
4356 if ((xt += xq) >= srcWidth) {
4357 xt -= srcWidth;
4358 xStep = xp + 1;
4359 } else {
4360 xStep = xp;
4361 }
4362
4363 // compute the final pixel
4364 for (i = 0; i < nComps; ++i) {
4365 // pixBuf[] / yStep
4366 pix[i] = (pixBuf[x * nComps + i] * d) >> 23;
4367 }
4368
4369 // store the pixel
4370 switch (srcMode) {
4371 case splashModeMono1: // mono1 is not allowed
4372 break;
4373 case splashModeMono8:
4374 for (i = 0; i < xStep; ++i) {
4375 *destPtr++ = (unsigned char)pix[0];
4376 }
4377 break;
4378 case splashModeRGB8:
4379 for (i = 0; i < xStep; ++i) {
4380 *destPtr++ = (unsigned char)pix[0];
4381 *destPtr++ = (unsigned char)pix[1];
4382 *destPtr++ = (unsigned char)pix[2];
4383 }
4384 break;
4385 case splashModeXBGR8:
4386 for (i = 0; i < xStep; ++i) {
4387 *destPtr++ = (unsigned char)pix[2];
4388 *destPtr++ = (unsigned char)pix[1];
4389 *destPtr++ = (unsigned char)pix[0];
4390 *destPtr++ = (unsigned char)255;
4391 }
4392 break;
4393 case splashModeBGR8:
4394 for (i = 0; i < xStep; ++i) {
4395 *destPtr++ = (unsigned char)pix[2];
4396 *destPtr++ = (unsigned char)pix[1];
4397 *destPtr++ = (unsigned char)pix[0];
4398 }
4399 break;
4400 case splashModeCMYK8:
4401 for (i = 0; i < xStep; ++i) {
4402 *destPtr++ = (unsigned char)pix[0];
4403 *destPtr++ = (unsigned char)pix[1];
4404 *destPtr++ = (unsigned char)pix[2];
4405 *destPtr++ = (unsigned char)pix[3];
4406 }
4407 break;
4408 case splashModeDeviceN8:
4409 for (i = 0; i < xStep; ++i) {
4410 for (unsigned int cp : pix) {
4411 *destPtr++ = (unsigned char)cp;
4412 }
4413 }
4414 break;
4415 }
4416
4417 // process alpha
4418 if (srcAlpha) {
4419 // alphaPixBuf[] / yStep
4420 alpha = (alphaPixBuf[x] * d) >> 23;
4421 for (i = 0; i < xStep; ++i) {
4422 *destAlphaPtr++ = (unsigned char)alpha;
4423 }
4424 }
4425 }
4426 }
4427
4428 gfree(p: alphaPixBuf);
4429 gfree(p: alphaLineBuf);
4430 gfree(p: pixBuf);
4431 gfree(p: lineBuf);
4432
4433 return true;
4434}
4435
4436bool Splash::scaleImageYupXdown(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4437{
4438 unsigned char *lineBuf, *alphaLineBuf;
4439 unsigned int pix[splashMaxColorComps];
4440 unsigned int alpha;
4441 unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4442 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx, xxa, d, d0, d1;
4443 int i, j;
4444
4445 // Bresenham parameters for y scale
4446 yp = scaledHeight / srcHeight;
4447 yq = scaledHeight % srcHeight;
4448
4449 // Bresenham parameters for x scale
4450 xp = srcWidth / scaledWidth;
4451 xq = srcWidth % scaledWidth;
4452
4453 // allocate buffers
4454 lineBuf = (unsigned char *)gmallocn_checkoverflow(count: srcWidth, size: nComps);
4455 if (unlikely(!lineBuf)) {
4456 gfree(p: dest->takeData());
4457 return false;
4458 }
4459 if (srcAlpha) {
4460 alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(size: srcWidth);
4461 if (unlikely(!alphaLineBuf)) {
4462 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYupXdown");
4463 gfree(p: lineBuf);
4464 return false;
4465 }
4466 } else {
4467 alphaLineBuf = nullptr;
4468 }
4469
4470 // init y scale Bresenham
4471 yt = 0;
4472
4473 destPtr0 = dest->data;
4474 destAlphaPtr0 = dest->alpha;
4475 for (y = 0; y < srcHeight; ++y) {
4476
4477 // y scale Bresenham
4478 if ((yt += yq) >= srcHeight) {
4479 yt -= srcHeight;
4480 yStep = yp + 1;
4481 } else {
4482 yStep = yp;
4483 }
4484
4485 // read row from image
4486 (*src)(srcData, lineBuf, alphaLineBuf);
4487
4488 // init x scale Bresenham
4489 xt = 0;
4490 d0 = (1 << 23) / xp;
4491 d1 = (1 << 23) / (xp + 1);
4492
4493 xx = xxa = 0;
4494 for (x = 0; x < scaledWidth; ++x) {
4495
4496 // x scale Bresenham
4497 if ((xt += xq) >= scaledWidth) {
4498 xt -= scaledWidth;
4499 xStep = xp + 1;
4500 d = d1;
4501 } else {
4502 xStep = xp;
4503 d = d0;
4504 }
4505
4506 // compute the final pixel
4507 for (i = 0; i < nComps; ++i) {
4508 pix[i] = 0;
4509 }
4510 for (i = 0; i < xStep; ++i) {
4511 for (j = 0; j < nComps; ++j, ++xx) {
4512 pix[j] += lineBuf[xx];
4513 }
4514 }
4515 for (i = 0; i < nComps; ++i) {
4516 // pix[] / xStep
4517 pix[i] = (pix[i] * d) >> 23;
4518 }
4519
4520 // store the pixel
4521 switch (srcMode) {
4522 case splashModeMono1: // mono1 is not allowed
4523 break;
4524 case splashModeMono8:
4525 for (i = 0; i < yStep; ++i) {
4526 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4527 *destPtr++ = (unsigned char)pix[0];
4528 }
4529 break;
4530 case splashModeRGB8:
4531 for (i = 0; i < yStep; ++i) {
4532 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4533 *destPtr++ = (unsigned char)pix[0];
4534 *destPtr++ = (unsigned char)pix[1];
4535 *destPtr++ = (unsigned char)pix[2];
4536 }
4537 break;
4538 case splashModeXBGR8:
4539 for (i = 0; i < yStep; ++i) {
4540 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4541 *destPtr++ = (unsigned char)pix[2];
4542 *destPtr++ = (unsigned char)pix[1];
4543 *destPtr++ = (unsigned char)pix[0];
4544 *destPtr++ = (unsigned char)255;
4545 }
4546 break;
4547 case splashModeBGR8:
4548 for (i = 0; i < yStep; ++i) {
4549 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4550 *destPtr++ = (unsigned char)pix[2];
4551 *destPtr++ = (unsigned char)pix[1];
4552 *destPtr++ = (unsigned char)pix[0];
4553 }
4554 break;
4555 case splashModeCMYK8:
4556 for (i = 0; i < yStep; ++i) {
4557 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4558 *destPtr++ = (unsigned char)pix[0];
4559 *destPtr++ = (unsigned char)pix[1];
4560 *destPtr++ = (unsigned char)pix[2];
4561 *destPtr++ = (unsigned char)pix[3];
4562 }
4563 break;
4564 case splashModeDeviceN8:
4565 for (i = 0; i < yStep; ++i) {
4566 destPtr = destPtr0 + (i * scaledWidth + x) * nComps;
4567 for (unsigned int cp : pix) {
4568 *destPtr++ = (unsigned char)cp;
4569 }
4570 }
4571 break;
4572 }
4573
4574 // process alpha
4575 if (srcAlpha) {
4576 alpha = 0;
4577 for (i = 0; i < xStep; ++i, ++xxa) {
4578 alpha += alphaLineBuf[xxa];
4579 }
4580 // alpha / xStep
4581 alpha = (alpha * d) >> 23;
4582 for (i = 0; i < yStep; ++i) {
4583 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x;
4584 *destAlphaPtr = (unsigned char)alpha;
4585 }
4586 }
4587 }
4588
4589 destPtr0 += yStep * scaledWidth * nComps;
4590 if (srcAlpha) {
4591 destAlphaPtr0 += yStep * scaledWidth;
4592 }
4593 }
4594
4595 gfree(p: alphaLineBuf);
4596 gfree(p: lineBuf);
4597
4598 return true;
4599}
4600
4601bool Splash::scaleImageYupXup(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4602{
4603 unsigned char *lineBuf, *alphaLineBuf;
4604 unsigned int pix[splashMaxColorComps];
4605 unsigned int alpha;
4606 unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4607 int yp, yq, xp, xq, yt, y, yStep, xt, x, xStep, xx;
4608 int i, j;
4609
4610 // Bresenham parameters for y scale
4611 yp = scaledHeight / srcHeight;
4612 yq = scaledHeight % srcHeight;
4613
4614 // Bresenham parameters for x scale
4615 xp = scaledWidth / srcWidth;
4616 xq = scaledWidth % srcWidth;
4617
4618 // allocate buffers
4619 lineBuf = (unsigned char *)gmallocn(count: srcWidth, size: nComps);
4620 if (unlikely(!lineBuf)) {
4621 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for lineBuf in Splash::scaleImageYupXup");
4622 return false;
4623 }
4624
4625 if (srcAlpha) {
4626 alphaLineBuf = (unsigned char *)gmalloc_checkoverflow(size: srcWidth);
4627 if (unlikely(!alphaLineBuf)) {
4628 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf in Splash::scaleImageYupXup");
4629 gfree(p: lineBuf);
4630 return false;
4631 }
4632 } else {
4633 alphaLineBuf = nullptr;
4634 }
4635
4636 // init y scale Bresenham
4637 yt = 0;
4638
4639 destPtr0 = dest->data;
4640 destAlphaPtr0 = dest->alpha;
4641 for (y = 0; y < srcHeight; ++y) {
4642
4643 // y scale Bresenham
4644 if ((yt += yq) >= srcHeight) {
4645 yt -= srcHeight;
4646 yStep = yp + 1;
4647 } else {
4648 yStep = yp;
4649 }
4650
4651 // read row from image
4652 (*src)(srcData, lineBuf, alphaLineBuf);
4653
4654 // init x scale Bresenham
4655 xt = 0;
4656
4657 xx = 0;
4658 for (x = 0; x < srcWidth; ++x) {
4659
4660 // x scale Bresenham
4661 if ((xt += xq) >= srcWidth) {
4662 xt -= srcWidth;
4663 xStep = xp + 1;
4664 } else {
4665 xStep = xp;
4666 }
4667
4668 // compute the final pixel
4669 for (i = 0; i < nComps; ++i) {
4670 pix[i] = lineBuf[x * nComps + i];
4671 }
4672
4673 // store the pixel
4674 switch (srcMode) {
4675 case splashModeMono1: // mono1 is not allowed
4676 break;
4677 case splashModeMono8:
4678 for (i = 0; i < yStep; ++i) {
4679 for (j = 0; j < xStep; ++j) {
4680 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4681 *destPtr++ = (unsigned char)pix[0];
4682 }
4683 }
4684 break;
4685 case splashModeRGB8:
4686 for (i = 0; i < yStep; ++i) {
4687 for (j = 0; j < xStep; ++j) {
4688 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4689 *destPtr++ = (unsigned char)pix[0];
4690 *destPtr++ = (unsigned char)pix[1];
4691 *destPtr++ = (unsigned char)pix[2];
4692 }
4693 }
4694 break;
4695 case splashModeXBGR8:
4696 for (i = 0; i < yStep; ++i) {
4697 for (j = 0; j < xStep; ++j) {
4698 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4699 *destPtr++ = (unsigned char)pix[2];
4700 *destPtr++ = (unsigned char)pix[1];
4701 *destPtr++ = (unsigned char)pix[0];
4702 *destPtr++ = (unsigned char)255;
4703 }
4704 }
4705 break;
4706 case splashModeBGR8:
4707 for (i = 0; i < yStep; ++i) {
4708 for (j = 0; j < xStep; ++j) {
4709 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4710 *destPtr++ = (unsigned char)pix[2];
4711 *destPtr++ = (unsigned char)pix[1];
4712 *destPtr++ = (unsigned char)pix[0];
4713 }
4714 }
4715 break;
4716 case splashModeCMYK8:
4717 for (i = 0; i < yStep; ++i) {
4718 for (j = 0; j < xStep; ++j) {
4719 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4720 *destPtr++ = (unsigned char)pix[0];
4721 *destPtr++ = (unsigned char)pix[1];
4722 *destPtr++ = (unsigned char)pix[2];
4723 *destPtr++ = (unsigned char)pix[3];
4724 }
4725 }
4726 break;
4727 case splashModeDeviceN8:
4728 for (i = 0; i < yStep; ++i) {
4729 for (j = 0; j < xStep; ++j) {
4730 destPtr = destPtr0 + (i * scaledWidth + xx + j) * nComps;
4731 for (unsigned int cp : pix) {
4732 *destPtr++ = (unsigned char)cp;
4733 }
4734 }
4735 }
4736 break;
4737 }
4738
4739 // process alpha
4740 if (srcAlpha) {
4741 alpha = alphaLineBuf[x];
4742 for (i = 0; i < yStep; ++i) {
4743 for (j = 0; j < xStep; ++j) {
4744 destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j;
4745 *destAlphaPtr = (unsigned char)alpha;
4746 }
4747 }
4748 }
4749
4750 xx += xStep;
4751 }
4752
4753 destPtr0 += yStep * scaledWidth * nComps;
4754 if (srcAlpha) {
4755 destAlphaPtr0 += yStep * scaledWidth;
4756 }
4757 }
4758
4759 gfree(p: alphaLineBuf);
4760 gfree(p: lineBuf);
4761
4762 return true;
4763}
4764
4765// expand source row to scaledWidth using linear interpolation
4766static void expandRow(unsigned char *srcBuf, unsigned char *dstBuf, int srcWidth, int scaledWidth, int nComps)
4767{
4768 double xStep = (double)srcWidth / scaledWidth;
4769 double xSrc = 0.0;
4770 double xFrac, xInt;
4771 int p;
4772
4773 // pad the source with an extra pixel equal to the last pixel
4774 // so that when xStep is inside the last pixel we still have two
4775 // pixels to interpolate between.
4776 for (int i = 0; i < nComps; i++) {
4777 srcBuf[srcWidth * nComps + i] = srcBuf[(srcWidth - 1) * nComps + i];
4778 }
4779
4780 for (int x = 0; x < scaledWidth; x++) {
4781 xFrac = modf(x: xSrc, iptr: &xInt);
4782 p = (int)xInt;
4783 for (int c = 0; c < nComps; c++) {
4784 dstBuf[nComps * x + c] = static_cast<unsigned char>(srcBuf[nComps * p + c] * (1.0 - xFrac) + srcBuf[nComps * (p + 1) + c] * xFrac);
4785 }
4786 xSrc += xStep;
4787 }
4788}
4789
4790// Scale up image using bilinear interpolation
4791bool Splash::scaleImageYupXupBilinear(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest)
4792{
4793 unsigned char *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2;
4794 unsigned int pix[splashMaxColorComps];
4795 unsigned char *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr;
4796 int i;
4797
4798 if (srcWidth < 1 || srcHeight < 1) {
4799 return false;
4800 }
4801
4802 // allocate buffers
4803 srcBuf = (unsigned char *)gmallocn_checkoverflow(count: srcWidth + 1, size: nComps); // + 1 pixel of padding
4804 if (unlikely(!srcBuf)) {
4805 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for srcBuf in Splash::scaleImageYupXupBilinear");
4806 return false;
4807 }
4808
4809 lineBuf1 = (unsigned char *)gmallocn_checkoverflow(count: scaledWidth, size: nComps);
4810 if (unlikely(!lineBuf1)) {
4811 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for lineBuf1 in Splash::scaleImageYupXupBilinear");
4812 gfree(p: srcBuf);
4813 return false;
4814 }
4815
4816 lineBuf2 = (unsigned char *)gmallocn_checkoverflow(count: scaledWidth, size: nComps);
4817 if (unlikely(!lineBuf2)) {
4818 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for lineBuf2 in Splash::scaleImageYupXupBilinear");
4819 gfree(p: srcBuf);
4820 gfree(p: lineBuf1);
4821 return false;
4822 }
4823
4824 if (srcAlpha) {
4825 alphaSrcBuf = (unsigned char *)gmalloc_checkoverflow(size: srcWidth + 1); // + 1 pixel of padding
4826 if (unlikely(!alphaSrcBuf)) {
4827 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaSrcBuf in Splash::scaleImageYupXupBilinear");
4828 gfree(p: srcBuf);
4829 gfree(p: lineBuf1);
4830 gfree(p: lineBuf2);
4831 return false;
4832 }
4833
4834 alphaLineBuf1 = (unsigned char *)gmalloc_checkoverflow(size: scaledWidth);
4835 if (unlikely(!alphaLineBuf1)) {
4836 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf1 in Splash::scaleImageYupXupBilinear");
4837 gfree(p: srcBuf);
4838 gfree(p: lineBuf1);
4839 gfree(p: lineBuf2);
4840 gfree(p: alphaSrcBuf);
4841 return false;
4842 }
4843
4844 alphaLineBuf2 = (unsigned char *)gmalloc_checkoverflow(size: scaledWidth);
4845 if (unlikely(!alphaLineBuf2)) {
4846 error(category: errInternal, pos: -1, msg: "Couldn't allocate memory for alphaLineBuf2 in Splash::scaleImageYupXupBilinear");
4847 gfree(p: srcBuf);
4848 gfree(p: lineBuf1);
4849 gfree(p: lineBuf2);
4850 gfree(p: alphaSrcBuf);
4851 gfree(p: alphaLineBuf1);
4852 return false;
4853 }
4854 } else {
4855 alphaSrcBuf = nullptr;
4856 alphaLineBuf1 = nullptr;
4857 alphaLineBuf2 = nullptr;
4858 }
4859
4860 double ySrc = 0.0;
4861 double yStep = (double)srcHeight / scaledHeight;
4862 double yFrac, yInt;
4863 int currentSrcRow = -1;
4864 (*src)(srcData, srcBuf, alphaSrcBuf);
4865 expandRow(srcBuf, dstBuf: lineBuf2, srcWidth, scaledWidth, nComps);
4866 if (srcAlpha) {
4867 expandRow(srcBuf: alphaSrcBuf, dstBuf: alphaLineBuf2, srcWidth, scaledWidth, nComps: 1);
4868 }
4869
4870 destPtr0 = dest->data;
4871 destAlphaPtr0 = dest->alpha;
4872 for (int y = 0; y < scaledHeight; y++) {
4873 yFrac = modf(x: ySrc, iptr: &yInt);
4874 if ((int)yInt > currentSrcRow) {
4875 currentSrcRow++;
4876 // Copy line2 data to line1 and get next line2 data.
4877 // If line2 already contains the last source row we don't touch it.
4878 // This effectively adds an extra row of padding for interpolating the
4879 // last source row with.
4880 memcpy(dest: lineBuf1, src: lineBuf2, n: scaledWidth * nComps);
4881 if (srcAlpha) {
4882 memcpy(dest: alphaLineBuf1, src: alphaLineBuf2, n: scaledWidth);
4883 }
4884 if (currentSrcRow < srcHeight - 1) {
4885 (*src)(srcData, srcBuf, alphaSrcBuf);
4886 expandRow(srcBuf, dstBuf: lineBuf2, srcWidth, scaledWidth, nComps);
4887 if (srcAlpha) {
4888 expandRow(srcBuf: alphaSrcBuf, dstBuf: alphaLineBuf2, srcWidth, scaledWidth, nComps: 1);
4889 }
4890 }
4891 }
4892
4893 // write row y using linear interpolation on lineBuf1 and lineBuf2
4894 for (int x = 0; x < scaledWidth; ++x) {
4895 // compute the final pixel
4896 for (i = 0; i < nComps; ++i) {
4897 pix[i] = static_cast<unsigned char>(lineBuf1[x * nComps + i] * (1.0 - yFrac) + lineBuf2[x * nComps + i] * yFrac);
4898 }
4899
4900 // store the pixel
4901 destPtr = destPtr0 + (y * scaledWidth + x) * nComps;
4902 switch (srcMode) {
4903 case splashModeMono1: // mono1 is not allowed
4904 break;
4905 case splashModeMono8:
4906 *destPtr++ = (unsigned char)pix[0];
4907 break;
4908 case splashModeRGB8:
4909 *destPtr++ = (unsigned char)pix[0];
4910 *destPtr++ = (unsigned char)pix[1];
4911 *destPtr++ = (unsigned char)pix[2];
4912 break;
4913 case splashModeXBGR8:
4914 *destPtr++ = (unsigned char)pix[2];
4915 *destPtr++ = (unsigned char)pix[1];
4916 *destPtr++ = (unsigned char)pix[0];
4917 *destPtr++ = (unsigned char)255;
4918 break;
4919 case splashModeBGR8:
4920 *destPtr++ = (unsigned char)pix[2];
4921 *destPtr++ = (unsigned char)pix[1];
4922 *destPtr++ = (unsigned char)pix[0];
4923 break;
4924 case splashModeCMYK8:
4925 *destPtr++ = (unsigned char)pix[0];
4926 *destPtr++ = (unsigned char)pix[1];
4927 *destPtr++ = (unsigned char)pix[2];
4928 *destPtr++ = (unsigned char)pix[3];
4929 break;
4930 case splashModeDeviceN8:
4931 for (unsigned int cp : pix) {
4932 *destPtr++ = (unsigned char)cp;
4933 }
4934 break;
4935 }
4936
4937 // process alpha
4938 if (srcAlpha) {
4939 destAlphaPtr = destAlphaPtr0 + y * scaledWidth + x;
4940 *destAlphaPtr = static_cast<unsigned char>(alphaLineBuf1[x] * (1.0 - yFrac) + alphaLineBuf2[x] * yFrac);
4941 }
4942 }
4943
4944 ySrc += yStep;
4945 }
4946
4947 gfree(p: alphaSrcBuf);
4948 gfree(p: alphaLineBuf1);
4949 gfree(p: alphaLineBuf2);
4950 gfree(p: srcBuf);
4951 gfree(p: lineBuf1);
4952 gfree(p: lineBuf2);
4953
4954 return true;
4955}
4956
4957void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps)
4958{
4959 unsigned char *lineBuf;
4960 unsigned char *p0, *p1;
4961 int w;
4962
4963 if (unlikely(img->data == nullptr)) {
4964 error(category: errInternal, pos: -1, msg: "img->data is NULL in Splash::vertFlipImage");
4965 return;
4966 }
4967
4968 w = width * nComps;
4969 lineBuf = (unsigned char *)gmalloc(size: w);
4970 for (p0 = img->data, p1 = img->data + (height - 1) * w; p0 < p1; p0 += w, p1 -= w) {
4971 memcpy(dest: lineBuf, src: p0, n: w);
4972 memcpy(dest: p0, src: p1, n: w);
4973 memcpy(dest: p1, src: lineBuf, n: w);
4974 }
4975 if (img->alpha) {
4976 for (p0 = img->alpha, p1 = img->alpha + (height - 1) * width; p0 < p1; p0 += width, p1 -= width) {
4977 memcpy(dest: lineBuf, src: p0, n: width);
4978 memcpy(dest: p0, src: p1, n: width);
4979 memcpy(dest: p1, src: lineBuf, n: width);
4980 }
4981 }
4982 gfree(p: lineBuf);
4983}
4984
4985void Splash::blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest)
4986{
4987 SplashClipResult clipRes = state->clip->testRect(rectXMin: xDest, rectYMin: yDest, rectXMax: xDest + src->getWidth() - 1, rectYMax: yDest + src->getHeight() - 1);
4988 if (clipRes != splashClipAllOutside) {
4989 blitImage(src, srcAlpha, xDest, yDest, clipRes);
4990 }
4991}
4992
4993void Splash::blitImage(SplashBitmap *src, bool srcAlpha, int xDest, int yDest, SplashClipResult clipRes)
4994{
4995 SplashPipe pipe;
4996 SplashColor pixel = {};
4997 unsigned char *ap;
4998 int w, h, x0, y0, x1, y1, x, y;
4999
5000 // split the image into clipped and unclipped regions
5001 w = src->getWidth();
5002 h = src->getHeight();
5003 if (clipRes == splashClipAllInside) {
5004 x0 = 0;
5005 y0 = 0;
5006 x1 = w;
5007 y1 = h;
5008 } else {
5009 if (state->clip->getNumPaths()) {
5010 x0 = x1 = w;
5011 y0 = y1 = h;
5012 } else {
5013 if ((x0 = splashCeil(x: state->clip->getXMin()) - xDest) < 0) {
5014 x0 = 0;
5015 }
5016 if ((y0 = splashCeil(x: state->clip->getYMin()) - yDest) < 0) {
5017 y0 = 0;
5018 }
5019 if ((x1 = splashFloor(x: state->clip->getXMax()) - xDest) > w) {
5020 x1 = w;
5021 }
5022 if (x1 < x0) {
5023 x1 = x0;
5024 }
5025 if ((y1 = splashFloor(x: state->clip->getYMax()) - yDest) > h) {
5026 y1 = h;
5027 }
5028 if (y1 < y0) {
5029 y1 = y0;
5030 }
5031 }
5032 }
5033
5034 // draw the unclipped region
5035 if (x0 < w && y0 < h && x0 < x1 && y0 < y1) {
5036 pipeInit(pipe: &pipe, x: xDest + x0, y: yDest + y0, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: srcAlpha, nonIsolatedGroup: false);
5037 if (srcAlpha) {
5038 for (y = y0; y < y1; ++y) {
5039 pipeSetXY(pipe: &pipe, x: xDest + x0, y: yDest + y);
5040 ap = src->getAlphaPtr() + y * w + x0;
5041 for (x = x0; x < x1; ++x) {
5042 src->getPixel(x, y, pixel);
5043 pipe.shape = *ap++;
5044 (this->*pipe.run)(&pipe);
5045 }
5046 }
5047 } else {
5048 for (y = y0; y < y1; ++y) {
5049 pipeSetXY(pipe: &pipe, x: xDest + x0, y: yDest + y);
5050 for (x = x0; x < x1; ++x) {
5051 src->getPixel(x, y, pixel);
5052 (this->*pipe.run)(&pipe);
5053 }
5054 }
5055 }
5056 }
5057
5058 // draw the clipped regions
5059 if (y0 > 0) {
5060 blitImageClipped(src, srcAlpha, xSrc: 0, ySrc: 0, xDest, yDest, w, h: y0);
5061 }
5062 if (y1 < h) {
5063 blitImageClipped(src, srcAlpha, xSrc: 0, ySrc: y1, xDest, yDest: yDest + y1, w, h: h - y1);
5064 }
5065 if (x0 > 0 && y0 < y1) {
5066 blitImageClipped(src, srcAlpha, xSrc: 0, ySrc: y0, xDest, yDest: yDest + y0, w: x0, h: y1 - y0);
5067 }
5068 if (x1 < w && y0 < y1) {
5069 blitImageClipped(src, srcAlpha, xSrc: x1, ySrc: y0, xDest: xDest + x1, yDest: yDest + y0, w: w - x1, h: y1 - y0);
5070 }
5071}
5072
5073void Splash::blitImageClipped(SplashBitmap *src, bool srcAlpha, int xSrc, int ySrc, int xDest, int yDest, int w, int h)
5074{
5075 SplashPipe pipe;
5076 SplashColor pixel = {};
5077 unsigned char *ap;
5078 int x, y;
5079
5080 if (vectorAntialias) {
5081 pipeInit(pipe: &pipe, x: xDest, y: yDest, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: true, nonIsolatedGroup: false);
5082 drawAAPixelInit();
5083 if (srcAlpha) {
5084 for (y = 0; y < h; ++y) {
5085 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5086 for (x = 0; x < w; ++x) {
5087 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5088 pipe.shape = *ap++;
5089 drawAAPixel(pipe: &pipe, x: xDest + x, y: yDest + y);
5090 }
5091 }
5092 } else {
5093 for (y = 0; y < h; ++y) {
5094 for (x = 0; x < w; ++x) {
5095 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5096 pipe.shape = 255;
5097 drawAAPixel(pipe: &pipe, x: xDest + x, y: yDest + y);
5098 }
5099 }
5100 }
5101 } else {
5102 pipeInit(pipe: &pipe, x: xDest, y: yDest, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: srcAlpha, nonIsolatedGroup: false);
5103 if (srcAlpha) {
5104 for (y = 0; y < h; ++y) {
5105 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5106 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5107 for (x = 0; x < w; ++x) {
5108 if (state->clip->test(x: xDest + x, y: yDest + y)) {
5109 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5110 pipe.shape = *ap++;
5111 (this->*pipe.run)(&pipe);
5112 } else {
5113 pipeIncX(pipe: &pipe);
5114 ++ap;
5115 }
5116 }
5117 }
5118 } else {
5119 for (y = 0; y < h; ++y) {
5120 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5121 for (x = 0; x < w; ++x) {
5122 if (state->clip->test(x: xDest + x, y: yDest + y)) {
5123 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5124 (this->*pipe.run)(&pipe);
5125 } else {
5126 pipeIncX(pipe: &pipe);
5127 }
5128 }
5129 }
5130 }
5131 }
5132}
5133
5134SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, bool noClip, bool nonIsolated, bool knockout, SplashCoord knockoutOpacity)
5135{
5136 SplashPipe pipe;
5137 SplashColor pixel;
5138 unsigned char alpha;
5139 unsigned char *ap;
5140 int x, y;
5141
5142 if (src->mode != bitmap->mode) {
5143 return splashErrModeMismatch;
5144 }
5145
5146 if (unlikely(!bitmap->data)) {
5147 return splashErrZeroImage;
5148 }
5149
5150 if (src->getSeparationList()->size() > bitmap->getSeparationList()->size()) {
5151 for (x = bitmap->getSeparationList()->size(); x < (int)src->getSeparationList()->size(); x++) {
5152 bitmap->getSeparationList()->push_back(x: (GfxSeparationColorSpace *)((*src->getSeparationList())[x])->copy());
5153 }
5154 }
5155 if (src->alpha) {
5156 pipeInit(pipe: &pipe, x: xDest, y: yDest, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: true, nonIsolatedGroup: nonIsolated, knockout, knockoutOpacity: (unsigned char)splashRound(x: knockoutOpacity * 255));
5157 if (noClip) {
5158 for (y = 0; y < h; ++y) {
5159 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5160 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5161 for (x = 0; x < w; ++x) {
5162 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5163 alpha = *ap++;
5164 // this uses shape instead of alpha, which isn't technically
5165 // correct, but works out the same
5166 pipe.shape = alpha;
5167 (this->*pipe.run)(&pipe);
5168 }
5169 }
5170 } else {
5171 for (y = 0; y < h; ++y) {
5172 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5173 ap = src->getAlphaPtr() + (ySrc + y) * src->getWidth() + xSrc;
5174 for (x = 0; x < w; ++x) {
5175 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5176 alpha = *ap++;
5177 if (state->clip->test(x: xDest + x, y: yDest + y)) {
5178 // this uses shape instead of alpha, which isn't technically
5179 // correct, but works out the same
5180 pipe.shape = alpha;
5181 (this->*pipe.run)(&pipe);
5182 } else {
5183 pipeIncX(pipe: &pipe);
5184 }
5185 }
5186 }
5187 }
5188 } else {
5189 pipeInit(pipe: &pipe, x: xDest, y: yDest, pattern: nullptr, cSrc: pixel, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: false, nonIsolatedGroup: nonIsolated);
5190 if (noClip) {
5191 for (y = 0; y < h; ++y) {
5192 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5193 for (x = 0; x < w; ++x) {
5194 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5195 (this->*pipe.run)(&pipe);
5196 }
5197 }
5198 } else {
5199 for (y = 0; y < h; ++y) {
5200 pipeSetXY(pipe: &pipe, x: xDest, y: yDest + y);
5201 for (x = 0; x < w; ++x) {
5202 src->getPixel(x: xSrc + x, y: ySrc + y, pixel);
5203 if (state->clip->test(x: xDest + x, y: yDest + y)) {
5204 (this->*pipe.run)(&pipe);
5205 } else {
5206 pipeIncX(pipe: &pipe);
5207 }
5208 }
5209 }
5210 }
5211 }
5212
5213 return splashOk;
5214}
5215
5216void Splash::compositeBackground(SplashColorConstPtr color)
5217{
5218 SplashColorPtr p;
5219 unsigned char *q;
5220 unsigned char alpha, alpha1, c, color0, color1, color2;
5221 unsigned char color3;
5222 unsigned char colorsp[SPOT_NCOMPS + 4], cp;
5223 int x, y, mask;
5224
5225 if (unlikely(bitmap->alpha == nullptr)) {
5226 error(category: errInternal, pos: -1, msg: "bitmap->alpha is NULL in Splash::compositeBackground");
5227 return;
5228 }
5229
5230 switch (bitmap->mode) {
5231 case splashModeMono1:
5232 color0 = color[0];
5233 for (y = 0; y < bitmap->height; ++y) {
5234 p = &bitmap->data[y * bitmap->rowSize];
5235 q = &bitmap->alpha[y * bitmap->width];
5236 mask = 0x80;
5237 for (x = 0; x < bitmap->width; ++x) {
5238 alpha = *q++;
5239 alpha1 = 255 - alpha;
5240 c = (*p & mask) ? 0xff : 0x00;
5241 c = div255(x: alpha1 * color0 + alpha * c);
5242 if (c & 0x80) {
5243 *p |= mask;
5244 } else {
5245 *p &= ~mask;
5246 }
5247 if (!(mask >>= 1)) {
5248 mask = 0x80;
5249 ++p;
5250 }
5251 }
5252 }
5253 break;
5254 case splashModeMono8:
5255 color0 = color[0];
5256 for (y = 0; y < bitmap->height; ++y) {
5257 p = &bitmap->data[y * bitmap->rowSize];
5258 q = &bitmap->alpha[y * bitmap->width];
5259 for (x = 0; x < bitmap->width; ++x) {
5260 alpha = *q++;
5261 alpha1 = 255 - alpha;
5262 p[0] = div255(x: alpha1 * color0 + alpha * p[0]);
5263 ++p;
5264 }
5265 }
5266 break;
5267 case splashModeRGB8:
5268 case splashModeBGR8:
5269 color0 = color[0];
5270 color1 = color[1];
5271 color2 = color[2];
5272 for (y = 0; y < bitmap->height; ++y) {
5273 p = &bitmap->data[y * bitmap->rowSize];
5274 q = &bitmap->alpha[y * bitmap->width];
5275 for (x = 0; x < bitmap->width; ++x) {
5276 alpha = *q++;
5277 if (alpha == 0) {
5278 p[0] = color0;
5279 p[1] = color1;
5280 p[2] = color2;
5281 } else if (alpha != 255) {
5282 alpha1 = 255 - alpha;
5283 p[0] = div255(x: alpha1 * color0 + alpha * p[0]);
5284 p[1] = div255(x: alpha1 * color1 + alpha * p[1]);
5285 p[2] = div255(x: alpha1 * color2 + alpha * p[2]);
5286 }
5287 p += 3;
5288 }
5289 }
5290 break;
5291 case splashModeXBGR8:
5292 color0 = color[0];
5293 color1 = color[1];
5294 color2 = color[2];
5295 for (y = 0; y < bitmap->height; ++y) {
5296 p = &bitmap->data[y * bitmap->rowSize];
5297 q = &bitmap->alpha[y * bitmap->width];
5298 for (x = 0; x < bitmap->width; ++x) {
5299 alpha = *q++;
5300 if (alpha == 0) {
5301 p[0] = color0;
5302 p[1] = color1;
5303 p[2] = color2;
5304 } else if (alpha != 255) {
5305 alpha1 = 255 - alpha;
5306 p[0] = div255(x: alpha1 * color0 + alpha * p[0]);
5307 p[1] = div255(x: alpha1 * color1 + alpha * p[1]);
5308 p[2] = div255(x: alpha1 * color2 + alpha * p[2]);
5309 }
5310 p[3] = 255;
5311 p += 4;
5312 }
5313 }
5314 break;
5315 case splashModeCMYK8:
5316 color0 = color[0];
5317 color1 = color[1];
5318 color2 = color[2];
5319 color3 = color[3];
5320 for (y = 0; y < bitmap->height; ++y) {
5321 p = &bitmap->data[y * bitmap->rowSize];
5322 q = &bitmap->alpha[y * bitmap->width];
5323 for (x = 0; x < bitmap->width; ++x) {
5324 alpha = *q++;
5325 if (alpha == 0) {
5326 p[0] = color0;
5327 p[1] = color1;
5328 p[2] = color2;
5329 p[3] = color3;
5330 } else if (alpha != 255) {
5331 alpha1 = 255 - alpha;
5332 p[0] = div255(x: alpha1 * color0 + alpha * p[0]);
5333 p[1] = div255(x: alpha1 * color1 + alpha * p[1]);
5334 p[2] = div255(x: alpha1 * color2 + alpha * p[2]);
5335 p[3] = div255(x: alpha1 * color3 + alpha * p[3]);
5336 }
5337 p += 4;
5338 }
5339 }
5340 break;
5341 case splashModeDeviceN8:
5342 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
5343 colorsp[cp] = color[cp];
5344 }
5345 for (y = 0; y < bitmap->height; ++y) {
5346 p = &bitmap->data[y * bitmap->rowSize];
5347 q = &bitmap->alpha[y * bitmap->width];
5348 for (x = 0; x < bitmap->width; ++x) {
5349 alpha = *q++;
5350 if (alpha == 0) {
5351 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
5352 p[cp] = colorsp[cp];
5353 }
5354 } else if (alpha != 255) {
5355 alpha1 = 255 - alpha;
5356 for (cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
5357 p[cp] = div255(x: alpha1 * colorsp[cp] + alpha * p[cp]);
5358 }
5359 }
5360 p += (SPOT_NCOMPS + 4);
5361 }
5362 }
5363 break;
5364 }
5365 memset(s: bitmap->alpha, c: 255, n: bitmap->width * bitmap->height);
5366}
5367
5368bool Splash::gouraudTriangleShadedFill(SplashGouraudColor *shading)
5369{
5370 double xdbl[3] = { 0., 0., 0. };
5371 double ydbl[3] = { 0., 0., 0. };
5372 int x[3] = { 0, 0, 0 };
5373 int y[3] = { 0, 0, 0 };
5374 double xt = 0., xa = 0., yt = 0.;
5375
5376 const int bitmapWidth = bitmap->getWidth();
5377 SplashClip *clip = getClip();
5378 SplashBitmap *blitTarget = bitmap;
5379 SplashColorPtr bitmapData = bitmap->getDataPtr();
5380 const int bitmapOffLimit = bitmap->getHeight() * bitmap->getRowSize();
5381 SplashColorPtr bitmapAlpha = bitmap->getAlphaPtr();
5382 SplashCoord *userToCanvasMatrix = getMatrix();
5383 const SplashColorMode bitmapMode = bitmap->getMode();
5384 bool hasAlpha = (bitmapAlpha != nullptr);
5385 const int rowSize = bitmap->getRowSize();
5386 const int colorComps = splashColorModeNComps[bitmapMode];
5387
5388 SplashPipe pipe;
5389 SplashColor cSrcVal;
5390
5391 pipeInit(pipe: &pipe, x: 0, y: 0, pattern: nullptr, cSrc: cSrcVal, aInput: (unsigned char)splashRound(x: state->fillAlpha * 255), usesShape: false, nonIsolatedGroup: false);
5392
5393 if (vectorAntialias) {
5394 if (aaBuf == nullptr) {
5395 return false; // fall back to old behaviour
5396 }
5397 drawAAPixelInit();
5398 }
5399
5400 // idea:
5401 // 1. If pipe->noTransparency && !state->blendFunc
5402 // -> blit directly into the drawing surface!
5403 // -> disable alpha manually.
5404 // 2. Otherwise:
5405 // - blit also directly, but into an intermediate surface.
5406 // Afterwards, blit the intermediate surface using the drawing pipeline.
5407 // This is necessary because triangle elements can be on top of each
5408 // other, so the complete shading needs to be drawn before opacity is
5409 // applied.
5410 // - the final step, is performed using a SplashPipe:
5411 // - assign the actual color into cSrcVal: pipe uses cSrcVal by reference
5412 // - invoke drawPixel(&pipe,X,Y,bNoClip);
5413 const bool bDirectBlit = vectorAntialias ? false : pipe.noTransparency && !state->blendFunc && !shading->isParameterized();
5414 if (!bDirectBlit) {
5415 blitTarget = new SplashBitmap(bitmap->getWidth(), bitmap->getHeight(), bitmap->getRowPad(), bitmap->getMode(), true, bitmap->getRowSize() >= 0);
5416 bitmapData = blitTarget->getDataPtr();
5417 bitmapAlpha = blitTarget->getAlphaPtr();
5418
5419 // initialisation seems to be necessary:
5420 const int S = bitmap->getWidth() * bitmap->getHeight();
5421 for (int i = 0; i < S; ++i) {
5422 bitmapAlpha[i] = 0;
5423 }
5424 hasAlpha = true;
5425 }
5426
5427 if (shading->isParameterized()) {
5428 double color[3];
5429 double scanLimitMapL[2] = { 0., 0. };
5430 double scanLimitMapR[2] = { 0., 0. };
5431 double scanColorMapL[2] = { 0., 0. };
5432 double scanColorMapR[2] = { 0., 0. };
5433 int scanEdgeL[2] = { 0, 0 };
5434 int scanEdgeR[2] = { 0, 0 };
5435
5436 for (int i = 0; i < shading->getNTriangles(); ++i) {
5437 shading->getParametrizedTriangle(i, x0: xdbl + 0, y0: ydbl + 0, color0: color + 0, x1: xdbl + 1, y1: ydbl + 1, color1: color + 1, x2: xdbl + 2, y2: ydbl + 2, color2: color + 2);
5438 for (int m = 0; m < 3; ++m) {
5439 xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
5440 yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5];
5441 xdbl[m] = xt;
5442 ydbl[m] = yt;
5443 // we operate on scanlines which are integer offsets into the
5444 // raster image. The double offsets are of no use here.
5445 x[m] = splashRound(x: xt);
5446 y[m] = splashRound(x: yt);
5447 }
5448 // sort according to y coordinate to simplify sweep through scanlines:
5449 // INSERTION SORT.
5450 if (y[0] > y[1]) {
5451 Guswap(a&: x[0], b&: x[1]);
5452 Guswap(a&: y[0], b&: y[1]);
5453 Guswap(a&: color[0], b&: color[1]);
5454 }
5455 // first two are sorted.
5456 assert(y[0] <= y[1]);
5457 if (y[1] > y[2]) {
5458 const int tmpX = x[2];
5459 const int tmpY = y[2];
5460 const double tmpC = color[2];
5461 x[2] = x[1];
5462 y[2] = y[1];
5463 color[2] = color[1];
5464
5465 if (y[0] > tmpY) {
5466 x[1] = x[0];
5467 y[1] = y[0];
5468 color[1] = color[0];
5469 x[0] = tmpX;
5470 y[0] = tmpY;
5471 color[0] = tmpC;
5472 } else {
5473 x[1] = tmpX;
5474 y[1] = tmpY;
5475 color[1] = tmpC;
5476 }
5477 }
5478 // first three are sorted
5479 assert(y[0] <= y[1]);
5480 assert(y[1] <= y[2]);
5481 /////
5482
5483 // this here is det( T ) == 0
5484 // where T is the matrix to map to barycentric coordinates.
5485 if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0) {
5486 continue; // degenerate triangle.
5487 }
5488
5489 // this here initialises the scanline generation.
5490 // We start with low Y coordinates and sweep up to the large Y
5491 // coordinates.
5492 //
5493 // scanEdgeL[m] in {0,1,2} m=0,1
5494 // scanEdgeR[m] in {0,1,2} m=0,1
5495 //
5496 // are the two edges between which scanlines are (currently)
5497 // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
5498 // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
5499 //
5500 scanEdgeL[0] = 0;
5501 scanEdgeR[0] = 0;
5502 if (y[0] == y[1]) {
5503 scanEdgeL[0] = 1;
5504 scanEdgeL[1] = scanEdgeR[1] = 2;
5505
5506 } else {
5507 scanEdgeL[1] = 1;
5508 scanEdgeR[1] = 2;
5509 }
5510 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5511 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5512
5513 // Ok. Now prepare the linear maps which map the y coordinate of
5514 // the current scanline to the corresponding LEFT and RIGHT x
5515 // coordinate (which define the scanline).
5516 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5517 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5518 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5519 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5520
5521 xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
5522 xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
5523 if (xa > xt) {
5524 // I have "left" is to the right of "right".
5525 // Exchange sides!
5526 Guswap(a&: scanEdgeL[0], b&: scanEdgeR[0]);
5527 Guswap(a&: scanEdgeL[1], b&: scanEdgeR[1]);
5528 Guswap(a&: scanLimitMapL[0], b&: scanLimitMapR[0]);
5529 Guswap(a&: scanLimitMapL[1], b&: scanLimitMapR[1]);
5530 // FIXME I'm sure there is a more efficient way to check this.
5531 }
5532
5533 // Same game: we can linearly interpolate the color based on the
5534 // current y coordinate (that's correct for triangle
5535 // interpolation due to linearity. We could also have done it in
5536 // barycentric coordinates, but that's slightly more involved)
5537 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5538 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5539 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5540 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5541
5542 bool hasFurtherSegment = (y[1] < y[2]);
5543 int scanLineOff = y[0] * rowSize;
5544
5545 for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
5546 if (hasFurtherSegment && Y == y[1]) {
5547 // SWEEP EVENT: we encountered the next segment.
5548 //
5549 // switch to next segment, either at left end or at right
5550 // end:
5551 if (scanEdgeL[1] == 1) {
5552 scanEdgeL[0] = 1;
5553 scanEdgeL[1] = 2;
5554 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5555 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5556
5557 scanColorMapL[0] = (color[scanEdgeL[1]] - color[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5558 scanColorMapL[1] = color[scanEdgeL[0]] - y[scanEdgeL[0]] * scanColorMapL[0];
5559 } else if (scanEdgeR[1] == 1) {
5560 scanEdgeR[0] = 1;
5561 scanEdgeR[1] = 2;
5562 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5563 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5564
5565 scanColorMapR[0] = (color[scanEdgeR[1]] - color[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5566 scanColorMapR[1] = color[scanEdgeR[0]] - y[scanEdgeR[0]] * scanColorMapR[0];
5567 }
5568 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5569 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5570 hasFurtherSegment = false;
5571 }
5572
5573 yt = Y;
5574
5575 xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
5576 xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
5577
5578 const double ca = yt * scanColorMapL[0] + scanColorMapL[1];
5579 const double ct = yt * scanColorMapR[0] + scanColorMapR[1];
5580
5581 const int scanLimitL = splashRound(x: xa);
5582 const int scanLimitR = splashRound(x: xt);
5583
5584 // Ok. Now: init the color interpolation depending on the X
5585 // coordinate inside of the current scanline:
5586 const double scanColorMap0 = (scanLimitR == scanLimitL) ? 0. : ((ct - ca) / (scanLimitR - scanLimitL));
5587 const double scanColorMap1 = ca - scanLimitL * scanColorMap0;
5588
5589 // handled by clipping:
5590 // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
5591 assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
5592 assert(scanLineOff == Y * rowSize);
5593
5594 double colorinterp = scanColorMap0 * scanLimitL + scanColorMap1;
5595
5596 int bitmapOff = scanLineOff + scanLimitL * colorComps;
5597 if (likely(bitmapOff >= 0)) {
5598 for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, colorinterp += scanColorMap0, bitmapOff += colorComps) {
5599 // FIXME : standard rectangular clipping can be done for a
5600 // complete scanline which is faster
5601 // --> see SplashClip and its methods
5602 if (!clip->test(x: X, y: Y)) {
5603 continue;
5604 }
5605
5606 assert(fabs(colorinterp - (scanColorMap0 * X + scanColorMap1)) < 1e-7);
5607 assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
5608
5609 shading->getParameterizedColor(t: colorinterp, mode: bitmapMode, c: &bitmapData[bitmapOff]);
5610
5611 // make the shading visible.
5612 // Note that opacity is handled by the bDirectBlit stuff, see
5613 // above for comments and below for implementation.
5614 if (hasAlpha) {
5615 bitmapAlpha[Y * bitmapWidth + X] = 255;
5616 }
5617 }
5618 }
5619 }
5620 }
5621 } else {
5622 SplashColor color, auxColor1, auxColor2;
5623 double scanLimitMapL[2] = { 0., 0. };
5624 double scanLimitMapR[2] = { 0., 0. };
5625 int scanEdgeL[2] = { 0, 0 };
5626 int scanEdgeR[2] = { 0, 0 };
5627
5628 for (int i = 0; i < shading->getNTriangles(); ++i) {
5629 // Sadly this current algorithm only supports shadings where the three triangle vertices have the same color
5630 shading->getNonParametrizedTriangle(i, mode: bitmapMode, x0: xdbl + 0, y0: ydbl + 0, color0: (SplashColorPtr)&color, x1: xdbl + 1, y1: ydbl + 1, color1: (SplashColorPtr)&auxColor1, x2: xdbl + 2, y2: ydbl + 2, color2: (SplashColorPtr)&auxColor2);
5631 if (!splashColorEqual(dest: color, src: auxColor1) || !splashColorEqual(dest: color, src: auxColor2)) {
5632 if (!bDirectBlit) {
5633 delete blitTarget;
5634 }
5635 return false;
5636 }
5637 for (int m = 0; m < 3; ++m) {
5638 xt = xdbl[m] * (double)userToCanvasMatrix[0] + ydbl[m] * (double)userToCanvasMatrix[2] + (double)userToCanvasMatrix[4];
5639 yt = xdbl[m] * (double)userToCanvasMatrix[1] + ydbl[m] * (double)userToCanvasMatrix[3] + (double)userToCanvasMatrix[5];
5640 xdbl[m] = xt;
5641 ydbl[m] = yt;
5642 // we operate on scanlines which are integer offsets into the
5643 // raster image. The double offsets are of no use here.
5644 x[m] = splashRound(x: xt);
5645 y[m] = splashRound(x: yt);
5646 }
5647 // sort according to y coordinate to simplify sweep through scanlines:
5648 // INSERTION SORT.
5649 if (y[0] > y[1]) {
5650 Guswap(a&: x[0], b&: x[1]);
5651 Guswap(a&: y[0], b&: y[1]);
5652 }
5653 // first two are sorted.
5654 assert(y[0] <= y[1]);
5655 if (y[1] > y[2]) {
5656 const int tmpX = x[2];
5657 const int tmpY = y[2];
5658 x[2] = x[1];
5659 y[2] = y[1];
5660
5661 if (y[0] > tmpY) {
5662 x[1] = x[0];
5663 y[1] = y[0];
5664 x[0] = tmpX;
5665 y[0] = tmpY;
5666 } else {
5667 x[1] = tmpX;
5668 y[1] = tmpY;
5669 }
5670 }
5671 // first three are sorted
5672 assert(y[0] <= y[1]);
5673 assert(y[1] <= y[2]);
5674 /////
5675
5676 // this here is det( T ) == 0
5677 // where T is the matrix to map to barycentric coordinates.
5678 if ((x[0] - x[2]) * (y[1] - y[2]) - (x[1] - x[2]) * (y[0] - y[2]) == 0) {
5679 continue; // degenerate triangle.
5680 }
5681
5682 // this here initialises the scanline generation.
5683 // We start with low Y coordinates and sweep up to the large Y
5684 // coordinates.
5685 //
5686 // scanEdgeL[m] in {0,1,2} m=0,1
5687 // scanEdgeR[m] in {0,1,2} m=0,1
5688 //
5689 // are the two edges between which scanlines are (currently)
5690 // sweeped. The values {0,1,2} are indices into 'x' and 'y'.
5691 // scanEdgeL[0] = 0 means: the left scan edge has (x[0],y[0]) as vertex.
5692 //
5693 scanEdgeL[0] = 0;
5694 scanEdgeR[0] = 0;
5695 if (y[0] == y[1]) {
5696 scanEdgeL[0] = 1;
5697 scanEdgeL[1] = scanEdgeR[1] = 2;
5698
5699 } else {
5700 scanEdgeL[1] = 1;
5701 scanEdgeR[1] = 2;
5702 }
5703 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5704 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5705
5706 // Ok. Now prepare the linear maps which map the y coordinate of
5707 // the current scanline to the corresponding LEFT and RIGHT x
5708 // coordinate (which define the scanline).
5709 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5710 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5711 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5712 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5713
5714 xa = y[1] * scanLimitMapL[0] + scanLimitMapL[1];
5715 xt = y[1] * scanLimitMapR[0] + scanLimitMapR[1];
5716 if (xa > xt) {
5717 // I have "left" is to the right of "right".
5718 // Exchange sides!
5719 Guswap(a&: scanEdgeL[0], b&: scanEdgeR[0]);
5720 Guswap(a&: scanEdgeL[1], b&: scanEdgeR[1]);
5721 Guswap(a&: scanLimitMapL[0], b&: scanLimitMapR[0]);
5722 Guswap(a&: scanLimitMapL[1], b&: scanLimitMapR[1]);
5723 // FIXME I'm sure there is a more efficient way to check this.
5724 }
5725
5726 bool hasFurtherSegment = (y[1] < y[2]);
5727 int scanLineOff = y[0] * rowSize;
5728
5729 for (int Y = y[0]; Y <= y[2]; ++Y, scanLineOff += rowSize) {
5730 if (hasFurtherSegment && Y == y[1]) {
5731 // SWEEP EVENT: we encountered the next segment.
5732 //
5733 // switch to next segment, either at left end or at right
5734 // end:
5735 if (scanEdgeL[1] == 1) {
5736 scanEdgeL[0] = 1;
5737 scanEdgeL[1] = 2;
5738 scanLimitMapL[0] = double(x[scanEdgeL[1]] - x[scanEdgeL[0]]) / (y[scanEdgeL[1]] - y[scanEdgeL[0]]);
5739 scanLimitMapL[1] = x[scanEdgeL[0]] - y[scanEdgeL[0]] * scanLimitMapL[0];
5740 } else if (scanEdgeR[1] == 1) {
5741 scanEdgeR[0] = 1;
5742 scanEdgeR[1] = 2;
5743 scanLimitMapR[0] = double(x[scanEdgeR[1]] - x[scanEdgeR[0]]) / (y[scanEdgeR[1]] - y[scanEdgeR[0]]);
5744 scanLimitMapR[1] = x[scanEdgeR[0]] - y[scanEdgeR[0]] * scanLimitMapR[0];
5745 }
5746 assert(y[scanEdgeL[0]] < y[scanEdgeL[1]]);
5747 assert(y[scanEdgeR[0]] < y[scanEdgeR[1]]);
5748 hasFurtherSegment = false;
5749 }
5750
5751 yt = Y;
5752
5753 xa = yt * scanLimitMapL[0] + scanLimitMapL[1];
5754 xt = yt * scanLimitMapR[0] + scanLimitMapR[1];
5755
5756 const int scanLimitL = splashRound(x: xa);
5757 const int scanLimitR = splashRound(x: xt);
5758
5759 // handled by clipping:
5760 // assert( scanLimitL >= 0 && scanLimitR < bitmap->getWidth() );
5761 assert(scanLimitL <= scanLimitR || abs(scanLimitL - scanLimitR) <= 2); // allow rounding inaccuracies
5762 assert(scanLineOff == Y * rowSize);
5763
5764 int bitmapOff = scanLineOff + scanLimitL * colorComps;
5765 if (likely(bitmapOff >= 0)) {
5766 for (int X = scanLimitL; X <= scanLimitR && bitmapOff + colorComps <= bitmapOffLimit; ++X, bitmapOff += colorComps) {
5767 // FIXME : standard rectangular clipping can be done for a
5768 // complete scanline which is faster
5769 // --> see SplashClip and its methods
5770 if (!clip->test(x: X, y: Y)) {
5771 continue;
5772 }
5773
5774 assert(bitmapOff == Y * rowSize + colorComps * X && scanLineOff == Y * rowSize);
5775
5776 for (int k = 0; k < colorComps; ++k) {
5777 bitmapData[bitmapOff + k] = color[k];
5778 }
5779
5780 // make the shading visible.
5781 // Note that opacity is handled by the bDirectBlit stuff, see
5782 // above for comments and below for implementation.
5783 if (hasAlpha) {
5784 bitmapAlpha[Y * bitmapWidth + X] = 255;
5785 }
5786 }
5787 }
5788 }
5789 }
5790 }
5791
5792 if (!bDirectBlit) {
5793 // ok. Finalize the stuff by blitting the shading into the final
5794 // geometry, this time respecting the rendering pipe.
5795 const int W = blitTarget->getWidth();
5796 const int H = blitTarget->getHeight();
5797 SplashColorPtr cur = cSrcVal;
5798
5799 for (int X = 0; X < W; ++X) {
5800 for (int Y = 0; Y < H; ++Y) {
5801 if (!bitmapAlpha[Y * bitmapWidth + X]) {
5802 continue; // draw only parts of the shading!
5803 }
5804 const int bitmapOff = Y * rowSize + colorComps * X;
5805
5806 for (int m = 0; m < colorComps; ++m) {
5807 cur[m] = bitmapData[bitmapOff + m];
5808 }
5809 if (vectorAntialias) {
5810 drawAAPixel(pipe: &pipe, x: X, y: Y);
5811 } else {
5812 drawPixel(pipe: &pipe, x: X, y: Y, noClip: true); // no clipping - has already been done.
5813 }
5814 }
5815 }
5816
5817 delete blitTarget;
5818 blitTarget = nullptr;
5819 }
5820
5821 return true;
5822}
5823
5824SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h)
5825{
5826 SplashColorPtr p, sp;
5827 unsigned char *q;
5828 int x, y, mask, srcMask, width = w, height = h;
5829
5830 if (src->mode != bitmap->mode) {
5831 return splashErrModeMismatch;
5832 }
5833
5834 if (unlikely(!bitmap->data)) {
5835 return splashErrZeroImage;
5836 }
5837
5838 if (src->getWidth() - xSrc < width) {
5839 width = src->getWidth() - xSrc;
5840 }
5841
5842 if (src->getHeight() - ySrc < height) {
5843 height = src->getHeight() - ySrc;
5844 }
5845
5846 if (bitmap->getWidth() - xDest < width) {
5847 width = bitmap->getWidth() - xDest;
5848 }
5849
5850 if (bitmap->getHeight() - yDest < height) {
5851 height = bitmap->getHeight() - yDest;
5852 }
5853
5854 if (width < 0) {
5855 width = 0;
5856 }
5857
5858 if (height < 0) {
5859 height = 0;
5860 }
5861
5862 switch (bitmap->mode) {
5863 case splashModeMono1:
5864 for (y = 0; y < height; ++y) {
5865 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (xDest >> 3)];
5866 mask = 0x80 >> (xDest & 7);
5867 sp = &src->data[(ySrc + y) * src->rowSize + (xSrc >> 3)];
5868 srcMask = 0x80 >> (xSrc & 7);
5869 for (x = 0; x < width; ++x) {
5870 if (*sp & srcMask) {
5871 *p |= mask;
5872 } else {
5873 *p &= ~mask;
5874 }
5875 if (!(mask >>= 1)) {
5876 mask = 0x80;
5877 ++p;
5878 }
5879 if (!(srcMask >>= 1)) {
5880 srcMask = 0x80;
5881 ++sp;
5882 }
5883 }
5884 }
5885 break;
5886 case splashModeMono8:
5887 for (y = 0; y < height; ++y) {
5888 p = &bitmap->data[(yDest + y) * bitmap->rowSize + xDest];
5889 sp = &src->data[(ySrc + y) * src->rowSize + xSrc];
5890 for (x = 0; x < width; ++x) {
5891 *p++ = *sp++;
5892 }
5893 }
5894 break;
5895 case splashModeRGB8:
5896 case splashModeBGR8:
5897 for (y = 0; y < height; ++y) {
5898 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 3 * xDest];
5899 sp = &src->data[(ySrc + y) * src->rowSize + 3 * xSrc];
5900 for (x = 0; x < width; ++x) {
5901 *p++ = *sp++;
5902 *p++ = *sp++;
5903 *p++ = *sp++;
5904 }
5905 }
5906 break;
5907 case splashModeXBGR8:
5908 for (y = 0; y < height; ++y) {
5909 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5910 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5911 for (x = 0; x < width; ++x) {
5912 *p++ = *sp++;
5913 *p++ = *sp++;
5914 *p++ = *sp++;
5915 *p++ = 255;
5916 sp++;
5917 }
5918 }
5919 break;
5920 case splashModeCMYK8:
5921 for (y = 0; y < height; ++y) {
5922 p = &bitmap->data[(yDest + y) * bitmap->rowSize + 4 * xDest];
5923 sp = &src->data[(ySrc + y) * src->rowSize + 4 * xSrc];
5924 for (x = 0; x < width; ++x) {
5925 *p++ = *sp++;
5926 *p++ = *sp++;
5927 *p++ = *sp++;
5928 *p++ = *sp++;
5929 }
5930 }
5931 break;
5932 case splashModeDeviceN8:
5933 for (y = 0; y < height; ++y) {
5934 p = &bitmap->data[(yDest + y) * bitmap->rowSize + (SPOT_NCOMPS + 4) * xDest];
5935 sp = &src->data[(ySrc + y) * src->rowSize + (SPOT_NCOMPS + 4) * xSrc];
5936 for (x = 0; x < width; ++x) {
5937 for (int cp = 0; cp < SPOT_NCOMPS + 4; cp++) {
5938 *p++ = *sp++;
5939 }
5940 }
5941 }
5942 break;
5943 }
5944
5945 if (bitmap->alpha) {
5946 for (y = 0; y < height; ++y) {
5947 q = &bitmap->alpha[(yDest + y) * bitmap->width + xDest];
5948 memset(s: q, c: 0x00, n: width);
5949 }
5950 }
5951
5952 return splashOk;
5953}
5954
5955SplashPath *Splash::makeStrokePath(SplashPath *path, SplashCoord w, bool flatten)
5956{
5957 SplashPath *pathIn, *dashPath, *pathOut;
5958 SplashCoord d, dx, dy, wdx, wdy, dxNext, dyNext, wdxNext, wdyNext;
5959 SplashCoord crossprod, dotprod, miter, m;
5960 bool first, last, closed, hasangle;
5961 int subpathStart0, subpathStart1, seg, i0, i1, j0, j1, k0, k1;
5962 int left0, left1, left2, right0, right1, right2, join0, join1, join2;
5963 int leftFirst, rightFirst, firstPt;
5964
5965 pathOut = new SplashPath();
5966
5967 if (path->length == 0) {
5968 return pathOut;
5969 }
5970
5971 if (flatten) {
5972 pathIn = flattenPath(path, matrix: state->matrix, flatness: state->flatness);
5973 if (!state->lineDash.empty()) {
5974 dashPath = makeDashedPath(path: pathIn);
5975 delete pathIn;
5976 pathIn = dashPath;
5977 if (pathIn->length == 0) {
5978 delete pathIn;
5979 return pathOut;
5980 }
5981 }
5982 } else {
5983 pathIn = path;
5984 }
5985
5986 subpathStart0 = subpathStart1 = 0; // make gcc happy
5987 seg = 0; // make gcc happy
5988 closed = false; // make gcc happy
5989 left0 = left1 = right0 = right1 = join0 = join1 = 0; // make gcc happy
5990 leftFirst = rightFirst = firstPt = 0; // make gcc happy
5991
5992 i0 = 0;
5993 for (i1 = i0; !(pathIn->flags[i1] & splashPathLast) && i1 + 1 < pathIn->length && pathIn->pts[i1 + 1].x == pathIn->pts[i1].x && pathIn->pts[i1 + 1].y == pathIn->pts[i1].y; ++i1) {
5994 ;
5995 }
5996
5997 while (i1 < pathIn->length) {
5998 if ((first = pathIn->flags[i0] & splashPathFirst)) {
5999 subpathStart0 = i0;
6000 subpathStart1 = i1;
6001 seg = 0;
6002 closed = pathIn->flags[i0] & splashPathClosed;
6003 }
6004 j0 = i1 + 1;
6005 if (j0 < pathIn->length) {
6006 for (j1 = j0; !(pathIn->flags[j1] & splashPathLast) && j1 + 1 < pathIn->length && pathIn->pts[j1 + 1].x == pathIn->pts[j1].x && pathIn->pts[j1 + 1].y == pathIn->pts[j1].y; ++j1) {
6007 ;
6008 }
6009 } else {
6010 j1 = j0;
6011 }
6012 if (pathIn->flags[i1] & splashPathLast) {
6013 if (first && state->lineCap == splashLineCapRound) {
6014 // special case: zero-length subpath with round line caps -->
6015 // draw a circle
6016 pathOut->moveTo(x: pathIn->pts[i0].x + (SplashCoord)0.5 * w, y: pathIn->pts[i0].y);
6017 pathOut->curveTo(x1: pathIn->pts[i0].x + (SplashCoord)0.5 * w, y1: pathIn->pts[i0].y + bezierCircle2 * w, x2: pathIn->pts[i0].x + bezierCircle2 * w, y2: pathIn->pts[i0].y + (SplashCoord)0.5 * w, x3: pathIn->pts[i0].x,
6018 y3: pathIn->pts[i0].y + (SplashCoord)0.5 * w);
6019 pathOut->curveTo(x1: pathIn->pts[i0].x - bezierCircle2 * w, y1: pathIn->pts[i0].y + (SplashCoord)0.5 * w, x2: pathIn->pts[i0].x - (SplashCoord)0.5 * w, y2: pathIn->pts[i0].y + bezierCircle2 * w, x3: pathIn->pts[i0].x - (SplashCoord)0.5 * w,
6020 y3: pathIn->pts[i0].y);
6021 pathOut->curveTo(x1: pathIn->pts[i0].x - (SplashCoord)0.5 * w, y1: pathIn->pts[i0].y - bezierCircle2 * w, x2: pathIn->pts[i0].x - bezierCircle2 * w, y2: pathIn->pts[i0].y - (SplashCoord)0.5 * w, x3: pathIn->pts[i0].x,
6022 y3: pathIn->pts[i0].y - (SplashCoord)0.5 * w);
6023 pathOut->curveTo(x1: pathIn->pts[i0].x + bezierCircle2 * w, y1: pathIn->pts[i0].y - (SplashCoord)0.5 * w, x2: pathIn->pts[i0].x + (SplashCoord)0.5 * w, y2: pathIn->pts[i0].y - bezierCircle2 * w, x3: pathIn->pts[i0].x + (SplashCoord)0.5 * w,
6024 y3: pathIn->pts[i0].y);
6025 pathOut->close();
6026 }
6027 i0 = j0;
6028 i1 = j1;
6029 continue;
6030 }
6031 last = pathIn->flags[j1] & splashPathLast;
6032 if (last) {
6033 k0 = subpathStart1 + 1;
6034 } else {
6035 k0 = j1 + 1;
6036 }
6037 for (k1 = k0; !(pathIn->flags[k1] & splashPathLast) && k1 + 1 < pathIn->length && pathIn->pts[k1 + 1].x == pathIn->pts[k1].x && pathIn->pts[k1 + 1].y == pathIn->pts[k1].y; ++k1) {
6038 ;
6039 }
6040
6041 // compute the deltas for segment (i1, j0)
6042 d = (SplashCoord)1 / splashDist(x0: pathIn->pts[i1].x, y0: pathIn->pts[i1].y, x1: pathIn->pts[j0].x, y1: pathIn->pts[j0].y);
6043 dx = d * (pathIn->pts[j0].x - pathIn->pts[i1].x);
6044 dy = d * (pathIn->pts[j0].y - pathIn->pts[i1].y);
6045 wdx = (SplashCoord)0.5 * w * dx;
6046 wdy = (SplashCoord)0.5 * w * dy;
6047
6048 // draw the start cap
6049 if (pathOut->moveTo(x: pathIn->pts[i0].x - wdy, y: pathIn->pts[i0].y + wdx) != splashOk) {
6050 break;
6051 }
6052 if (i0 == subpathStart0) {
6053 firstPt = pathOut->length - 1;
6054 }
6055 if (first && !closed) {
6056 switch (state->lineCap) {
6057 case splashLineCapButt:
6058 pathOut->lineTo(x: pathIn->pts[i0].x + wdy, y: pathIn->pts[i0].y - wdx);
6059 break;
6060 case splashLineCapRound:
6061 pathOut->curveTo(x1: pathIn->pts[i0].x - wdy - bezierCircle * wdx, y1: pathIn->pts[i0].y + wdx - bezierCircle * wdy, x2: pathIn->pts[i0].x - wdx - bezierCircle * wdy, y2: pathIn->pts[i0].y - wdy + bezierCircle * wdx,
6062 x3: pathIn->pts[i0].x - wdx, y3: pathIn->pts[i0].y - wdy);
6063 pathOut->curveTo(x1: pathIn->pts[i0].x - wdx + bezierCircle * wdy, y1: pathIn->pts[i0].y - wdy - bezierCircle * wdx, x2: pathIn->pts[i0].x + wdy - bezierCircle * wdx, y2: pathIn->pts[i0].y - wdx - bezierCircle * wdy,
6064 x3: pathIn->pts[i0].x + wdy, y3: pathIn->pts[i0].y - wdx);
6065 break;
6066 case splashLineCapProjecting:
6067 pathOut->lineTo(x: pathIn->pts[i0].x - wdx - wdy, y: pathIn->pts[i0].y + wdx - wdy);
6068 pathOut->lineTo(x: pathIn->pts[i0].x - wdx + wdy, y: pathIn->pts[i0].y - wdx - wdy);
6069 pathOut->lineTo(x: pathIn->pts[i0].x + wdy, y: pathIn->pts[i0].y - wdx);
6070 break;
6071 }
6072 } else {
6073 pathOut->lineTo(x: pathIn->pts[i0].x + wdy, y: pathIn->pts[i0].y - wdx);
6074 }
6075
6076 // draw the left side of the segment rectangle
6077 left2 = pathOut->length - 1;
6078 pathOut->lineTo(x: pathIn->pts[j0].x + wdy, y: pathIn->pts[j0].y - wdx);
6079
6080 // draw the end cap
6081 if (last && !closed) {
6082 switch (state->lineCap) {
6083 case splashLineCapButt:
6084 pathOut->lineTo(x: pathIn->pts[j0].x - wdy, y: pathIn->pts[j0].y + wdx);
6085 break;
6086 case splashLineCapRound:
6087 pathOut->curveTo(x1: pathIn->pts[j0].x + wdy + bezierCircle * wdx, y1: pathIn->pts[j0].y - wdx + bezierCircle * wdy, x2: pathIn->pts[j0].x + wdx + bezierCircle * wdy, y2: pathIn->pts[j0].y + wdy - bezierCircle * wdx,
6088 x3: pathIn->pts[j0].x + wdx, y3: pathIn->pts[j0].y + wdy);
6089 pathOut->curveTo(x1: pathIn->pts[j0].x + wdx - bezierCircle * wdy, y1: pathIn->pts[j0].y + wdy + bezierCircle * wdx, x2: pathIn->pts[j0].x - wdy + bezierCircle * wdx, y2: pathIn->pts[j0].y + wdx + bezierCircle * wdy,
6090 x3: pathIn->pts[j0].x - wdy, y3: pathIn->pts[j0].y + wdx);
6091 break;
6092 case splashLineCapProjecting:
6093 pathOut->lineTo(x: pathIn->pts[j0].x + wdy + wdx, y: pathIn->pts[j0].y - wdx + wdy);
6094 pathOut->lineTo(x: pathIn->pts[j0].x - wdy + wdx, y: pathIn->pts[j0].y + wdx + wdy);
6095 pathOut->lineTo(x: pathIn->pts[j0].x - wdy, y: pathIn->pts[j0].y + wdx);
6096 break;
6097 }
6098 } else {
6099 pathOut->lineTo(x: pathIn->pts[j0].x - wdy, y: pathIn->pts[j0].y + wdx);
6100 }
6101
6102 // draw the right side of the segment rectangle
6103 // (NB: if stroke adjustment is enabled, the closepath operation MUST
6104 // add a segment because this segment is used for a hint)
6105 right2 = pathOut->length - 1;
6106 pathOut->close(force: state->strokeAdjust);
6107
6108 // draw the join
6109 join2 = pathOut->length;
6110 if (!last || closed) {
6111
6112 // compute the deltas for segment (j1, k0)
6113 d = (SplashCoord)1 / splashDist(x0: pathIn->pts[j1].x, y0: pathIn->pts[j1].y, x1: pathIn->pts[k0].x, y1: pathIn->pts[k0].y);
6114 dxNext = d * (pathIn->pts[k0].x - pathIn->pts[j1].x);
6115 dyNext = d * (pathIn->pts[k0].y - pathIn->pts[j1].y);
6116 wdxNext = (SplashCoord)0.5 * w * dxNext;
6117 wdyNext = (SplashCoord)0.5 * w * dyNext;
6118
6119 // compute the join parameters
6120 crossprod = dx * dyNext - dy * dxNext;
6121 dotprod = -(dx * dxNext + dy * dyNext);
6122 hasangle = crossprod != 0 || dx * dxNext < 0 || dy * dyNext < 0;
6123 if (dotprod > 0.9999) {
6124 // avoid a divide-by-zero -- set miter to something arbitrary
6125 // such that sqrt(miter) will exceed miterLimit (and m is never
6126 // used in that situation)
6127 // (note: the comparison value (0.9999) has to be less than
6128 // 1-epsilon, where epsilon is the smallest value
6129 // representable in the fixed point format)
6130 miter = (state->miterLimit + 1) * (state->miterLimit + 1);
6131 m = 0;
6132 } else {
6133 miter = (SplashCoord)2 / ((SplashCoord)1 - dotprod);
6134 if (miter < 1) {
6135 // this can happen because of floating point inaccuracies
6136 miter = 1;
6137 }
6138 m = splashSqrt(x: miter - 1);
6139 }
6140
6141 // hasangle == false means that the current and and the next segment
6142 // are parallel. In that case no join needs to be drawn.
6143 // round join
6144 if (hasangle && state->lineJoin == splashLineJoinRound) {
6145 // join angle < 180
6146 if (crossprod < 0) {
6147 SplashCoord angle = atan2(y: (double)dx, x: (double)-dy);
6148 SplashCoord angleNext = atan2(y: (double)dxNext, x: (double)-dyNext);
6149 if (angle < angleNext) {
6150 angle += 2 * M_PI;
6151 }
6152 SplashCoord dAngle = (angle - angleNext) / M_PI;
6153 if (dAngle < 0.501) {
6154 // span angle is <= 90 degrees -> draw a single arc
6155 SplashCoord kappa = dAngle * bezierCircle * w;
6156 SplashCoord cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
6157 SplashCoord cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
6158 SplashCoord cx2 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
6159 SplashCoord cy2 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
6160 pathOut->moveTo(x: pathIn->pts[j0].x, y: pathIn->pts[j0].y);
6161 pathOut->lineTo(x: pathIn->pts[j0].x - wdyNext, y: pathIn->pts[j0].y + wdxNext);
6162 pathOut->curveTo(x1: cx2, y1: cy2, x2: cx1, y2: cy1, x3: pathIn->pts[j0].x - wdy, y3: pathIn->pts[j0].y + wdx);
6163 } else {
6164 // span angle is > 90 degrees -> split into two arcs
6165 SplashCoord dJoin = splashDist(x0: -wdy, y0: wdx, x1: -wdyNext, y1: wdxNext);
6166 if (dJoin > 0) {
6167 SplashCoord dxJoin = (-wdyNext + wdy) / dJoin;
6168 SplashCoord dyJoin = (wdxNext - wdx) / dJoin;
6169 SplashCoord xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos(x: (double)((SplashCoord)0.5 * (angle + angleNext)));
6170 SplashCoord yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin(x: (double)((SplashCoord)0.5 * (angle + angleNext)));
6171 SplashCoord kappa = dAngle * bezierCircle2 * w;
6172 SplashCoord cx1 = pathIn->pts[j0].x - wdy + kappa * dx;
6173 SplashCoord cy1 = pathIn->pts[j0].y + wdx + kappa * dy;
6174 SplashCoord cx2 = xc - kappa * dxJoin;
6175 SplashCoord cy2 = yc - kappa * dyJoin;
6176 SplashCoord cx3 = xc + kappa * dxJoin;
6177 SplashCoord cy3 = yc + kappa * dyJoin;
6178 SplashCoord cx4 = pathIn->pts[j0].x - wdyNext - kappa * dxNext;
6179 SplashCoord cy4 = pathIn->pts[j0].y + wdxNext - kappa * dyNext;
6180 pathOut->moveTo(x: pathIn->pts[j0].x, y: pathIn->pts[j0].y);
6181 pathOut->lineTo(x: pathIn->pts[j0].x - wdyNext, y: pathIn->pts[j0].y + wdxNext);
6182 pathOut->curveTo(x1: cx4, y1: cy4, x2: cx3, y2: cy3, x3: xc, y3: yc);
6183 pathOut->curveTo(x1: cx2, y1: cy2, x2: cx1, y2: cy1, x3: pathIn->pts[j0].x - wdy, y3: pathIn->pts[j0].y + wdx);
6184 }
6185 }
6186
6187 // join angle >= 180
6188 } else {
6189 SplashCoord angle = atan2(y: (double)-dx, x: (double)dy);
6190 SplashCoord angleNext = atan2(y: (double)-dxNext, x: (double)dyNext);
6191 if (angleNext < angle) {
6192 angleNext += 2 * M_PI;
6193 }
6194 SplashCoord dAngle = (angleNext - angle) / M_PI;
6195 if (dAngle < 0.501) {
6196 // span angle is <= 90 degrees -> draw a single arc
6197 SplashCoord kappa = dAngle * bezierCircle * w;
6198 SplashCoord cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
6199 SplashCoord cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
6200 SplashCoord cx2 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
6201 SplashCoord cy2 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
6202 pathOut->moveTo(x: pathIn->pts[j0].x, y: pathIn->pts[j0].y);
6203 pathOut->lineTo(x: pathIn->pts[j0].x + wdy, y: pathIn->pts[j0].y - wdx);
6204 pathOut->curveTo(x1: cx1, y1: cy1, x2: cx2, y2: cy2, x3: pathIn->pts[j0].x + wdyNext, y3: pathIn->pts[j0].y - wdxNext);
6205 } else {
6206 // span angle is > 90 degrees -> split into two arcs
6207 SplashCoord dJoin = splashDist(x0: wdy, y0: -wdx, x1: wdyNext, y1: -wdxNext);
6208 if (dJoin > 0) {
6209 SplashCoord dxJoin = (wdyNext - wdy) / dJoin;
6210 SplashCoord dyJoin = (-wdxNext + wdx) / dJoin;
6211 SplashCoord xc = pathIn->pts[j0].x + (SplashCoord)0.5 * w * cos(x: (double)((SplashCoord)0.5 * (angle + angleNext)));
6212 SplashCoord yc = pathIn->pts[j0].y + (SplashCoord)0.5 * w * sin(x: (double)((SplashCoord)0.5 * (angle + angleNext)));
6213 SplashCoord kappa = dAngle * bezierCircle2 * w;
6214 SplashCoord cx1 = pathIn->pts[j0].x + wdy + kappa * dx;
6215 SplashCoord cy1 = pathIn->pts[j0].y - wdx + kappa * dy;
6216 SplashCoord cx2 = xc - kappa * dxJoin;
6217 SplashCoord cy2 = yc - kappa * dyJoin;
6218 SplashCoord cx3 = xc + kappa * dxJoin;
6219 SplashCoord cy3 = yc + kappa * dyJoin;
6220 SplashCoord cx4 = pathIn->pts[j0].x + wdyNext - kappa * dxNext;
6221 SplashCoord cy4 = pathIn->pts[j0].y - wdxNext - kappa * dyNext;
6222 pathOut->moveTo(x: pathIn->pts[j0].x, y: pathIn->pts[j0].y);
6223 pathOut->lineTo(x: pathIn->pts[j0].x + wdy, y: pathIn->pts[j0].y - wdx);
6224 pathOut->curveTo(x1: cx1, y1: cy1, x2: cx2, y2: cy2, x3: xc, y3: yc);
6225 pathOut->curveTo(x1: cx3, y1: cy3, x2: cx4, y2: cy4, x3: pathIn->pts[j0].x + wdyNext, y3: pathIn->pts[j0].y - wdxNext);
6226 }
6227 }
6228 }
6229
6230 } else if (hasangle) {
6231 pathOut->moveTo(x: pathIn->pts[j0].x, y: pathIn->pts[j0].y);
6232
6233 // angle < 180
6234 if (crossprod < 0) {
6235 pathOut->lineTo(x: pathIn->pts[j0].x - wdyNext, y: pathIn->pts[j0].y + wdxNext);
6236 // miter join inside limit
6237 if (state->lineJoin == splashLineJoinMiter && splashSqrt(x: miter) <= state->miterLimit) {
6238 pathOut->lineTo(x: pathIn->pts[j0].x - wdy + wdx * m, y: pathIn->pts[j0].y + wdx + wdy * m);
6239 pathOut->lineTo(x: pathIn->pts[j0].x - wdy, y: pathIn->pts[j0].y + wdx);
6240 // bevel join or miter join outside limit
6241 } else {
6242 pathOut->lineTo(x: pathIn->pts[j0].x - wdy, y: pathIn->pts[j0].y + wdx);
6243 }
6244
6245 // angle >= 180
6246 } else {
6247 pathOut->lineTo(x: pathIn->pts[j0].x + wdy, y: pathIn->pts[j0].y - wdx);
6248 // miter join inside limit
6249 if (state->lineJoin == splashLineJoinMiter && splashSqrt(x: miter) <= state->miterLimit) {
6250 pathOut->lineTo(x: pathIn->pts[j0].x + wdy + wdx * m, y: pathIn->pts[j0].y - wdx + wdy * m);
6251 pathOut->lineTo(x: pathIn->pts[j0].x + wdyNext, y: pathIn->pts[j0].y - wdxNext);
6252 // bevel join or miter join outside limit
6253 } else {
6254 pathOut->lineTo(x: pathIn->pts[j0].x + wdyNext, y: pathIn->pts[j0].y - wdxNext);
6255 }
6256 }
6257 }
6258
6259 pathOut->close();
6260 }
6261
6262 // add stroke adjustment hints
6263 if (state->strokeAdjust) {
6264 if (seg == 0 && !closed) {
6265 if (state->lineCap == splashLineCapButt) {
6266 pathOut->addStrokeAdjustHint(ctrl0: firstPt, ctrl1: left2 + 1, firstPt, lastPt: firstPt + 1);
6267 if (last) {
6268 pathOut->addStrokeAdjustHint(ctrl0: firstPt, ctrl1: left2 + 1, firstPt: left2 + 1, lastPt: left2 + 2);
6269 }
6270 } else if (state->lineCap == splashLineCapProjecting) {
6271 if (last) {
6272 pathOut->addStrokeAdjustHint(ctrl0: firstPt + 1, ctrl1: left2 + 2, firstPt: firstPt + 1, lastPt: firstPt + 2);
6273 pathOut->addStrokeAdjustHint(ctrl0: firstPt + 1, ctrl1: left2 + 2, firstPt: left2 + 2, lastPt: left2 + 3);
6274 } else {
6275 pathOut->addStrokeAdjustHint(ctrl0: firstPt + 1, ctrl1: left2 + 1, firstPt: firstPt + 1, lastPt: firstPt + 2);
6276 }
6277 }
6278 }
6279 if (seg >= 1) {
6280 if (seg >= 2) {
6281 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: left0 + 1, lastPt: right0);
6282 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: join0, lastPt: left2);
6283 } else {
6284 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt, lastPt: left2);
6285 }
6286 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: right2 + 1, lastPt: right2 + 1);
6287 }
6288 left0 = left1;
6289 left1 = left2;
6290 right0 = right1;
6291 right1 = right2;
6292 join0 = join1;
6293 join1 = join2;
6294 if (seg == 0) {
6295 leftFirst = left2;
6296 rightFirst = right2;
6297 }
6298 if (last) {
6299 if (seg >= 2) {
6300 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: left0 + 1, lastPt: right0);
6301 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: join0, lastPt: pathOut->length - 1);
6302 } else {
6303 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt, lastPt: pathOut->length - 1);
6304 }
6305 if (closed) {
6306 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt, lastPt: leftFirst);
6307 pathOut->addStrokeAdjustHint(ctrl0: left1, ctrl1: right1, firstPt: rightFirst + 1, lastPt: rightFirst + 1);
6308 pathOut->addStrokeAdjustHint(ctrl0: leftFirst, ctrl1: rightFirst, firstPt: left1 + 1, lastPt: right1);
6309 pathOut->addStrokeAdjustHint(ctrl0: leftFirst, ctrl1: rightFirst, firstPt: join1, lastPt: pathOut->length - 1);
6310 }
6311 if (!closed && seg > 0) {
6312 if (state->lineCap == splashLineCapButt) {
6313 pathOut->addStrokeAdjustHint(ctrl0: left1 - 1, ctrl1: left1 + 1, firstPt: left1 + 1, lastPt: left1 + 2);
6314 } else if (state->lineCap == splashLineCapProjecting) {
6315 pathOut->addStrokeAdjustHint(ctrl0: left1 - 1, ctrl1: left1 + 2, firstPt: left1 + 2, lastPt: left1 + 3);
6316 }
6317 }
6318 }
6319 }
6320
6321 i0 = j0;
6322 i1 = j1;
6323 ++seg;
6324 }
6325
6326 if (pathIn != path) {
6327 delete pathIn;
6328 }
6329
6330 return pathOut;
6331}
6332
6333void Splash::dumpPath(SplashPath *path)
6334{
6335 int i;
6336
6337 for (i = 0; i < path->length; ++i) {
6338 printf(format: " %3d: x=%8.2f y=%8.2f%s%s%s%s\n", i, (double)path->pts[i].x, (double)path->pts[i].y, (path->flags[i] & splashPathFirst) ? " first" : "", (path->flags[i] & splashPathLast) ? " last" : "",
6339 (path->flags[i] & splashPathClosed) ? " closed" : "", (path->flags[i] & splashPathCurve) ? " curve" : "");
6340 }
6341}
6342
6343void Splash::dumpXPath(SplashXPath *path)
6344{
6345 int i;
6346
6347 for (i = 0; i < path->length; ++i) {
6348 printf(format: " %4d: x0=%8.2f y0=%8.2f x1=%8.2f y1=%8.2f %s%s%s\n", i, (double)path->segs[i].x0, (double)path->segs[i].y0, (double)path->segs[i].x1, (double)path->segs[i].y1, (path->segs[i].flags & splashXPathHoriz) ? "H" : " ",
6349 (path->segs[i].flags & splashXPathVert) ? "V" : " ", (path->segs[i].flags & splashXPathFlip) ? "P" : " ");
6350 }
6351}
6352
6353SplashError Splash::shadedFill(SplashPath *path, bool hasBBox, SplashPattern *pattern, bool clipToStrokePath)
6354{
6355 SplashPipe pipe;
6356 int xMinI, yMinI, xMaxI, yMaxI, x0, x1, y;
6357 SplashClipResult clipRes;
6358
6359 if (vectorAntialias && aaBuf == nullptr) { // should not happen, but to be secure
6360 return splashErrGeneric;
6361 }
6362 if (path->length == 0) {
6363 return splashErrEmptyPath;
6364 }
6365 SplashXPath xPath(path, state->matrix, state->flatness, true);
6366 if (vectorAntialias) {
6367 xPath.aaScale();
6368 }
6369 xPath.sort();
6370 yMinI = state->clip->getYMinI();
6371 yMaxI = state->clip->getYMaxI();
6372 if (vectorAntialias && !inShading) {
6373 yMinI = yMinI * splashAASize;
6374 yMaxI = (yMaxI + 1) * splashAASize - 1;
6375 }
6376 SplashXPathScanner scanner(xPath, false, yMinI, yMaxI);
6377
6378 // get the min and max x and y values
6379 if (vectorAntialias) {
6380 scanner.getBBoxAA(xMinA: &xMinI, yMinA: &yMinI, xMaxA: &xMaxI, yMaxA: &yMaxI);
6381 } else {
6382 scanner.getBBox(xMinA: &xMinI, yMinA: &yMinI, xMaxA: &xMaxI, yMaxA: &yMaxI);
6383 }
6384
6385 // check clipping
6386 if ((clipRes = state->clip->testRect(rectXMin: xMinI, rectYMin: yMinI, rectXMax: xMaxI, rectYMax: yMaxI)) != splashClipAllOutside) {
6387 // limit the y range
6388 if (yMinI < state->clip->getYMinI()) {
6389 yMinI = state->clip->getYMinI();
6390 }
6391 if (yMaxI > state->clip->getYMaxI()) {
6392 yMaxI = state->clip->getYMaxI();
6393 }
6394
6395 unsigned char alpha = splashRound(x: (clipToStrokePath) ? state->strokeAlpha * 255 : state->fillAlpha * 255);
6396 pipeInit(pipe: &pipe, x: 0, y: yMinI, pattern, cSrc: nullptr, aInput: alpha, usesShape: vectorAntialias && !hasBBox, nonIsolatedGroup: false);
6397
6398 // draw the spans
6399 if (vectorAntialias) {
6400 for (y = yMinI; y <= yMaxI; ++y) {
6401 scanner.renderAALine(aaBuf, x0: &x0, x1: &x1, y);
6402 if (clipRes != splashClipAllInside) {
6403 state->clip->clipAALine(aaBuf, x0: &x0, x1: &x1, y);
6404 }
6405#if splashAASize == 4
6406 if (!hasBBox && y > yMinI && y < yMaxI) {
6407 // correct shape on left side if clip is
6408 // vertical through the middle of shading:
6409 unsigned char *p0, *p1, *p2, *p3;
6410 unsigned char c1, c2, c3, c4;
6411 p0 = aaBuf->getDataPtr() + (x0 >> 1);
6412 p1 = p0 + aaBuf->getRowSize();
6413 p2 = p1 + aaBuf->getRowSize();
6414 p3 = p2 + aaBuf->getRowSize();
6415 if (x0 & 1) {
6416 c1 = (*p0 & 0x0f);
6417 c2 = (*p1 & 0x0f);
6418 c3 = (*p2 & 0x0f);
6419 c4 = (*p3 & 0x0f);
6420 } else {
6421 c1 = (*p0 >> 4);
6422 c2 = (*p1 >> 4);
6423 c3 = (*p2 >> 4);
6424 c4 = (*p3 >> 4);
6425 }
6426 if ((c1 & 0x03) == 0x03 && (c2 & 0x03) == 0x03 && (c3 & 0x03) == 0x03 && (c4 & 0x03) == 0x03 && c1 == c2 && c2 == c3 && c3 == c4 && pattern->testPosition(x: x0 - 1, y)) {
6427 unsigned char shapeCorrection = (x0 & 1) ? 0x0f : 0xf0;
6428 *p0 |= shapeCorrection;
6429 *p1 |= shapeCorrection;
6430 *p2 |= shapeCorrection;
6431 *p3 |= shapeCorrection;
6432 }
6433 // correct shape on right side if clip is
6434 // through the middle of shading:
6435 p0 = aaBuf->getDataPtr() + (x1 >> 1);
6436 p1 = p0 + aaBuf->getRowSize();
6437 p2 = p1 + aaBuf->getRowSize();
6438 p3 = p2 + aaBuf->getRowSize();
6439 if (x1 & 1) {
6440 c1 = (*p0 & 0x0f);
6441 c2 = (*p1 & 0x0f);
6442 c3 = (*p2 & 0x0f);
6443 c4 = (*p3 & 0x0f);
6444 } else {
6445 c1 = (*p0 >> 4);
6446 c2 = (*p1 >> 4);
6447 c3 = (*p2 >> 4);
6448 c4 = (*p3 >> 4);
6449 }
6450
6451 if ((c1 & 0xc) == 0x0c && (c2 & 0x0c) == 0x0c && (c3 & 0x0c) == 0x0c && (c4 & 0x0c) == 0x0c && c1 == c2 && c2 == c3 && c3 == c4 && pattern->testPosition(x: x1 + 1, y)) {
6452 unsigned char shapeCorrection = (x1 & 1) ? 0x0f : 0xf0;
6453 *p0 |= shapeCorrection;
6454 *p1 |= shapeCorrection;
6455 *p2 |= shapeCorrection;
6456 *p3 |= shapeCorrection;
6457 }
6458 }
6459#endif
6460 drawAALine(pipe: &pipe, x0, x1, y);
6461 }
6462 } else {
6463 SplashClipResult clipRes2;
6464 for (y = yMinI; y <= yMaxI; ++y) {
6465 SplashXPathScanIterator iterator(scanner, y);
6466 while (iterator.getNextSpan(x0: &x0, x1: &x1)) {
6467 if (clipRes == splashClipAllInside) {
6468 drawSpan(pipe: &pipe, x0, x1, y, noClip: true);
6469 } else {
6470 // limit the x range
6471 if (x0 < state->clip->getXMinI()) {
6472 x0 = state->clip->getXMinI();
6473 }
6474 if (x1 > state->clip->getXMaxI()) {
6475 x1 = state->clip->getXMaxI();
6476 }
6477 clipRes2 = state->clip->testSpan(spanXMin: x0, spanXMax: x1, spanY: y);
6478 drawSpan(pipe: &pipe, x0, x1, y, noClip: clipRes2 == splashClipAllInside);
6479 }
6480 }
6481 }
6482 }
6483 }
6484 opClipRes = clipRes;
6485
6486 return splashOk;
6487}
6488

source code of poppler/splash/Splash.cc