Coverage Report

Created: 2025-09-27 07:50

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