Coverage Report

Created: 2026-10-14 06:52

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