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