Coverage Report

Created: 2026-07-30 07:17

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