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