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. |
74 | static 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]. |
80 | static inline unsigned char clip255(int x) |
81 | { |
82 | return x < 0 ? 0 : x > 255 ? 255 : x; |
83 | } |
84 | |
85 | template<typename T> |
86 | inline 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 |
101 | static inline int imgCoordMungeLower(SplashCoord x) { |
102 | return splashCeil(x + 0.5) - 1; |
103 | } |
104 | static inline int imgCoordMungeUpper(SplashCoord x) { |
105 | return splashCeil(x + 0.5) - 1; |
106 | } |
107 | #else |
108 | static inline int imgCoordMungeLower(SplashCoord x) |
109 | { |
110 | return splashFloor(x); |
111 | } |
112 | static inline int imgCoordMungeUpper(SplashCoord x) |
113 | { |
114 | return splashFloor(x) + 1; |
115 | } |
116 | static inline int imgCoordMungeLowerC(SplashCoord x, bool glyphMode) |
117 | { |
118 | return glyphMode ? (splashCeil(x: x + 0.5) - 1) : splashFloor(x); |
119 | } |
120 | static 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. |
128 | struct 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 | |
145 | struct 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 | |
188 | SplashPipeResultColorCtrl Splash::pipeResultColorNoAlphaBlend[] = { splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendMono, splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendRGB, |
189 | splashPipeResultColorNoAlphaBlendRGB, splashPipeResultColorNoAlphaBlendCMYK, splashPipeResultColorNoAlphaBlendDeviceN }; |
190 | |
191 | SplashPipeResultColorCtrl Splash::pipeResultColorAlphaNoBlend[] = { splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendMono, splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendRGB, |
192 | splashPipeResultColorAlphaNoBlendRGB, splashPipeResultColorAlphaNoBlendCMYK, splashPipeResultColorAlphaNoBlendDeviceN }; |
193 | |
194 | SplashPipeResultColorCtrl Splash::pipeResultColorAlphaBlend[] = { splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendMono, splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendRGB, |
195 | splashPipeResultColorAlphaBlendRGB, splashPipeResultColorAlphaBlendCMYK, splashPipeResultColorAlphaBlendDeviceN }; |
196 | |
197 | //------------------------------------------------------------------------ |
198 | |
199 | static 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 | |
212 | inline void Splash::pipeInit(SplashPipe *pipe, int x, int y, SplashPattern *pattern, SplashColorPtr cSrc, unsigned char aInput, bool usesShape, bool nonIsolatedGroup, bool knockout, unsigned char knockoutOpacity) |
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 |
296 | void 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) { |
746 | void 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) { |
768 | void 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) { |
780 | void 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) { |
794 | void 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) { |
809 | void 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) { |
823 | void 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) { |
847 | void 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 |
868 | void 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 |
903 | void 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 |
939 | void 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 |
993 | void 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 |
1048 | void 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 |
1102 | void 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 |
1159 | void 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 | |
1204 | inline 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 | |
1245 | inline 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 | |
1283 | inline 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 | |
1295 | inline void Splash::drawAAPixelInit() |
1296 | { |
1297 | aaBufY = -1; |
1298 | } |
1299 | |
1300 | inline 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 | |
1351 | inline 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 | |
1378 | inline 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. |
1432 | inline 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 | |
1445 | Splash::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 | |
1467 | Splash::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 | |
1489 | Splash::~Splash() |
1490 | { |
1491 | while (state->next) { |
1492 | restoreState(); |
1493 | } |
1494 | delete state; |
1495 | delete aaBuf; |
1496 | } |
1497 | |
1498 | //------------------------------------------------------------------------ |
1499 | // state read |
1500 | //------------------------------------------------------------------------ |
1501 | |
1502 | SplashCoord *Splash::getMatrix() |
1503 | { |
1504 | return state->matrix; |
1505 | } |
1506 | |
1507 | SplashPattern *Splash::getStrokePattern() |
1508 | { |
1509 | return state->strokePattern; |
1510 | } |
1511 | |
1512 | SplashPattern *Splash::getFillPattern() |
1513 | { |
1514 | return state->fillPattern; |
1515 | } |
1516 | |
1517 | SplashScreen *Splash::getScreen() |
1518 | { |
1519 | return state->screen; |
1520 | } |
1521 | |
1522 | SplashBlendFunc Splash::getBlendFunc() |
1523 | { |
1524 | return state->blendFunc; |
1525 | } |
1526 | |
1527 | SplashCoord Splash::getStrokeAlpha() |
1528 | { |
1529 | return state->strokeAlpha; |
1530 | } |
1531 | |
1532 | SplashCoord Splash::getFillAlpha() |
1533 | { |
1534 | return state->fillAlpha; |
1535 | } |
1536 | |
1537 | SplashCoord Splash::getLineWidth() |
1538 | { |
1539 | return state->lineWidth; |
1540 | } |
1541 | |
1542 | int Splash::getLineCap() |
1543 | { |
1544 | return state->lineCap; |
1545 | } |
1546 | |
1547 | int Splash::getLineJoin() |
1548 | { |
1549 | return state->lineJoin; |
1550 | } |
1551 | |
1552 | SplashCoord Splash::getMiterLimit() |
1553 | { |
1554 | return state->miterLimit; |
1555 | } |
1556 | |
1557 | SplashCoord Splash::getFlatness() |
1558 | { |
1559 | return state->flatness; |
1560 | } |
1561 | |
1562 | SplashCoord Splash::getLineDashPhase() |
1563 | { |
1564 | return state->lineDashPhase; |
1565 | } |
1566 | |
1567 | bool Splash::getStrokeAdjust() |
1568 | { |
1569 | return state->strokeAdjust; |
1570 | } |
1571 | |
1572 | SplashClip *Splash::getClip() |
1573 | { |
1574 | return state->clip; |
1575 | } |
1576 | |
1577 | SplashBitmap *Splash::getSoftMask() |
1578 | { |
1579 | return state->softMask; |
1580 | } |
1581 | |
1582 | bool Splash::getInNonIsolatedGroup() |
1583 | { |
1584 | return state->inNonIsolatedGroup; |
1585 | } |
1586 | |
1587 | //------------------------------------------------------------------------ |
1588 | // state write |
1589 | //------------------------------------------------------------------------ |
1590 | |
1591 | void Splash::setMatrix(SplashCoord *matrix) |
1592 | { |
1593 | memcpy(dest: state->matrix, src: matrix, n: 6 * sizeof(SplashCoord)); |
1594 | } |
1595 | |
1596 | void Splash::setStrokePattern(SplashPattern *strokePattern) |
1597 | { |
1598 | state->setStrokePattern(strokePattern); |
1599 | } |
1600 | |
1601 | void Splash::setFillPattern(SplashPattern *fillPattern) |
1602 | { |
1603 | state->setFillPattern(fillPattern); |
1604 | } |
1605 | |
1606 | void Splash::setScreen(SplashScreen *screen) |
1607 | { |
1608 | state->setScreen(screen); |
1609 | } |
1610 | |
1611 | void Splash::setBlendFunc(SplashBlendFunc func) |
1612 | { |
1613 | state->blendFunc = func; |
1614 | } |
1615 | |
1616 | void Splash::setStrokeAlpha(SplashCoord alpha) |
1617 | { |
1618 | state->strokeAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternStrokeAlpha : alpha; |
1619 | } |
1620 | |
1621 | void Splash::setFillAlpha(SplashCoord alpha) |
1622 | { |
1623 | state->fillAlpha = (state->multiplyPatternAlpha) ? alpha * state->patternFillAlpha : alpha; |
1624 | } |
1625 | |
1626 | void Splash::setPatternAlpha(SplashCoord strokeAlpha, SplashCoord fillAlpha) |
1627 | { |
1628 | state->patternStrokeAlpha = strokeAlpha; |
1629 | state->patternFillAlpha = fillAlpha; |
1630 | state->multiplyPatternAlpha = true; |
1631 | } |
1632 | |
1633 | void Splash::clearPatternAlpha() |
1634 | { |
1635 | state->patternStrokeAlpha = 1; |
1636 | state->patternFillAlpha = 1; |
1637 | state->multiplyPatternAlpha = false; |
1638 | } |
1639 | |
1640 | void Splash::setFillOverprint(bool fop) |
1641 | { |
1642 | state->fillOverprint = fop; |
1643 | } |
1644 | |
1645 | void Splash::setStrokeOverprint(bool sop) |
1646 | { |
1647 | state->strokeOverprint = sop; |
1648 | } |
1649 | |
1650 | void Splash::setOverprintMode(int opm) |
1651 | { |
1652 | state->overprintMode = opm; |
1653 | } |
1654 | |
1655 | void Splash::setLineWidth(SplashCoord lineWidth) |
1656 | { |
1657 | state->lineWidth = lineWidth; |
1658 | } |
1659 | |
1660 | void Splash::setLineCap(int lineCap) |
1661 | { |
1662 | state->lineCap = lineCap; |
1663 | } |
1664 | |
1665 | void Splash::setLineJoin(int lineJoin) |
1666 | { |
1667 | state->lineJoin = lineJoin; |
1668 | } |
1669 | |
1670 | void Splash::setMiterLimit(SplashCoord miterLimit) |
1671 | { |
1672 | state->miterLimit = miterLimit; |
1673 | } |
1674 | |
1675 | void Splash::setFlatness(SplashCoord flatness) |
1676 | { |
1677 | if (flatness < 1) { |
1678 | state->flatness = 1; |
1679 | } else { |
1680 | state->flatness = flatness; |
1681 | } |
1682 | } |
1683 | |
1684 | void Splash::setLineDash(std::vector<SplashCoord> &&lineDash, SplashCoord lineDashPhase) |
1685 | { |
1686 | state->setLineDash(lineDashA: std::move(lineDash), lineDashPhaseA: lineDashPhase); |
1687 | } |
1688 | |
1689 | void Splash::setStrokeAdjust(bool strokeAdjust) |
1690 | { |
1691 | state->strokeAdjust = strokeAdjust; |
1692 | } |
1693 | |
1694 | void Splash::clipResetToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) |
1695 | { |
1696 | state->clip->resetToRect(x0, y0, x1, y1); |
1697 | } |
1698 | |
1699 | SplashError Splash::clipToRect(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1) |
1700 | { |
1701 | return state->clip->clipToRect(x0, y0, x1, y1); |
1702 | } |
1703 | |
1704 | SplashError Splash::clipToPath(SplashPath *path, bool eo) |
1705 | { |
1706 | return state->clip->clipToPath(path, matrix: state->matrix, flatness: state->flatness, eo); |
1707 | } |
1708 | |
1709 | void Splash::setSoftMask(SplashBitmap *softMask) |
1710 | { |
1711 | state->setSoftMask(softMask); |
1712 | } |
1713 | |
1714 | void Splash::setInNonIsolatedGroup(SplashBitmap *alpha0BitmapA, int alpha0XA, int alpha0YA) |
1715 | { |
1716 | alpha0Bitmap = alpha0BitmapA; |
1717 | alpha0X = alpha0XA; |
1718 | alpha0Y = alpha0YA; |
1719 | state->inNonIsolatedGroup = true; |
1720 | } |
1721 | |
1722 | void Splash::setTransfer(unsigned char *red, unsigned char *green, unsigned char *blue, unsigned char *gray) |
1723 | { |
1724 | state->setTransfer(red, green, blue, gray); |
1725 | } |
1726 | |
1727 | void 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 | |
1737 | void Splash::saveState() |
1738 | { |
1739 | SplashState *newState; |
1740 | |
1741 | newState = state->copy(); |
1742 | newState->next = state; |
1743 | state = newState; |
1744 | } |
1745 | |
1746 | SplashError 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 | |
1763 | void 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 | |
1886 | SplashError 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 | |
1945 | void 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 | |
2034 | void 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 | |
2043 | SplashPath *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 | |
2074 | void Splash::flattenCurve(SplashCoord x0, SplashCoord y0, SplashCoord x1, SplashCoord y1, SplashCoord x2, SplashCoord y2, SplashCoord x3, SplashCoord y3, SplashCoord *matrix, SplashCoord flatness2, SplashPath *fPath) |
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 | |
2162 | SplashPath *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 | |
2282 | SplashError 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 | |
2291 | inline 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 | |
2324 | SplashError 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 | |
2455 | bool 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 | |
2522 | SplashError 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 | |
2575 | SplashError 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 | |
2603 | void 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 | |
2618 | void 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 | |
2740 | SplashError 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 | |
2826 | void 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. |
3090 | SplashBitmap *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 | |
3111 | void 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 | |
3200 | void 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 | |
3289 | void 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 | |
3373 | void 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 | |
3458 | void 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 | |
3512 | SplashError 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 | |
3652 | SplashError 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 |
3963 | static 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. |
3978 | SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, bool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, bool interpolate, bool tilingPattern) |
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 | |
4013 | bool 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 | |
4266 | bool 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 | |
4436 | bool 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 | |
4601 | bool 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 |
4766 | static 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 |
4791 | bool 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 | |
4957 | void 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 | |
4985 | void 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 | |
4993 | void 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 | |
5073 | void 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 | |
5134 | SplashError Splash::composite(SplashBitmap *src, int xSrc, int ySrc, int xDest, int yDest, int w, int h, bool noClip, bool nonIsolated, bool knockout, SplashCoord knockoutOpacity) |
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 | |
5216 | void 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 | |
5368 | bool 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 | |
5824 | SplashError 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 | |
5955 | SplashPath *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 | |
6333 | void 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 | |
6343 | void 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 | |
6353 | SplashError 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 | |