Coverage Report

Created: 2026-08-25 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/poppler/poppler/GfxState.cc
Line
Count
Source
1
//========================================================================
2
//
3
// GfxState.cc
4
//
5
// Copyright 1996-2003 Glyph & Cog, LLC
6
//
7
//========================================================================
8
9
//========================================================================
10
//
11
// Modified under the Poppler project - http://poppler.freedesktop.org
12
//
13
// All changes made under the Poppler project to this file are licensed
14
// under GPL version 2 or later
15
//
16
// Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17
// Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net>
18
// Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
19
// Copyright (C) 2006-2022, 2024-2026 Albert Astals Cid <aacid@kde.org>
20
// Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp>
21
// Copyright (C) 2009, 2011-2016, 2020, 2023 Thomas Freitag <Thomas.Freitag@alfa.de>
22
// Copyright (C) 2009, 2019 Christian Persch <chpe@gnome.org>
23
// Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com>
24
// Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
25
// Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
26
// Copyright (C) 2012, 2020 William Bader <williambader@hotmail.com>
27
// Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
28
// Copyright (C) 2013 Hib Eris <hib@hiberis.nl>
29
// Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it>
30
// Copyright (C) 2015, 2020 Adrian Johnson <ajohnson@redneon.com>
31
// Copyright (C) 2016 Marek Kasik <mkasik@redhat.com>
32
// Copyright (C) 2017, 2019, 2022 Oliver Sander <oliver.sander@tu-dresden.de>
33
// Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, <info@kdab.com>. Work sponsored by the LiMux project of the city of Munich
34
// Copyright (C) 2018 Volker Krause <vkrause@kde.org>
35
// Copyright (C) 2018, 2019 Adam Reichold <adam.reichold@t-online.de>
36
// Copyright (C) 2019 LE GARREC Vincent <legarrec.vincent@gmail.com>
37
// Copyright (C) 2020, 2021, 2026 Philipp Knechtges <philipp-dev@knechtges.com>
38
// Copyright (C) 2020 Lluís Batlle i Rossell <viric@viric.name>
39
// Copyright (C) 2024 Athul Raj Kollareth <krathul3152@gmail.com>
40
// Copyright (C) 2024 Nelson Benítez León <nbenitezl@gmail.com>
41
// Copyright (C) 2025, 2026 g10 Code GmbH, Author: Sune Stolborg Vuorela <sune@vuorela.dk>
42
// Copyright (C) 2025 Trystan Mata <trystan.mata@tytanium.xyz>
43
// Copyright (C) 2025 Arnav V <arnav0872@gmail.com>
44
// Copyright (C) 2026 Adam Sampson <ats@offog.org>
45
// Copyright (C) 2026 Stefan Brüns <stefan.bruens@rwth-aachen.de>
46
//
47
// To see a description of the changes please see the Changelog file that
48
// came with your tarball or type make ChangeLog if you are building from git
49
//
50
//========================================================================
51
52
#include <config.h>
53
54
#include <algorithm>
55
#include <memory>
56
#include <cstddef>
57
#include <cmath>
58
#include <cstring>
59
#include "goo/gmem.h"
60
#include "Error.h"
61
#include "Object.h"
62
#include "Array.h"
63
#include "Page.h"
64
#include "Gfx.h"
65
#include "GfxState.h"
66
#include "GfxState_helpers.h"
67
#include "GfxFont.h"
68
#include "GlobalParams.h"
69
#include "OutputDev.h"
70
#include "Stream.h"
71
#include "splash/SplashTypes.h"
72
73
//------------------------------------------------------------------------
74
75
// Max depth of nested color spaces.  This is used to catch infinite
76
// loops in the color space object structure.
77
constexpr int colorSpaceRecursionLimit = 8;
78
79
//------------------------------------------------------------------------
80
81
bool Matrix::invertTo(Matrix *other) const
82
0
{
83
0
    const double det_denominator = determinant();
84
0
    if (unlikely(det_denominator == 0)) {
85
0
        *other = { 1, 0, 0, 1, 0, 0 };
86
0
        return false;
87
0
    }
88
89
0
    const double det = 1 / det_denominator;
90
0
    other->m[0] = m[3] * det;
91
0
    other->m[1] = -m[1] * det;
92
0
    other->m[2] = -m[2] * det;
93
0
    other->m[3] = m[0] * det;
94
0
    other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det;
95
0
    other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det;
96
97
0
    return true;
98
0
}
99
100
void Matrix::translate(double tx, double ty)
101
0
{
102
0
    double x0 = tx * m[0] + ty * m[2] + m[4];
103
0
    double y0 = tx * m[1] + ty * m[3] + m[5];
104
0
    m[4] = x0;
105
0
    m[5] = y0;
106
0
}
107
108
void Matrix::scale(double sx, double sy)
109
0
{
110
0
    m[0] *= sx;
111
0
    m[1] *= sx;
112
0
    m[2] *= sy;
113
0
    m[3] *= sy;
114
0
}
115
116
void Matrix::transform(double x, double y, double *tx, double *ty) const
117
0
{
118
0
    double temp_x, temp_y;
119
120
0
    temp_x = m[0] * x + m[2] * y + m[4];
121
0
    temp_y = m[1] * x + m[3] * y + m[5];
122
123
0
    *tx = temp_x;
124
0
    *ty = temp_y;
125
0
}
126
127
// Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
128
double Matrix::norm() const
129
0
{
130
0
    double f, g, h, i, j;
131
132
0
    i = m[0] * m[0] + m[1] * m[1];
133
0
    j = m[2] * m[2] + m[3] * m[3];
134
135
0
    f = 0.5 * (i + j);
136
0
    g = 0.5 * (i - j);
137
0
    h = m[0] * m[2] + m[1] * m[3];
138
139
0
    return sqrt(f + hypot(g, h));
140
0
}
141
142
//------------------------------------------------------------------------
143
144
struct GfxBlendModeInfo
145
{
146
    const char *name;
147
    GfxBlendMode mode;
148
};
149
150
static const GfxBlendModeInfo gfxBlendModeNames[] = { { .name = "Normal", .mode = gfxBlendNormal },         { .name = "Compatible", .mode = gfxBlendNormal },
151
                                                      { .name = "Multiply", .mode = gfxBlendMultiply },     { .name = "Screen", .mode = gfxBlendScreen },
152
                                                      { .name = "Overlay", .mode = gfxBlendOverlay },       { .name = "Darken", .mode = gfxBlendDarken },
153
                                                      { .name = "Lighten", .mode = gfxBlendLighten },       { .name = "ColorDodge", .mode = gfxBlendColorDodge },
154
                                                      { .name = "ColorBurn", .mode = gfxBlendColorBurn },   { .name = "HardLight", .mode = gfxBlendHardLight },
155
                                                      { .name = "SoftLight", .mode = gfxBlendSoftLight },   { .name = "Difference", .mode = gfxBlendDifference },
156
                                                      { .name = "Exclusion", .mode = gfxBlendExclusion },   { .name = "Hue", .mode = gfxBlendHue },
157
                                                      { .name = "Saturation", .mode = gfxBlendSaturation }, { .name = "Color", .mode = gfxBlendColor },
158
                                                      { .name = "Luminosity", .mode = gfxBlendLuminosity } };
159
160
0
#define nGfxBlendModeNames ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
161
162
//------------------------------------------------------------------------
163
//
164
// NB: This must match the GfxColorSpaceMode enum defined in
165
// GfxState.h
166
static const char *gfxColorSpaceModeNames[] = { "DeviceGray", "CalGray", "DeviceRGB", "CalRGB", "DeviceCMYK", "Lab", "ICCBased", "Indexed", "Separation", "DeviceN", "Pattern", "DeviceRGBA" };
167
168
0
#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
169
170
#if USE_CMS
171
172
static const std::map<unsigned int, unsigned int>::size_type CMSCACHE_LIMIT = 2048;
173
174
#    include <lcms2.h>
175
#    define LCMS_FLAGS (cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION)
176
177
static void lcmsprofiledeleter(void *profile)
178
{
179
    cmsCloseProfile(profile);
180
}
181
182
GfxLCMSProfilePtr make_GfxLCMSProfilePtr(void *profile)
183
{
184
    if (profile == nullptr) {
185
        return GfxLCMSProfilePtr();
186
    }
187
    return GfxLCMSProfilePtr(profile, lcmsprofiledeleter);
188
}
189
190
void GfxColorTransform::doTransform(void *in, void *out, unsigned int size)
191
{
192
    cmsDoTransform(transform, in, out, size);
193
}
194
195
// transformA should be a cmsHTRANSFORM
196
GfxColorTransform::GfxColorTransform(void *transformA, int cmsIntentA, unsigned int inputPixelTypeA, unsigned int transformPixelTypeA)
197
{
198
    transform = transformA;
199
    cmsIntent = cmsIntentA;
200
    inputPixelType = inputPixelTypeA;
201
    transformPixelType = transformPixelTypeA;
202
}
203
204
GfxColorTransform::~GfxColorTransform()
205
{
206
    cmsDeleteTransform(transform);
207
}
208
209
// convert color space signature to cmsColor type
210
static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs);
211
static unsigned int getCMSNChannels(cmsColorSpaceSignature cs);
212
213
#endif
214
215
//------------------------------------------------------------------------
216
// GfxColorSpace
217
//------------------------------------------------------------------------
218
219
GfxColorSpace::GfxColorSpace()
220
136k
{
221
136k
    overprintMask = 0x0f;
222
136k
}
223
224
136k
GfxColorSpace::~GfxColorSpace() = default;
225
226
std::unique_ptr<GfxColorSpace> GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion)
227
487
{
228
487
    Object obj1;
229
230
487
    if (recursion > colorSpaceRecursionLimit) {
231
0
        error(errSyntaxError, -1, "Loop detected in color space objects");
232
0
        return {};
233
0
    }
234
235
487
    if (csObj->isName()) {
236
478
        const std::string &csName = csObj->getNameString();
237
478
        if (csName == "DeviceGray" || csName == "G") {
238
0
            if (res != nullptr) {
239
0
                Object objCS = res->lookupColorSpace("DefaultGray");
240
0
                if (objCS.isNull()) {
241
0
                    return state->copyDefaultGrayColorSpace();
242
0
                }
243
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
244
0
            }
245
0
            return state->copyDefaultGrayColorSpace();
246
0
        }
247
478
        if (csName == "DeviceRGB" || csName == "RGB") {
248
418
            if (res != nullptr) {
249
418
                Object objCS = res->lookupColorSpace("DefaultRGB");
250
418
                if (objCS.isNull()) {
251
418
                    return state->copyDefaultRGBColorSpace();
252
418
                }
253
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
254
418
            }
255
0
            return state->copyDefaultRGBColorSpace();
256
418
        }
257
60
        if (csName == "DeviceCMYK" || csName == "CMYK") {
258
0
            if (res != nullptr) {
259
0
                Object objCS = res->lookupColorSpace("DefaultCMYK");
260
0
                if (objCS.isNull()) {
261
0
                    return state->copyDefaultCMYKColorSpace();
262
0
                }
263
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
264
0
            }
265
0
            return state->copyDefaultCMYKColorSpace();
266
0
        }
267
60
        if (csName == "Pattern") {
268
0
            return std::make_unique<GfxPatternColorSpace>(nullptr);
269
0
        }
270
60
        error(errSyntaxWarning, -1, "Bad color space '{0:r}'", &csName);
271
272
60
    } else if (csObj->isArrayOfLengthAtLeast(1)) {
273
0
        obj1 = csObj->arrayGet(0);
274
0
        if (obj1.isName("DeviceGray") || obj1.isName("G")) {
275
0
            if (res != nullptr) {
276
0
                Object objCS = res->lookupColorSpace("DefaultGray");
277
0
                if (objCS.isNull()) {
278
0
                    return state->copyDefaultGrayColorSpace();
279
0
                }
280
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
281
0
            }
282
0
            return state->copyDefaultGrayColorSpace();
283
0
        }
284
0
        if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
285
0
            if (res != nullptr) {
286
0
                Object objCS = res->lookupColorSpace("DefaultRGB");
287
0
                if (objCS.isNull()) {
288
0
                    return state->copyDefaultRGBColorSpace();
289
0
                }
290
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
291
0
            }
292
0
            return state->copyDefaultRGBColorSpace();
293
0
        }
294
0
        if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
295
0
            if (res != nullptr) {
296
0
                Object objCS = res->lookupColorSpace("DefaultCMYK");
297
0
                if (objCS.isNull()) {
298
0
                    return state->copyDefaultCMYKColorSpace();
299
0
                }
300
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
301
0
            }
302
0
            return state->copyDefaultCMYKColorSpace();
303
0
        }
304
0
        if (obj1.isName("CalGray")) {
305
0
            return GfxCalGrayColorSpace::parse(*csObj->getArray(), state);
306
0
        }
307
0
        if (obj1.isName("CalRGB")) {
308
0
            return GfxCalRGBColorSpace::parse(*csObj->getArray(), state);
309
0
        }
310
0
        if (obj1.isName("Lab")) {
311
0
            return GfxLabColorSpace::parse(*csObj->getArray(), state);
312
0
        }
313
0
        if (obj1.isName("ICCBased")) {
314
0
            return GfxICCBasedColorSpace::parse(*csObj->getArray(), out, state, recursion);
315
0
        }
316
0
        if (obj1.isName("Indexed") || obj1.isName("I")) {
317
0
            return GfxIndexedColorSpace::parse(res, *csObj->getArray(), out, state, recursion);
318
0
        }
319
0
        if (obj1.isName("Separation")) {
320
0
            return GfxSeparationColorSpace::parse(res, *csObj->getArray(), out, state, recursion);
321
0
        }
322
0
        if (obj1.isName("DeviceN")) {
323
0
            return GfxDeviceNColorSpace::parse(res, *csObj->getArray(), out, state, recursion);
324
0
        }
325
0
        if (obj1.isName("Pattern")) {
326
0
            return GfxPatternColorSpace::parse(res, *csObj->getArray(), out, state, recursion);
327
0
        }
328
0
        error(errSyntaxWarning, -1, "Bad color space");
329
330
9
    } else if (csObj->isDict()) {
331
0
        obj1 = csObj->dictLookup("ColorSpace");
332
0
        if (obj1.isName("DeviceGray")) {
333
0
            if (res != nullptr) {
334
0
                Object objCS = res->lookupColorSpace("DefaultGray");
335
0
                if (objCS.isNull()) {
336
0
                    return state->copyDefaultGrayColorSpace();
337
0
                }
338
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
339
0
            }
340
0
            return state->copyDefaultGrayColorSpace();
341
0
        }
342
0
        if (obj1.isName("DeviceRGB")) {
343
0
            if (res != nullptr) {
344
0
                Object objCS = res->lookupColorSpace("DefaultRGB");
345
0
                if (objCS.isNull()) {
346
0
                    return state->copyDefaultRGBColorSpace();
347
0
                }
348
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
349
0
            }
350
0
            return state->copyDefaultRGBColorSpace();
351
0
        }
352
0
        if (obj1.isName("DeviceCMYK")) {
353
0
            if (res != nullptr) {
354
0
                Object objCS = res->lookupColorSpace("DefaultCMYK");
355
0
                if (objCS.isNull()) {
356
0
                    return state->copyDefaultCMYKColorSpace();
357
0
                }
358
0
                return GfxColorSpace::parse(nullptr, &objCS, out, state);
359
0
            }
360
0
            return state->copyDefaultCMYKColorSpace();
361
0
        }
362
0
        error(errSyntaxWarning, -1, "Bad color space dict'");
363
364
9
    } else {
365
9
        error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict");
366
9
    }
367
69
    return {};
368
487
}
369
370
0
void GfxColorSpace::createMapping(std::vector<std::unique_ptr<GfxSeparationColorSpace>> * /*separationList*/, size_t /*maxSepComps*/) { }
371
372
void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int /*maxImgPixel*/) const
373
0
{
374
0
    int i;
375
376
0
    for (i = 0; i < getNComps(); ++i) {
377
0
        decodeLow[i] = 0;
378
0
        decodeRange[i] = 1;
379
0
    }
380
0
}
381
382
int GfxColorSpace::getNumColorSpaceModes()
383
0
{
384
0
    return nGfxColorSpaceModes;
385
0
}
386
387
const char *GfxColorSpace::getColorSpaceModeName(int idx)
388
0
{
389
0
    return gfxColorSpaceModeNames[idx];
390
0
}
391
392
#if USE_CMS
393
394
static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text)
395
{
396
    error(errSyntaxWarning, -1, "{0:s}", text);
397
}
398
399
static void setCMSErrorHandler()
400
{
401
    static bool installed = false;
402
403
    if (!installed) {
404
        cmsSetLogErrorHandler(CMSError);
405
        installed = true;
406
    }
407
}
408
409
unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs)
410
{
411
    switch (cs) {
412
    case cmsSigXYZData:
413
        return PT_XYZ;
414
        break;
415
    case cmsSigLabData:
416
        return PT_Lab;
417
        break;
418
    case cmsSigLuvData:
419
        return PT_YUV;
420
        break;
421
    case cmsSigYCbCrData:
422
        return PT_YCbCr;
423
        break;
424
    case cmsSigYxyData:
425
        return PT_Yxy;
426
        break;
427
    case cmsSigRgbData:
428
        return PT_RGB;
429
        break;
430
    case cmsSigGrayData:
431
        return PT_GRAY;
432
        break;
433
    case cmsSigHsvData:
434
        return PT_HSV;
435
        break;
436
    case cmsSigHlsData:
437
        return PT_HLS;
438
        break;
439
    case cmsSigCmykData:
440
        return PT_CMYK;
441
        break;
442
    case cmsSigCmyData:
443
        return PT_CMY;
444
        break;
445
    case cmsSig2colorData:
446
    case cmsSig3colorData:
447
    case cmsSig4colorData:
448
    case cmsSig5colorData:
449
    case cmsSig6colorData:
450
    case cmsSig7colorData:
451
    case cmsSig8colorData:
452
    case cmsSig9colorData:
453
    case cmsSig10colorData:
454
    case cmsSig11colorData:
455
    case cmsSig12colorData:
456
    case cmsSig13colorData:
457
    case cmsSig14colorData:
458
    case cmsSig15colorData:
459
    default:
460
        break;
461
    }
462
    return PT_RGB;
463
}
464
465
unsigned int getCMSNChannels(cmsColorSpaceSignature cs)
466
{
467
    switch (cs) {
468
    case cmsSigXYZData:
469
    case cmsSigLuvData:
470
    case cmsSigLabData:
471
    case cmsSigYCbCrData:
472
    case cmsSigYxyData:
473
    case cmsSigRgbData:
474
    case cmsSigHsvData:
475
    case cmsSigHlsData:
476
    case cmsSigCmyData:
477
    case cmsSig3colorData:
478
        return 3;
479
        break;
480
    case cmsSigGrayData:
481
        return 1;
482
        break;
483
    case cmsSigCmykData:
484
    case cmsSig4colorData:
485
        return 4;
486
        break;
487
    case cmsSig2colorData:
488
        return 2;
489
        break;
490
    case cmsSig5colorData:
491
        return 5;
492
        break;
493
    case cmsSig6colorData:
494
        return 6;
495
        break;
496
    case cmsSig7colorData:
497
        return 7;
498
        break;
499
    case cmsSig8colorData:
500
        return 8;
501
        break;
502
    case cmsSig9colorData:
503
        return 9;
504
        break;
505
    case cmsSig10colorData:
506
        return 10;
507
        break;
508
    case cmsSig11colorData:
509
        return 11;
510
        break;
511
    case cmsSig12colorData:
512
        return 12;
513
        break;
514
    case cmsSig13colorData:
515
        return 13;
516
        break;
517
    case cmsSig14colorData:
518
        return 14;
519
        break;
520
    case cmsSig15colorData:
521
        return 15;
522
    default:
523
        break;
524
    }
525
    return 3;
526
}
527
#endif
528
529
//------------------------------------------------------------------------
530
// GfxDeviceGrayColorSpace
531
//------------------------------------------------------------------------
532
533
70.1k
GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() = default;
534
535
GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() = default;
536
537
std::unique_ptr<GfxColorSpace> GfxDeviceGrayColorSpace::copy() const
538
60.9k
{
539
60.9k
    return std::make_unique<GfxDeviceGrayColorSpace>();
540
60.9k
}
541
542
void GfxDeviceGrayColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
543
0
{
544
0
    *gray = clip01(color.c[0]);
545
0
}
546
547
void GfxDeviceGrayColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length)
548
0
{
549
0
    memcpy(out, in, length);
550
0
}
551
552
void GfxDeviceGrayColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
553
7.59k
{
554
7.59k
    rgb->r = rgb->g = rgb->b = clip01(color.c[0]);
555
7.59k
}
556
557
void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length)
558
0
{
559
0
    int i;
560
561
0
    for (i = 0; i < length; i++) {
562
0
        out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0);
563
0
    }
564
0
}
565
566
void GfxDeviceGrayColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
567
0
{
568
0
    for (int i = 0; i < length; i++) {
569
0
        *out++ = in[i];
570
0
        *out++ = in[i];
571
0
        *out++ = in[i];
572
0
    }
573
0
}
574
575
void GfxDeviceGrayColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length)
576
0
{
577
0
    for (int i = 0; i < length; i++) {
578
0
        *out++ = in[i];
579
0
        *out++ = in[i];
580
0
        *out++ = in[i];
581
0
        *out++ = 255;
582
0
    }
583
0
}
584
585
void GfxDeviceGrayColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
586
0
{
587
0
    for (int i = 0; i < length; i++) {
588
0
        *out++ = 0;
589
0
        *out++ = 0;
590
0
        *out++ = 0;
591
0
        *out++ = in[i];
592
0
    }
593
0
}
594
595
void GfxDeviceGrayColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
596
0
{
597
0
    for (int i = 0; i < length; i++) {
598
0
        for (int j = 0; j < SPOT_NCOMPS + 4; j++) {
599
0
            out[j] = 0;
600
0
        }
601
0
        out[4] = in[i];
602
0
        out += (SPOT_NCOMPS + 4);
603
0
    }
604
0
}
605
606
void GfxDeviceGrayColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
607
0
{
608
0
    cmyk->c = cmyk->m = cmyk->y = 0;
609
0
    cmyk->k = clip01(gfxColorComp1 - color.c[0]);
610
0
}
611
612
void GfxDeviceGrayColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
613
0
{
614
0
    clearGfxColor(deviceN);
615
0
    deviceN->c[3] = clip01(gfxColorComp1 - color.c[0]);
616
0
}
617
618
void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) const
619
0
{
620
0
    color->c[0] = 0;
621
0
}
622
623
//------------------------------------------------------------------------
624
// GfxCalGrayColorSpace
625
//------------------------------------------------------------------------
626
627
GfxCalGrayColorSpace::GfxCalGrayColorSpace()
628
0
{
629
0
    whiteX = whiteY = whiteZ = 1;
630
0
    blackX = blackY = blackZ = 0;
631
0
    gamma = 1;
632
0
}
633
634
GfxCalGrayColorSpace::~GfxCalGrayColorSpace() = default;
635
636
std::unique_ptr<GfxColorSpace> GfxCalGrayColorSpace::copy() const
637
0
{
638
0
    auto cs = std::make_unique<GfxCalGrayColorSpace>();
639
0
    cs->whiteX = whiteX;
640
0
    cs->whiteY = whiteY;
641
0
    cs->whiteZ = whiteZ;
642
0
    cs->blackX = blackX;
643
0
    cs->blackY = blackY;
644
0
    cs->blackZ = blackZ;
645
0
    cs->gamma = gamma;
646
#if USE_CMS
647
    cs->transform = transform;
648
#endif
649
0
    return cs;
650
0
}
651
652
// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
653
// Language Reference, Third Edition.
654
static const double xyzrgb[3][3] = { { 3.240449, -1.537136, -0.498531 }, { -0.969265, 1.876011, 0.041556 }, { 0.055643, -0.204026, 1.057229 } };
655
656
// From the same reference as above, the inverse of the DecodeLMN function.
657
// This is essentially the gamma function of the sRGB profile.
658
static double srgb_gamma_function(double x)
659
0
{
660
    // 0.04045 is what lcms2 uses, but the PS Reference Example 4.10 specifies 0.03928???
661
    // if (x <= 0.04045 / 12.92321) {
662
0
    if (x <= 0.03928 / 12.92321) {
663
0
        return x * 12.92321;
664
0
    }
665
0
    return 1.055 * pow(x, 1.0 / 2.4) - 0.055;
666
0
}
667
668
// D65 is the white point of the sRGB profile as it is specified above in the xyzrgb array
669
static const double white_d65_X = 0.9505;
670
static const double white_d65_Y = 1.0;
671
static const double white_d65_Z = 1.0890;
672
673
#if USE_CMS
674
// D50 is the default white point as used in ICC profiles and in the lcms2 library
675
static const double white_d50_X = 0.96422;
676
static const double white_d50_Y = 1.0;
677
static const double white_d50_Z = 0.82521;
678
679
static void inline bradford_transform_to_d50(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ)
680
{
681
    if (source_whiteX == white_d50_X && source_whiteY == white_d50_Y && source_whiteZ == white_d50_Z) {
682
        // early exit if noop
683
        return;
684
    }
685
    // at first apply Bradford matrix
686
    double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z;
687
    double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z;
688
    double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z;
689
690
    // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point
691
    rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ;
692
    gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ;
693
    beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ;
694
695
    // now revert the two steps above, but substituting the source white point by the device white point (D50)
696
    // Since the white point is known a priori this has been combined into a single operation.
697
    X = 0.98332566 * rho_in - 0.15005819 * gamma_in + 0.13095252 * beta_in;
698
    Y = 0.43069901 * rho_in + 0.52894900 * gamma_in + 0.04035199 * beta_in;
699
    Z = 0.00849698 * rho_in + 0.04086079 * gamma_in + 0.79284618 * beta_in;
700
}
701
#endif
702
703
static void inline bradford_transform_to_d65(double &X, double &Y, double &Z, const double source_whiteX, const double source_whiteY, const double source_whiteZ)
704
0
{
705
0
    if (source_whiteX == white_d65_X && source_whiteY == white_d65_Y && source_whiteZ == white_d65_Z) {
706
        // early exit if noop
707
0
        return;
708
0
    }
709
    // at first apply Bradford matrix
710
0
    double rho_in = 0.8951000 * X + 0.2664000 * Y - 0.1614000 * Z;
711
0
    double gamma_in = -0.7502000 * X + 1.7135000 * Y + 0.0367000 * Z;
712
0
    double beta_in = 0.0389000 * X - 0.0685000 * Y + 1.0296000 * Z;
713
714
    // apply a diagonal matrix with the diagonal entries being the inverse bradford-transformed white point
715
0
    rho_in /= 0.8951000 * source_whiteX + 0.2664000 * source_whiteY - 0.1614000 * source_whiteZ;
716
0
    gamma_in /= -0.7502000 * source_whiteX + 1.7135000 * source_whiteY + 0.0367000 * source_whiteZ;
717
0
    beta_in /= 0.0389000 * source_whiteX - 0.0685000 * source_whiteY + 1.0296000 * source_whiteZ;
718
719
    // now revert the two steps above, but substituting the source white point by the device white point (D65)
720
    // Since the white point is known a priori this has been combined into a single operation.
721
0
    X = 0.92918329 * rho_in - 0.15299782 * gamma_in + 0.17428453 * beta_in;
722
0
    Y = 0.40698452 * rho_in + 0.53931108 * gamma_in + 0.05370440 * beta_in;
723
0
    Z = -0.00802913 * rho_in + 0.04166125 * gamma_in + 1.05519788 * beta_in;
724
0
}
725
726
std::unique_ptr<GfxColorSpace> GfxCalGrayColorSpace::parse(const Array &arr, GfxState *state)
727
0
{
728
0
    Object obj1, obj2;
729
730
0
    obj1 = arr.get(1);
731
0
    if (!obj1.isDict()) {
732
0
        error(errSyntaxWarning, -1, "Bad CalGray color space");
733
0
        return {};
734
0
    }
735
0
    auto cs = std::make_unique<GfxCalGrayColorSpace>();
736
0
    obj2 = obj1.dictLookup("WhitePoint");
737
0
    if (obj2.isArrayOfLength(3)) {
738
0
        cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1);
739
0
        cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1);
740
0
        cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1);
741
0
    }
742
0
    obj2 = obj1.dictLookup("BlackPoint");
743
0
    if (obj2.isArrayOfLength(3)) {
744
0
        cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0);
745
0
        cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0);
746
0
        cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0);
747
0
    }
748
749
0
    cs->gamma = obj1.dictLookup("Gamma").getNumWithDefaultValue(1);
750
751
#if USE_CMS
752
    cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr;
753
#else
754
0
    (void)state;
755
0
#endif
756
0
    return cs;
757
0
}
758
759
// convert CalGray to media XYZ color space
760
void GfxCalGrayColorSpace::getXYZ(const GfxColor &color, double *pX, double *pY, double *pZ) const
761
0
{
762
0
    const double A = colToDbl(color.c[0]);
763
0
    const double xyzColor = pow(A, gamma);
764
0
    *pX = whiteX * xyzColor;
765
0
    *pY = whiteY * xyzColor;
766
0
    *pZ = whiteZ * xyzColor;
767
0
}
768
769
void GfxCalGrayColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
770
0
{
771
0
    GfxRGB rgb;
772
773
#if USE_CMS
774
    if (transform && transform->getTransformPixelType() == PT_GRAY) {
775
        unsigned char out[gfxColorMaxComps];
776
        double in[gfxColorMaxComps];
777
        double X, Y, Z;
778
779
        getXYZ(color, &X, &Y, &Z);
780
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
781
        in[0] = X;
782
        in[1] = Y;
783
        in[2] = Z;
784
        transform->doTransform(in, out, 1);
785
        *gray = byteToCol(out[0]);
786
        return;
787
    }
788
#endif
789
0
    getRGB(color, &rgb);
790
0
    *gray = clip01(static_cast<GfxColorComp>(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5));
791
0
}
792
793
void GfxCalGrayColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
794
0
{
795
0
    double X, Y, Z;
796
0
    double r, g, b;
797
798
0
    getXYZ(color, &X, &Y, &Z);
799
#if USE_CMS
800
    if (transform && transform->getTransformPixelType() == PT_RGB) {
801
        unsigned char out[gfxColorMaxComps];
802
        double in[gfxColorMaxComps];
803
804
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
805
        in[0] = X;
806
        in[1] = Y;
807
        in[2] = Z;
808
        transform->doTransform(in, out, 1);
809
        rgb->r = byteToCol(out[0]);
810
        rgb->g = byteToCol(out[1]);
811
        rgb->b = byteToCol(out[2]);
812
        return;
813
    }
814
#endif
815
0
    bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ);
816
    // convert XYZ to RGB, including gamut mapping and gamma correction
817
0
    r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
818
0
    g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
819
0
    b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
820
0
    rgb->r = dblToCol(srgb_gamma_function(clip01(r)));
821
0
    rgb->g = dblToCol(srgb_gamma_function(clip01(g)));
822
0
    rgb->b = dblToCol(srgb_gamma_function(clip01(b)));
823
0
}
824
825
void GfxCalGrayColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
826
0
{
827
0
    GfxRGB rgb;
828
0
    GfxColorComp c, m, y, k;
829
830
#if USE_CMS
831
    if (transform && transform->getTransformPixelType() == PT_CMYK) {
832
        double in[gfxColorMaxComps];
833
        unsigned char out[gfxColorMaxComps];
834
        double X, Y, Z;
835
836
        getXYZ(color, &X, &Y, &Z);
837
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
838
        in[0] = X;
839
        in[1] = Y;
840
        in[2] = Z;
841
        transform->doTransform(in, out, 1);
842
        cmyk->c = byteToCol(out[0]);
843
        cmyk->m = byteToCol(out[1]);
844
        cmyk->y = byteToCol(out[2]);
845
        cmyk->k = byteToCol(out[3]);
846
        return;
847
    }
848
#endif
849
0
    getRGB(color, &rgb);
850
0
    c = clip01(gfxColorComp1 - rgb.r);
851
0
    m = clip01(gfxColorComp1 - rgb.g);
852
0
    y = clip01(gfxColorComp1 - rgb.b);
853
0
    k = c;
854
0
    if (m < k) {
855
0
        k = m;
856
0
    }
857
0
    if (y < k) {
858
0
        k = y;
859
0
    }
860
0
    cmyk->c = c - k;
861
0
    cmyk->m = m - k;
862
0
    cmyk->y = y - k;
863
0
    cmyk->k = k;
864
0
}
865
866
void GfxCalGrayColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
867
0
{
868
0
    GfxCMYK cmyk;
869
0
    clearGfxColor(deviceN);
870
0
    getCMYK(color, &cmyk);
871
0
    deviceN->c[0] = cmyk.c;
872
0
    deviceN->c[1] = cmyk.m;
873
0
    deviceN->c[2] = cmyk.y;
874
0
    deviceN->c[3] = cmyk.k;
875
0
}
876
877
void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) const
878
0
{
879
0
    color->c[0] = 0;
880
0
}
881
882
//------------------------------------------------------------------------
883
// GfxDeviceRGBColorSpace
884
//------------------------------------------------------------------------
885
886
65.9k
GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() = default;
887
888
GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() = default;
889
890
std::unique_ptr<GfxColorSpace> GfxDeviceRGBColorSpace::copy() const
891
34.1k
{
892
34.1k
    return std::make_unique<GfxDeviceRGBColorSpace>();
893
34.1k
}
894
895
void GfxDeviceRGBColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
896
0
{
897
0
    *gray = clip01(static_cast<GfxColorComp>(0.3 * color.c[0] + 0.59 * color.c[1] + 0.11 * color.c[2] + 0.5));
898
0
}
899
900
void GfxDeviceRGBColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length)
901
0
{
902
0
    int i;
903
904
0
    for (i = 0; i < length; i++) {
905
0
        out[i] = (in[i * 3 + 0] * 19595 + in[i * 3 + 1] * 38469 + in[i * 3 + 2] * 7472) / 65536;
906
0
    }
907
0
}
908
909
void GfxDeviceRGBColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
910
853k
{
911
853k
    rgb->r = clip01(color.c[0]);
912
853k
    rgb->g = clip01(color.c[1]);
913
853k
    rgb->b = clip01(color.c[2]);
914
853k
}
915
916
void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length)
917
0
{
918
0
    unsigned char *p;
919
0
    int i;
920
921
0
    for (i = 0, p = in; i < length; i++, p += 3) {
922
0
        out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0);
923
0
    }
924
0
}
925
926
void GfxDeviceRGBColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
927
0
{
928
0
    for (int i = 0; i < length; i++) {
929
0
        *out++ = *in++;
930
0
        *out++ = *in++;
931
0
        *out++ = *in++;
932
0
    }
933
0
}
934
935
void GfxDeviceRGBColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length)
936
0
{
937
0
    for (int i = 0; i < length; i++) {
938
0
        *out++ = *in++;
939
0
        *out++ = *in++;
940
0
        *out++ = *in++;
941
0
        *out++ = 255;
942
0
    }
943
0
}
944
945
void GfxDeviceRGBColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
946
0
{
947
0
    GfxColorComp c, m, y, k;
948
949
0
    for (int i = 0; i < length; i++) {
950
0
        c = byteToCol(255 - *in++);
951
0
        m = byteToCol(255 - *in++);
952
0
        y = byteToCol(255 - *in++);
953
0
        k = c;
954
0
        if (m < k) {
955
0
            k = m;
956
0
        }
957
0
        if (y < k) {
958
0
            k = y;
959
0
        }
960
0
        *out++ = colToByte(c - k);
961
0
        *out++ = colToByte(m - k);
962
0
        *out++ = colToByte(y - k);
963
0
        *out++ = colToByte(k);
964
0
    }
965
0
}
966
967
void GfxDeviceRGBColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
968
0
{
969
0
    GfxColorComp c, m, y, k;
970
971
0
    for (int i = 0; i < length; i++) {
972
0
        for (int j = 0; j < SPOT_NCOMPS + 4; j++) {
973
0
            out[j] = 0;
974
0
        }
975
0
        c = byteToCol(255 - *in++);
976
0
        m = byteToCol(255 - *in++);
977
0
        y = byteToCol(255 - *in++);
978
0
        k = c;
979
0
        if (m < k) {
980
0
            k = m;
981
0
        }
982
0
        if (y < k) {
983
0
            k = y;
984
0
        }
985
0
        out[0] = colToByte(c - k);
986
0
        out[1] = colToByte(m - k);
987
0
        out[2] = colToByte(y - k);
988
0
        out[3] = colToByte(k);
989
0
        out += (SPOT_NCOMPS + 4);
990
0
    }
991
0
}
992
993
void GfxDeviceRGBColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
994
0
{
995
0
    GfxColorComp c, m, y, k;
996
997
0
    c = clip01(gfxColorComp1 - color.c[0]);
998
0
    m = clip01(gfxColorComp1 - color.c[1]);
999
0
    y = clip01(gfxColorComp1 - color.c[2]);
1000
0
    k = c;
1001
0
    if (m < k) {
1002
0
        k = m;
1003
0
    }
1004
0
    if (y < k) {
1005
0
        k = y;
1006
0
    }
1007
0
    cmyk->c = c - k;
1008
0
    cmyk->m = m - k;
1009
0
    cmyk->y = y - k;
1010
0
    cmyk->k = k;
1011
0
}
1012
1013
void GfxDeviceRGBColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
1014
0
{
1015
0
    GfxCMYK cmyk;
1016
0
    clearGfxColor(deviceN);
1017
0
    getCMYK(color, &cmyk);
1018
0
    deviceN->c[0] = cmyk.c;
1019
0
    deviceN->c[1] = cmyk.m;
1020
0
    deviceN->c[2] = cmyk.y;
1021
0
    deviceN->c[3] = cmyk.k;
1022
0
}
1023
1024
void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) const
1025
0
{
1026
0
    color->c[0] = 0;
1027
0
    color->c[1] = 0;
1028
0
    color->c[2] = 0;
1029
0
}
1030
1031
//------------------------------------------------------------------------
1032
// GfxDeviceRGBAColorSpace
1033
//------------------------------------------------------------------------
1034
1035
0
GfxDeviceRGBAColorSpace::GfxDeviceRGBAColorSpace() = default;
1036
1037
GfxDeviceRGBAColorSpace::~GfxDeviceRGBAColorSpace() = default;
1038
1039
std::unique_ptr<GfxColorSpace> GfxDeviceRGBAColorSpace::copy() const
1040
0
{
1041
0
    return std::make_unique<GfxDeviceRGBAColorSpace>();
1042
0
}
1043
1044
void GfxDeviceRGBAColorSpace::getARGBPremultipliedLine(unsigned char *in, unsigned int *out, int length)
1045
0
{
1046
0
    unsigned char *p;
1047
0
    int i;
1048
1049
    // Conversion from 'in' RGBA to 'out' ARGB32_PREMULTIPLIED (used by Cairo)
1050
0
    for (i = 0, p = in; i < length; i++, p += 4) {
1051
        // This applies alpha component p[3] to each RGB values (using bitwise division)
1052
        // so final result in out[i] is ARGB32 with premultiplied alpha.
1053
0
        out[i] = p[3] << 24 | (p[0] * p[3] >> 8) << 16 | (p[1] * p[3] >> 8) << 8 | (p[2] * p[3] >> 8) << 0;
1054
0
    }
1055
0
}
1056
1057
//------------------------------------------------------------------------
1058
// GfxCalRGBColorSpace
1059
//------------------------------------------------------------------------
1060
1061
GfxCalRGBColorSpace::GfxCalRGBColorSpace()
1062
0
{
1063
0
    whiteX = whiteY = whiteZ = 1;
1064
0
    blackX = blackY = blackZ = 0;
1065
0
    gammaR = gammaG = gammaB = 1;
1066
0
    mat[0] = 1;
1067
0
    mat[1] = 0;
1068
0
    mat[2] = 0;
1069
0
    mat[3] = 0;
1070
0
    mat[4] = 1;
1071
0
    mat[5] = 0;
1072
0
    mat[6] = 0;
1073
0
    mat[7] = 0;
1074
0
    mat[8] = 1;
1075
0
}
1076
1077
GfxCalRGBColorSpace::~GfxCalRGBColorSpace() = default;
1078
1079
std::unique_ptr<GfxColorSpace> GfxCalRGBColorSpace::copy() const
1080
0
{
1081
0
    auto cs = std::make_unique<GfxCalRGBColorSpace>();
1082
0
    cs->whiteX = whiteX;
1083
0
    cs->whiteY = whiteY;
1084
0
    cs->whiteZ = whiteZ;
1085
0
    cs->blackX = blackX;
1086
0
    cs->blackY = blackY;
1087
0
    cs->blackZ = blackZ;
1088
0
    cs->gammaR = gammaR;
1089
0
    cs->gammaG = gammaG;
1090
0
    cs->gammaB = gammaB;
1091
0
    cs->mat = mat;
1092
#if USE_CMS
1093
    cs->transform = transform;
1094
#endif
1095
0
    return cs;
1096
0
}
1097
1098
std::unique_ptr<GfxColorSpace> GfxCalRGBColorSpace::parse(const Array &arr, GfxState *state)
1099
0
{
1100
0
    Object obj1, obj2;
1101
0
    int i;
1102
1103
0
    obj1 = arr.get(1);
1104
0
    if (!obj1.isDict()) {
1105
0
        error(errSyntaxWarning, -1, "Bad CalRGB color space");
1106
0
        return {};
1107
0
    }
1108
0
    auto cs = std::make_unique<GfxCalRGBColorSpace>();
1109
0
    obj2 = obj1.dictLookup("WhitePoint");
1110
0
    if (obj2.isArrayOfLength(3)) {
1111
0
        cs->whiteX = obj2.arrayGet(0).getNumWithDefaultValue(1);
1112
0
        cs->whiteY = obj2.arrayGet(1).getNumWithDefaultValue(1);
1113
0
        cs->whiteZ = obj2.arrayGet(2).getNumWithDefaultValue(1);
1114
0
    }
1115
0
    obj2 = obj1.dictLookup("BlackPoint");
1116
0
    if (obj2.isArrayOfLength(3)) {
1117
0
        cs->blackX = obj2.arrayGet(0).getNumWithDefaultValue(0);
1118
0
        cs->blackY = obj2.arrayGet(1).getNumWithDefaultValue(0);
1119
0
        cs->blackZ = obj2.arrayGet(2).getNumWithDefaultValue(0);
1120
0
    }
1121
0
    obj2 = obj1.dictLookup("Gamma");
1122
0
    if (obj2.isArrayOfLength(3)) {
1123
0
        cs->gammaR = obj2.arrayGet(0).getNumWithDefaultValue(1);
1124
0
        cs->gammaG = obj2.arrayGet(1).getNumWithDefaultValue(1);
1125
0
        cs->gammaB = obj2.arrayGet(2).getNumWithDefaultValue(1);
1126
0
    }
1127
0
    obj2 = obj1.dictLookup("Matrix");
1128
0
    if (obj2.isArrayOfLength(9)) {
1129
0
        for (i = 0; i < 9; ++i) {
1130
0
            Object obj3 = obj2.arrayGet(i);
1131
0
            if (likely(obj3.isNum())) {
1132
0
                cs->mat[i] = obj3.getNum();
1133
0
            }
1134
0
        }
1135
0
    }
1136
1137
#if USE_CMS
1138
    cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr;
1139
#else
1140
0
    (void)state;
1141
0
#endif
1142
0
    return cs;
1143
0
}
1144
1145
// convert CalRGB to XYZ color space
1146
void GfxCalRGBColorSpace::getXYZ(const GfxColor &color, double *pX, double *pY, double *pZ) const
1147
0
{
1148
0
    double A, B, C;
1149
1150
0
    A = pow(colToDbl(color.c[0]), gammaR);
1151
0
    B = pow(colToDbl(color.c[1]), gammaG);
1152
0
    C = pow(colToDbl(color.c[2]), gammaB);
1153
0
    *pX = mat[0] * A + mat[3] * B + mat[6] * C;
1154
0
    *pY = mat[1] * A + mat[4] * B + mat[7] * C;
1155
0
    *pZ = mat[2] * A + mat[5] * B + mat[8] * C;
1156
0
}
1157
1158
void GfxCalRGBColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
1159
0
{
1160
0
    GfxRGB rgb;
1161
1162
#if USE_CMS
1163
    if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) {
1164
        unsigned char out[gfxColorMaxComps];
1165
        double in[gfxColorMaxComps];
1166
        double X, Y, Z;
1167
1168
        getXYZ(color, &X, &Y, &Z);
1169
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
1170
        in[0] = X;
1171
        in[1] = Y;
1172
        in[2] = Z;
1173
        transform->doTransform(in, out, 1);
1174
        *gray = byteToCol(out[0]);
1175
        return;
1176
    }
1177
#endif
1178
0
    getRGB(color, &rgb);
1179
0
    *gray = clip01(static_cast<GfxColorComp>(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5));
1180
0
}
1181
1182
void GfxCalRGBColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
1183
0
{
1184
0
    double X, Y, Z;
1185
0
    double r, g, b;
1186
1187
0
    getXYZ(color, &X, &Y, &Z);
1188
#if USE_CMS
1189
    if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) {
1190
        unsigned char out[gfxColorMaxComps];
1191
        double in[gfxColorMaxComps];
1192
1193
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
1194
        in[0] = X;
1195
        in[1] = Y;
1196
        in[2] = Z;
1197
        transform->doTransform(in, out, 1);
1198
        rgb->r = byteToCol(out[0]);
1199
        rgb->g = byteToCol(out[1]);
1200
        rgb->b = byteToCol(out[2]);
1201
1202
        return;
1203
    }
1204
#endif
1205
0
    bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ);
1206
    // convert XYZ to RGB, including gamut mapping and gamma correction
1207
0
    r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1208
0
    g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1209
0
    b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1210
0
    rgb->r = dblToCol(srgb_gamma_function(clip01(r)));
1211
0
    rgb->g = dblToCol(srgb_gamma_function(clip01(g)));
1212
0
    rgb->b = dblToCol(srgb_gamma_function(clip01(b)));
1213
0
}
1214
1215
void GfxCalRGBColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
1216
0
{
1217
0
    GfxRGB rgb;
1218
0
    GfxColorComp c, m, y, k;
1219
1220
#if USE_CMS
1221
    if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
1222
        double in[gfxColorMaxComps];
1223
        unsigned char out[gfxColorMaxComps];
1224
        double X, Y, Z;
1225
1226
        getXYZ(color, &X, &Y, &Z);
1227
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
1228
        in[0] = X;
1229
        in[1] = Y;
1230
        in[2] = Z;
1231
        transform->doTransform(in, out, 1);
1232
        cmyk->c = byteToCol(out[0]);
1233
        cmyk->m = byteToCol(out[1]);
1234
        cmyk->y = byteToCol(out[2]);
1235
        cmyk->k = byteToCol(out[3]);
1236
        return;
1237
    }
1238
#endif
1239
0
    getRGB(color, &rgb);
1240
0
    c = clip01(gfxColorComp1 - rgb.r);
1241
0
    m = clip01(gfxColorComp1 - rgb.g);
1242
0
    y = clip01(gfxColorComp1 - rgb.b);
1243
0
    k = c;
1244
0
    if (m < k) {
1245
0
        k = m;
1246
0
    }
1247
0
    if (y < k) {
1248
0
        k = y;
1249
0
    }
1250
0
    cmyk->c = c - k;
1251
0
    cmyk->m = m - k;
1252
0
    cmyk->y = y - k;
1253
0
    cmyk->k = k;
1254
0
}
1255
1256
void GfxCalRGBColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
1257
0
{
1258
0
    GfxCMYK cmyk;
1259
0
    clearGfxColor(deviceN);
1260
0
    getCMYK(color, &cmyk);
1261
0
    deviceN->c[0] = cmyk.c;
1262
0
    deviceN->c[1] = cmyk.m;
1263
0
    deviceN->c[2] = cmyk.y;
1264
0
    deviceN->c[3] = cmyk.k;
1265
0
}
1266
1267
void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) const
1268
0
{
1269
0
    color->c[0] = 0;
1270
0
    color->c[1] = 0;
1271
0
    color->c[2] = 0;
1272
0
}
1273
1274
//------------------------------------------------------------------------
1275
// GfxDeviceCMYKColorSpace
1276
//------------------------------------------------------------------------
1277
1278
10
GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() = default;
1279
1280
GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() = default;
1281
1282
std::unique_ptr<GfxColorSpace> GfxDeviceCMYKColorSpace::copy() const
1283
0
{
1284
0
    return std::make_unique<GfxDeviceCMYKColorSpace>();
1285
0
}
1286
1287
void GfxDeviceCMYKColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
1288
0
{
1289
0
    *gray = clip01(static_cast<GfxColorComp>(gfxColorComp1 - color.c[3] - 0.3 * color.c[0] - 0.59 * color.c[1] - 0.11 * color.c[2] + 0.5));
1290
0
}
1291
1292
void GfxDeviceCMYKColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
1293
0
{
1294
0
    double c, m, y, k, c1, m1, y1, k1, r, g, b;
1295
1296
0
    c = colToDbl(color.c[0]);
1297
0
    m = colToDbl(color.c[1]);
1298
0
    y = colToDbl(color.c[2]);
1299
0
    k = colToDbl(color.c[3]);
1300
0
    c1 = 1 - c;
1301
0
    m1 = 1 - m;
1302
0
    y1 = 1 - y;
1303
0
    k1 = 1 - k;
1304
0
    cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1305
0
    rgb->r = clip01(dblToCol(r));
1306
0
    rgb->g = clip01(dblToCol(g));
1307
0
    rgb->b = clip01(dblToCol(b));
1308
0
}
1309
1310
static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(unsigned char *&in, double &r, double &g, double &b)
1311
0
{
1312
0
    double c, m, y, k, c1, m1, y1, k1;
1313
1314
0
    c = byteToDbl(*in++);
1315
0
    m = byteToDbl(*in++);
1316
0
    y = byteToDbl(*in++);
1317
0
    k = byteToDbl(*in++);
1318
0
    c1 = 1 - c;
1319
0
    m1 = 1 - m;
1320
0
    y1 = 1 - y;
1321
0
    k1 = 1 - k;
1322
0
    cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1323
0
}
1324
1325
void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length)
1326
0
{
1327
0
    double r, g, b;
1328
0
    for (int i = 0; i < length; i++) {
1329
0
        GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1330
0
        *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b));
1331
0
    }
1332
0
}
1333
1334
void GfxDeviceCMYKColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
1335
0
{
1336
0
    double r, g, b;
1337
1338
0
    for (int i = 0; i < length; i++) {
1339
0
        GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1340
0
        *out++ = dblToByte(clip01(r));
1341
0
        *out++ = dblToByte(clip01(g));
1342
0
        *out++ = dblToByte(clip01(b));
1343
0
    }
1344
0
}
1345
1346
void GfxDeviceCMYKColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length)
1347
0
{
1348
0
    double r, g, b;
1349
1350
0
    for (int i = 0; i < length; i++) {
1351
0
        GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1352
0
        *out++ = dblToByte(clip01(r));
1353
0
        *out++ = dblToByte(clip01(g));
1354
0
        *out++ = dblToByte(clip01(b));
1355
0
        *out++ = 255;
1356
0
    }
1357
0
}
1358
1359
void GfxDeviceCMYKColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
1360
0
{
1361
0
    for (int i = 0; i < length; i++) {
1362
0
        *out++ = *in++;
1363
0
        *out++ = *in++;
1364
0
        *out++ = *in++;
1365
0
        *out++ = *in++;
1366
0
    }
1367
0
}
1368
1369
void GfxDeviceCMYKColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
1370
0
{
1371
0
    for (int i = 0; i < length; i++) {
1372
0
        for (int j = 0; j < SPOT_NCOMPS + 4; j++) {
1373
0
            out[j] = 0;
1374
0
        }
1375
0
        out[0] = *in++;
1376
0
        out[1] = *in++;
1377
0
        out[2] = *in++;
1378
0
        out[3] = *in++;
1379
0
        out += (SPOT_NCOMPS + 4);
1380
0
    }
1381
0
}
1382
1383
void GfxDeviceCMYKColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
1384
0
{
1385
0
    cmyk->c = clip01(color.c[0]);
1386
0
    cmyk->m = clip01(color.c[1]);
1387
0
    cmyk->y = clip01(color.c[2]);
1388
0
    cmyk->k = clip01(color.c[3]);
1389
0
}
1390
1391
void GfxDeviceCMYKColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
1392
0
{
1393
0
    clearGfxColor(deviceN);
1394
0
    deviceN->c[0] = clip01(color.c[0]);
1395
0
    deviceN->c[1] = clip01(color.c[1]);
1396
0
    deviceN->c[2] = clip01(color.c[2]);
1397
0
    deviceN->c[3] = clip01(color.c[3]);
1398
0
}
1399
1400
void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) const
1401
0
{
1402
0
    color->c[0] = 0;
1403
0
    color->c[1] = 0;
1404
0
    color->c[2] = 0;
1405
0
    color->c[3] = gfxColorComp1;
1406
0
}
1407
1408
//------------------------------------------------------------------------
1409
// GfxLabColorSpace
1410
//------------------------------------------------------------------------
1411
1412
GfxLabColorSpace::GfxLabColorSpace()
1413
0
{
1414
0
    whiteX = whiteY = whiteZ = 1;
1415
0
    blackX = blackY = blackZ = 0;
1416
0
    aMin = bMin = -100;
1417
0
    aMax = bMax = 100;
1418
0
}
1419
1420
GfxLabColorSpace::~GfxLabColorSpace() = default;
1421
1422
std::unique_ptr<GfxColorSpace> GfxLabColorSpace::copy() const
1423
0
{
1424
0
    auto cs = std::make_unique<GfxLabColorSpace>();
1425
0
    cs->whiteX = whiteX;
1426
0
    cs->whiteY = whiteY;
1427
0
    cs->whiteZ = whiteZ;
1428
0
    cs->blackX = blackX;
1429
0
    cs->blackY = blackY;
1430
0
    cs->blackZ = blackZ;
1431
0
    cs->aMin = aMin;
1432
0
    cs->aMax = aMax;
1433
0
    cs->bMin = bMin;
1434
0
    cs->bMax = bMax;
1435
#if USE_CMS
1436
    cs->transform = transform;
1437
#endif
1438
0
    return cs;
1439
0
}
1440
1441
std::unique_ptr<GfxColorSpace> GfxLabColorSpace::parse(const Array &arr, GfxState *state)
1442
0
{
1443
0
    Object obj1, obj2;
1444
1445
0
    obj1 = arr.get(1);
1446
0
    if (!obj1.isDict()) {
1447
0
        error(errSyntaxWarning, -1, "Bad Lab color space");
1448
0
        return {};
1449
0
    }
1450
0
    auto cs = std::make_unique<GfxLabColorSpace>();
1451
0
    bool ok = true;
1452
0
    obj2 = obj1.dictLookup("WhitePoint");
1453
0
    if (obj2.isArrayOfLength(3)) {
1454
0
        cs->whiteX = obj2.arrayGet(0).getNum(&ok);
1455
0
        cs->whiteY = obj2.arrayGet(1).getNum(&ok);
1456
0
        cs->whiteZ = obj2.arrayGet(2).getNum(&ok);
1457
0
    }
1458
0
    obj2 = obj1.dictLookup("BlackPoint");
1459
0
    if (obj2.isArrayOfLength(3)) {
1460
0
        cs->blackX = obj2.arrayGet(0).getNum(&ok);
1461
0
        cs->blackY = obj2.arrayGet(1).getNum(&ok);
1462
0
        cs->blackZ = obj2.arrayGet(2).getNum(&ok);
1463
0
    }
1464
0
    obj2 = obj1.dictLookup("Range");
1465
0
    if (obj2.isArrayOfLength(4)) {
1466
0
        cs->aMin = obj2.arrayGet(0).getNum(&ok);
1467
0
        cs->aMax = obj2.arrayGet(1).getNum(&ok);
1468
0
        cs->bMin = obj2.arrayGet(2).getNum(&ok);
1469
0
        cs->bMax = obj2.arrayGet(3).getNum(&ok);
1470
0
    }
1471
1472
0
    if (!ok) {
1473
0
        error(errSyntaxWarning, -1, "Bad Lab color space");
1474
#if USE_CMS
1475
        cs->transform = nullptr;
1476
#endif
1477
0
        return {};
1478
0
    }
1479
1480
#if USE_CMS
1481
    cs->transform = (state != nullptr) ? state->getXYZ2DisplayTransform() : nullptr;
1482
#else
1483
0
    (void)state;
1484
0
#endif
1485
0
    return cs;
1486
0
}
1487
1488
void GfxLabColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
1489
0
{
1490
0
    GfxRGB rgb;
1491
1492
#if USE_CMS
1493
    if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) {
1494
        unsigned char out[gfxColorMaxComps];
1495
        double in[gfxColorMaxComps];
1496
1497
        getXYZ(color, &in[0], &in[1], &in[2]);
1498
        bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ);
1499
        transform->doTransform(in, out, 1);
1500
        *gray = byteToCol(out[0]);
1501
        return;
1502
    }
1503
#endif
1504
0
    getRGB(color, &rgb);
1505
0
    *gray = clip01(static_cast<GfxColorComp>(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b + 0.5));
1506
0
}
1507
1508
// convert L*a*b* to media XYZ color space
1509
// (not multiply by the white point)
1510
void GfxLabColorSpace::getXYZ(const GfxColor &color, double *pX, double *pY, double *pZ)
1511
0
{
1512
0
    double X, Y, Z;
1513
0
    double t1, t2;
1514
1515
0
    t1 = (colToDbl(color.c[0]) + 16) / 116;
1516
0
    t2 = t1 + colToDbl(color.c[1]) / 500;
1517
0
    if (t2 >= (6.0 / 29.0)) {
1518
0
        X = t2 * t2 * t2;
1519
0
    } else {
1520
0
        X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1521
0
    }
1522
0
    if (t1 >= (6.0 / 29.0)) {
1523
0
        Y = t1 * t1 * t1;
1524
0
    } else {
1525
0
        Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
1526
0
    }
1527
0
    t2 = t1 - colToDbl(color.c[2]) / 200;
1528
0
    if (t2 >= (6.0 / 29.0)) {
1529
0
        Z = t2 * t2 * t2;
1530
0
    } else {
1531
0
        Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1532
0
    }
1533
0
    *pX = X;
1534
0
    *pY = Y;
1535
0
    *pZ = Z;
1536
0
}
1537
1538
void GfxLabColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
1539
0
{
1540
0
    double X, Y, Z;
1541
1542
0
    getXYZ(color, &X, &Y, &Z);
1543
0
    X *= whiteX;
1544
0
    Y *= whiteY;
1545
0
    Z *= whiteZ;
1546
#if USE_CMS
1547
    if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) {
1548
        unsigned char out[gfxColorMaxComps];
1549
        double in[gfxColorMaxComps];
1550
1551
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
1552
        in[0] = X;
1553
        in[1] = Y;
1554
        in[2] = Z;
1555
        transform->doTransform(in, out, 1);
1556
        rgb->r = byteToCol(out[0]);
1557
        rgb->g = byteToCol(out[1]);
1558
        rgb->b = byteToCol(out[2]);
1559
        return;
1560
    }
1561
    if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
1562
        unsigned char out[gfxColorMaxComps];
1563
        double in[gfxColorMaxComps];
1564
        double c, m, y, k, c1, m1, y1, k1, r, g, b;
1565
1566
        bradford_transform_to_d50(X, Y, Z, whiteX, whiteY, whiteZ);
1567
        in[0] = X;
1568
        in[1] = Y;
1569
        in[2] = Z;
1570
        transform->doTransform(in, out, 1);
1571
        c = byteToDbl(out[0]);
1572
        m = byteToDbl(out[1]);
1573
        y = byteToDbl(out[2]);
1574
        k = byteToDbl(out[3]);
1575
        c1 = 1 - c;
1576
        m1 = 1 - m;
1577
        y1 = 1 - y;
1578
        k1 = 1 - k;
1579
        cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1580
        rgb->r = clip01(dblToCol(r));
1581
        rgb->g = clip01(dblToCol(g));
1582
        rgb->b = clip01(dblToCol(b));
1583
        return;
1584
    }
1585
#endif
1586
0
    bradford_transform_to_d65(X, Y, Z, whiteX, whiteY, whiteZ);
1587
    // convert XYZ to RGB, including gamut mapping and gamma correction
1588
0
    const double r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1589
0
    const double g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1590
0
    const double b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1591
0
    rgb->r = dblToCol(srgb_gamma_function(clip01(r)));
1592
0
    rgb->g = dblToCol(srgb_gamma_function(clip01(g)));
1593
0
    rgb->b = dblToCol(srgb_gamma_function(clip01(b)));
1594
0
}
1595
1596
void GfxLabColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
1597
0
{
1598
0
    GfxRGB rgb;
1599
0
    GfxColorComp c, m, y, k;
1600
1601
#if USE_CMS
1602
    if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
1603
        double in[gfxColorMaxComps];
1604
        unsigned char out[gfxColorMaxComps];
1605
1606
        getXYZ(color, &in[0], &in[1], &in[2]);
1607
        bradford_transform_to_d50(in[0], in[1], in[2], whiteX, whiteY, whiteZ);
1608
        transform->doTransform(in, out, 1);
1609
        cmyk->c = byteToCol(out[0]);
1610
        cmyk->m = byteToCol(out[1]);
1611
        cmyk->y = byteToCol(out[2]);
1612
        cmyk->k = byteToCol(out[3]);
1613
        return;
1614
    }
1615
#endif
1616
0
    getRGB(color, &rgb);
1617
0
    c = clip01(gfxColorComp1 - rgb.r);
1618
0
    m = clip01(gfxColorComp1 - rgb.g);
1619
0
    y = clip01(gfxColorComp1 - rgb.b);
1620
0
    k = c;
1621
0
    if (m < k) {
1622
0
        k = m;
1623
0
    }
1624
0
    if (y < k) {
1625
0
        k = y;
1626
0
    }
1627
0
    cmyk->c = c - k;
1628
0
    cmyk->m = m - k;
1629
0
    cmyk->y = y - k;
1630
0
    cmyk->k = k;
1631
0
}
1632
1633
void GfxLabColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
1634
0
{
1635
0
    GfxCMYK cmyk;
1636
0
    clearGfxColor(deviceN);
1637
0
    getCMYK(color, &cmyk);
1638
0
    deviceN->c[0] = cmyk.c;
1639
0
    deviceN->c[1] = cmyk.m;
1640
0
    deviceN->c[2] = cmyk.y;
1641
0
    deviceN->c[3] = cmyk.k;
1642
0
}
1643
1644
void GfxLabColorSpace::getDefaultColor(GfxColor *color) const
1645
0
{
1646
0
    color->c[0] = 0;
1647
0
    if (aMin > 0) {
1648
0
        color->c[1] = dblToCol(aMin);
1649
0
    } else if (aMax < 0) {
1650
0
        color->c[1] = dblToCol(aMax);
1651
0
    } else {
1652
0
        color->c[1] = 0;
1653
0
    }
1654
0
    if (bMin > 0) {
1655
0
        color->c[2] = dblToCol(bMin);
1656
0
    } else if (bMax < 0) {
1657
0
        color->c[2] = dblToCol(bMax);
1658
0
    } else {
1659
0
        color->c[2] = 0;
1660
0
    }
1661
0
}
1662
1663
void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int /*maxImgPixel*/) const
1664
0
{
1665
0
    decodeLow[0] = 0;
1666
0
    decodeRange[0] = 100;
1667
0
    decodeLow[1] = aMin;
1668
0
    decodeRange[1] = aMax - aMin;
1669
0
    decodeLow[2] = bMin;
1670
0
    decodeRange[2] = bMax - bMin;
1671
0
}
1672
1673
//------------------------------------------------------------------------
1674
// GfxICCBasedColorSpace
1675
//------------------------------------------------------------------------
1676
1677
0
GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, std::unique_ptr<GfxColorSpace> &&altA, const Ref *iccProfileStreamA) : alt(std::move(altA))
1678
0
{
1679
0
    nComps = nCompsA;
1680
0
    iccProfileStream = *iccProfileStreamA;
1681
0
    rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
1682
0
    rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
1683
#if USE_CMS
1684
    transform = nullptr;
1685
    lineTransform = nullptr;
1686
    psCSA = nullptr;
1687
#endif
1688
0
}
1689
1690
GfxICCBasedColorSpace::~GfxICCBasedColorSpace()
1691
0
{
1692
#if USE_CMS
1693
    if (psCSA) {
1694
        gfree(psCSA);
1695
    }
1696
#endif
1697
0
}
1698
1699
std::unique_ptr<GfxColorSpace> GfxICCBasedColorSpace::copy() const
1700
0
{
1701
0
    return copyAsOwnType();
1702
0
}
1703
1704
std::unique_ptr<GfxICCBasedColorSpace> GfxICCBasedColorSpace::copyAsOwnType() const
1705
0
{
1706
0
    int i;
1707
1708
0
    auto cs = std::make_unique<GfxICCBasedColorSpace>(nComps, alt->copy(), &iccProfileStream);
1709
0
    for (i = 0; i < 4; ++i) {
1710
0
        cs->rangeMin[i] = rangeMin[i];
1711
0
        cs->rangeMax[i] = rangeMax[i];
1712
0
    }
1713
#if USE_CMS
1714
    cs->profile = profile;
1715
    cs->transform = transform;
1716
    cs->lineTransform = lineTransform;
1717
#endif
1718
0
    return cs;
1719
0
}
1720
1721
std::unique_ptr<GfxColorSpace> GfxICCBasedColorSpace::parse(const Array &arr, OutputDev *out, GfxState *state, int recursion)
1722
0
{
1723
0
    int nCompsA;
1724
0
    Dict *dict;
1725
0
    Object obj1, obj2;
1726
0
    int i;
1727
1728
0
    if (arr.getLength() < 2) {
1729
0
        error(errSyntaxError, -1, "Bad ICCBased color space");
1730
0
        return {};
1731
0
    }
1732
0
    const Object &obj1Ref = arr.getNF(1);
1733
0
    const Ref iccProfileStreamA = obj1Ref.isRef() ? obj1Ref.getRef() : Ref::INVALID();
1734
#if USE_CMS
1735
    // check cache
1736
    if (out && iccProfileStreamA != Ref::INVALID()) {
1737
        if (auto *item = out->getIccColorSpaceCache()->lookup(iccProfileStreamA)) {
1738
            std::unique_ptr<GfxICCBasedColorSpace> cs = item->copyAsOwnType();
1739
            int transformIntent = cs->getIntent();
1740
            int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
1741
            if (state != nullptr) {
1742
                cmsIntent = state->getCmsRenderingIntent();
1743
            }
1744
            if (transformIntent == cmsIntent) {
1745
                return cs;
1746
            }
1747
        }
1748
    }
1749
#endif
1750
0
    obj1 = arr.get(1);
1751
0
    if (!obj1.isStream()) {
1752
0
        error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)");
1753
0
        return nullptr;
1754
0
    }
1755
0
    dict = obj1.getStream()->getDict();
1756
0
    obj2 = dict->lookup("N");
1757
0
    if (!obj2.isInt()) {
1758
0
        error(errSyntaxWarning, -1, "Bad ICCBased color space (N)");
1759
0
        return nullptr;
1760
0
    }
1761
0
    nCompsA = obj2.getInt();
1762
0
    if (nCompsA > 4) {
1763
0
        error(errSyntaxError, -1, "ICCBased color space with too many ({0:d} > 4) components", nCompsA);
1764
0
        nCompsA = 4;
1765
0
    }
1766
0
    obj2 = dict->lookup("Alternate");
1767
0
    std::unique_ptr<GfxColorSpace> altA;
1768
0
    if (obj2.isNull() || !(altA = GfxColorSpace::parse(nullptr, &obj2, out, state, recursion + 1))) {
1769
0
        switch (nCompsA) {
1770
0
        case 1:
1771
0
            altA = std::make_unique<GfxDeviceGrayColorSpace>();
1772
0
            break;
1773
0
        case 3:
1774
0
            altA = std::make_unique<GfxDeviceRGBColorSpace>();
1775
0
            break;
1776
0
        case 4:
1777
0
            altA = std::make_unique<GfxDeviceCMYKColorSpace>();
1778
0
            break;
1779
0
        default:
1780
0
            error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N");
1781
0
            return nullptr;
1782
0
        }
1783
0
    }
1784
0
    if (altA->getNComps() != nCompsA) {
1785
0
        error(errSyntaxWarning, -1, "Bad ICCBased color space - N doesn't match alt color space");
1786
0
        return {};
1787
0
    }
1788
0
    auto cs = std::make_unique<GfxICCBasedColorSpace>(nCompsA, std::move(altA), &iccProfileStreamA);
1789
0
    obj2 = dict->lookup("Range");
1790
0
    if (obj2.isArrayOfLength(2 * nCompsA)) {
1791
0
        for (i = 0; i < nCompsA; ++i) {
1792
0
            cs->rangeMin[i] = obj2.arrayGet(2 * i).getNumWithDefaultValue(0);
1793
0
            cs->rangeMax[i] = obj2.arrayGet(2 * i + 1).getNumWithDefaultValue(1);
1794
0
        }
1795
0
    }
1796
1797
#if USE_CMS
1798
    obj1 = arr.get(1);
1799
    if (!obj1.isStream()) {
1800
        error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)");
1801
        return {};
1802
    }
1803
    Stream *iccStream = obj1.getStream();
1804
1805
    const std::vector<unsigned char> profBuf = iccStream->toUnsignedChars(65536, 65536);
1806
    auto hp = make_GfxLCMSProfilePtr(cmsOpenProfileFromMem(profBuf.data(), profBuf.size()));
1807
    cs->profile = hp;
1808
    if (!hp) {
1809
        error(errSyntaxWarning, -1, "read ICCBased color space profile error");
1810
    } else {
1811
        cs->buildTransforms(state);
1812
    }
1813
    // put this colorSpace into cache
1814
    if (out && iccProfileStreamA != Ref::INVALID()) {
1815
        out->getIccColorSpaceCache()->put(iccProfileStreamA, cs->copyAsOwnType());
1816
    }
1817
#endif
1818
0
    return cs;
1819
0
}
1820
1821
#if USE_CMS
1822
void GfxICCBasedColorSpace::buildTransforms(GfxState *state)
1823
{
1824
    auto dhp = (state != nullptr && state->getDisplayProfile() != nullptr) ? state->getDisplayProfile() : nullptr;
1825
    if (!dhp) {
1826
        dhp = GfxState::sRGBProfile;
1827
    }
1828
    unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(profile.get()));
1829
    unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp.get()));
1830
    unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp.get()));
1831
    cmsHTRANSFORM transformA;
1832
1833
    int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
1834
    if (state != nullptr) {
1835
        cmsIntent = state->getCmsRenderingIntent();
1836
    }
1837
    if ((transformA = cmsCreateTransform(profile.get(), COLORSPACE_SH(cst) | CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), COLORSPACE_SH(dcst) | CHANNELS_SH(dNChannels) | BYTES_SH(1), cmsIntent, LCMS_FLAGS)) == nullptr) {
1838
        error(errSyntaxWarning, -1, "Can't create transform");
1839
        transform = nullptr;
1840
    } else {
1841
        transform = std::make_shared<GfxColorTransform>(transformA, cmsIntent, cst, dcst);
1842
    }
1843
    if (dcst == PT_RGB || dcst == PT_CMYK) {
1844
        // create line transform only when the display is RGB type color space
1845
        if ((transformA = cmsCreateTransform(profile.get(), CHANNELS_SH(nComps) | BYTES_SH(1), dhp.get(), (dcst == PT_RGB) ? TYPE_RGB_8 : TYPE_CMYK_8, cmsIntent, LCMS_FLAGS)) == nullptr) {
1846
            error(errSyntaxWarning, -1, "Can't create transform");
1847
            lineTransform = nullptr;
1848
        } else {
1849
            lineTransform = std::make_shared<GfxColorTransform>(transformA, cmsIntent, cst, dcst);
1850
        }
1851
    }
1852
}
1853
#endif
1854
1855
void GfxICCBasedColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
1856
0
{
1857
#if USE_CMS
1858
    if (transform != nullptr && transform->getTransformPixelType() == PT_GRAY) {
1859
        unsigned char in[gfxColorMaxComps];
1860
        unsigned char out[gfxColorMaxComps];
1861
1862
        if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
1863
            in[0] = colToByte(dblToCol(colToDbl(color.c[0]) / 100.0));
1864
            in[1] = colToByte(dblToCol((colToDbl(color.c[1]) + 128.0) / 255.0));
1865
            in[2] = colToByte(dblToCol((colToDbl(color.c[2]) + 128.0) / 255.0));
1866
        } else {
1867
            for (int i = 0; i < nComps; i++) {
1868
                in[i] = colToByte(color.c[i]);
1869
            }
1870
        }
1871
        if (nComps <= 4) {
1872
            unsigned int key = 0;
1873
            for (int j = 0; j < nComps; j++) {
1874
                key = (key << 8) + in[j];
1875
            }
1876
            auto it = cmsCache.find(key);
1877
            if (it != cmsCache.end()) {
1878
                unsigned int value = it->second;
1879
                *gray = byteToCol(value & 0xff);
1880
                return;
1881
            }
1882
        }
1883
        transform->doTransform(in, out, 1);
1884
        *gray = byteToCol(out[0]);
1885
        if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
1886
            unsigned int key = 0;
1887
            for (int j = 0; j < nComps; j++) {
1888
                key = (key << 8) + in[j];
1889
            }
1890
            unsigned int value = out[0];
1891
            cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
1892
        }
1893
    } else {
1894
        GfxRGB rgb;
1895
        getRGB(color, &rgb);
1896
        *gray = clip01(static_cast<GfxColorComp>(0.3 * rgb.r + 0.59 * rgb.g + 0.11 * rgb.b + 0.5));
1897
    }
1898
#else
1899
0
    alt->getGray(color, gray);
1900
0
#endif
1901
0
}
1902
1903
void GfxICCBasedColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
1904
0
{
1905
#if USE_CMS
1906
    if (transform != nullptr && transform->getTransformPixelType() == PT_RGB) {
1907
        unsigned char in[gfxColorMaxComps];
1908
        unsigned char out[gfxColorMaxComps];
1909
1910
        if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
1911
            in[0] = colToByte(dblToCol(colToDbl(color.c[0]) / 100.0));
1912
            in[1] = colToByte(dblToCol((colToDbl(color.c[1]) + 128.0) / 255.0));
1913
            in[2] = colToByte(dblToCol((colToDbl(color.c[2]) + 128.0) / 255.0));
1914
        } else {
1915
            for (int i = 0; i < nComps; i++) {
1916
                in[i] = colToByte(color.c[i]);
1917
            }
1918
        }
1919
        if (nComps <= 4) {
1920
            unsigned int key = 0;
1921
            for (int j = 0; j < nComps; j++) {
1922
                key = (key << 8) + in[j];
1923
            }
1924
            auto it = cmsCache.find(key);
1925
            if (it != cmsCache.end()) {
1926
                unsigned int value = it->second;
1927
                rgb->r = byteToCol(value >> 16);
1928
                rgb->g = byteToCol((value >> 8) & 0xff);
1929
                rgb->b = byteToCol(value & 0xff);
1930
                return;
1931
            }
1932
        }
1933
        transform->doTransform(in, out, 1);
1934
        rgb->r = byteToCol(out[0]);
1935
        rgb->g = byteToCol(out[1]);
1936
        rgb->b = byteToCol(out[2]);
1937
        if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
1938
            unsigned int key = 0;
1939
            for (int j = 0; j < nComps; j++) {
1940
                key = (key << 8) + in[j];
1941
            }
1942
            unsigned int value = (out[0] << 16) + (out[1] << 8) + out[2];
1943
            cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
1944
        }
1945
    } else if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
1946
        unsigned char in[gfxColorMaxComps];
1947
        unsigned char out[gfxColorMaxComps];
1948
        double c, m, y, k, c1, m1, y1, k1, r, g, b;
1949
1950
        if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
1951
            in[0] = colToByte(dblToCol(colToDbl(color.c[0]) / 100.0));
1952
            in[1] = colToByte(dblToCol((colToDbl(color.c[1]) + 128.0) / 255.0));
1953
            in[2] = colToByte(dblToCol((colToDbl(color.c[2]) + 128.0) / 255.0));
1954
        } else {
1955
            for (int i = 0; i < nComps; i++) {
1956
                in[i] = colToByte(color.c[i]);
1957
            }
1958
        }
1959
        if (nComps <= 4) {
1960
            unsigned int key = 0;
1961
            for (int j = 0; j < nComps; j++) {
1962
                key = (key << 8) + in[j];
1963
            }
1964
            auto it = cmsCache.find(key);
1965
            if (it != cmsCache.end()) {
1966
                unsigned int value = it->second;
1967
                rgb->r = byteToCol(value >> 16);
1968
                rgb->g = byteToCol((value >> 8) & 0xff);
1969
                rgb->b = byteToCol(value & 0xff);
1970
                return;
1971
            }
1972
        }
1973
        transform->doTransform(in, out, 1);
1974
        c = byteToDbl(out[0]);
1975
        m = byteToDbl(out[1]);
1976
        y = byteToDbl(out[2]);
1977
        k = byteToDbl(out[3]);
1978
        c1 = 1 - c;
1979
        m1 = 1 - m;
1980
        y1 = 1 - y;
1981
        k1 = 1 - k;
1982
        cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1983
        rgb->r = clip01(dblToCol(r));
1984
        rgb->g = clip01(dblToCol(g));
1985
        rgb->b = clip01(dblToCol(b));
1986
        if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
1987
            unsigned int key = 0;
1988
            for (int j = 0; j < nComps; j++) {
1989
                key = (key << 8) + in[j];
1990
            }
1991
            unsigned int value = (dblToByte(r) << 16) + (dblToByte(g) << 8) + dblToByte(b);
1992
            cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
1993
        }
1994
    } else {
1995
        alt->getRGB(color, rgb);
1996
    }
1997
#else
1998
0
    alt->getRGB(color, rgb);
1999
0
#endif
2000
0
}
2001
2002
void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length)
2003
0
{
2004
#if USE_CMS
2005
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) {
2006
        auto *tmp = static_cast<unsigned char *>(gmallocn(3 * length, sizeof(unsigned char)));
2007
        lineTransform->doTransform(in, tmp, length);
2008
        for (int i = 0; i < length; ++i) {
2009
            unsigned char *current = tmp + (i * 3);
2010
            out[i] = (current[0] << 16) | (current[1] << 8) | current[2];
2011
        }
2012
        gfree(tmp);
2013
    } else {
2014
        alt->getRGBLine(in, out, length);
2015
    }
2016
#else
2017
0
    alt->getRGBLine(in, out, length);
2018
0
#endif
2019
0
}
2020
2021
void GfxICCBasedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
2022
0
{
2023
#if USE_CMS
2024
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) {
2025
        auto *tmp = static_cast<unsigned char *>(gmallocn(3 * length, sizeof(unsigned char)));
2026
        lineTransform->doTransform(in, tmp, length);
2027
        unsigned char *current = tmp;
2028
        for (int i = 0; i < length; ++i) {
2029
            *out++ = *current++;
2030
            *out++ = *current++;
2031
            *out++ = *current++;
2032
        }
2033
        gfree(tmp);
2034
    } else if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) {
2035
        auto *tmp = static_cast<unsigned char *>(gmallocn(4 * length, sizeof(unsigned char)));
2036
        lineTransform->doTransform(in, tmp, length);
2037
        unsigned char *current = tmp;
2038
        double c, m, y, k, c1, m1, y1, k1, r, g, b;
2039
        for (int i = 0; i < length; ++i) {
2040
            c = byteToDbl(*current++);
2041
            m = byteToDbl(*current++);
2042
            y = byteToDbl(*current++);
2043
            k = byteToDbl(*current++);
2044
            c1 = 1 - c;
2045
            m1 = 1 - m;
2046
            y1 = 1 - y;
2047
            k1 = 1 - k;
2048
            cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
2049
            *out++ = dblToByte(r);
2050
            *out++ = dblToByte(g);
2051
            *out++ = dblToByte(b);
2052
        }
2053
        gfree(tmp);
2054
    } else {
2055
        alt->getRGBLine(in, out, length);
2056
    }
2057
#else
2058
0
    alt->getRGBLine(in, out, length);
2059
0
#endif
2060
0
}
2061
2062
void GfxICCBasedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length)
2063
0
{
2064
#if USE_CMS
2065
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_RGB) {
2066
        auto *tmp = static_cast<unsigned char *>(gmallocn(3 * length, sizeof(unsigned char)));
2067
        lineTransform->doTransform(in, tmp, length);
2068
        unsigned char *current = tmp;
2069
        for (int i = 0; i < length; ++i) {
2070
            *out++ = *current++;
2071
            *out++ = *current++;
2072
            *out++ = *current++;
2073
            *out++ = 255;
2074
        }
2075
        gfree(tmp);
2076
    } else {
2077
        alt->getRGBXLine(in, out, length);
2078
    }
2079
#else
2080
0
    alt->getRGBXLine(in, out, length);
2081
0
#endif
2082
0
}
2083
2084
void GfxICCBasedColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
2085
0
{
2086
#if USE_CMS
2087
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) {
2088
        transform->doTransform(in, out, length);
2089
    } else if (lineTransform != nullptr && nComps != 4) {
2090
        GfxColorComp c, m, y, k;
2091
        auto *tmp = static_cast<unsigned char *>(gmallocn(3 * length, sizeof(unsigned char)));
2092
        getRGBLine(in, tmp, length);
2093
        unsigned char *p = tmp;
2094
        for (int i = 0; i < length; i++) {
2095
            c = byteToCol(255 - *p++);
2096
            m = byteToCol(255 - *p++);
2097
            y = byteToCol(255 - *p++);
2098
            k = c;
2099
            if (m < k) {
2100
                k = m;
2101
            }
2102
            if (y < k) {
2103
                k = y;
2104
            }
2105
            *out++ = colToByte(c - k);
2106
            *out++ = colToByte(m - k);
2107
            *out++ = colToByte(y - k);
2108
            *out++ = colToByte(k);
2109
        }
2110
        gfree(tmp);
2111
    } else {
2112
        alt->getCMYKLine(in, out, length);
2113
    }
2114
#else
2115
0
    alt->getCMYKLine(in, out, length);
2116
0
#endif
2117
0
}
2118
2119
void GfxICCBasedColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
2120
0
{
2121
#if USE_CMS
2122
    if (lineTransform != nullptr && lineTransform->getTransformPixelType() == PT_CMYK) {
2123
        auto *tmp = static_cast<unsigned char *>(gmallocn(4 * length, sizeof(unsigned char)));
2124
        transform->doTransform(in, tmp, length);
2125
        unsigned char *p = tmp;
2126
        for (int i = 0; i < length; i++) {
2127
            for (int j = 0; j < 4; j++) {
2128
                *out++ = *p++;
2129
            }
2130
            for (int j = 4; j < SPOT_NCOMPS + 4; j++) {
2131
                *out++ = 0;
2132
            }
2133
        }
2134
        gfree(tmp);
2135
    } else if (lineTransform != nullptr && nComps != 4) {
2136
        GfxColorComp c, m, y, k;
2137
        auto *tmp = static_cast<unsigned char *>(gmallocn(3 * length, sizeof(unsigned char)));
2138
        getRGBLine(in, tmp, length);
2139
        unsigned char *p = tmp;
2140
        for (int i = 0; i < length; i++) {
2141
            for (int j = 0; j < SPOT_NCOMPS + 4; j++) {
2142
                out[j] = 0;
2143
            }
2144
            c = byteToCol(255 - *p++);
2145
            m = byteToCol(255 - *p++);
2146
            y = byteToCol(255 - *p++);
2147
            k = c;
2148
            if (m < k) {
2149
                k = m;
2150
            }
2151
            if (y < k) {
2152
                k = y;
2153
            }
2154
            out[0] = colToByte(c - k);
2155
            out[1] = colToByte(m - k);
2156
            out[2] = colToByte(y - k);
2157
            out[3] = colToByte(k);
2158
            out += (SPOT_NCOMPS + 4);
2159
        }
2160
        gfree(tmp);
2161
    } else {
2162
        alt->getDeviceNLine(in, out, length);
2163
    }
2164
#else
2165
0
    alt->getDeviceNLine(in, out, length);
2166
0
#endif
2167
0
}
2168
2169
void GfxICCBasedColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
2170
0
{
2171
#if USE_CMS
2172
    if (transform != nullptr && transform->getTransformPixelType() == PT_CMYK) {
2173
        unsigned char in[gfxColorMaxComps];
2174
        unsigned char out[gfxColorMaxComps];
2175
2176
        if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2177
            in[0] = colToByte(dblToCol(colToDbl(color.c[0]) / 100.0));
2178
            in[1] = colToByte(dblToCol((colToDbl(color.c[1]) + 128.0) / 255.0));
2179
            in[2] = colToByte(dblToCol((colToDbl(color.c[2]) + 128.0) / 255.0));
2180
        } else {
2181
            for (int i = 0; i < nComps; i++) {
2182
                in[i] = colToByte(color.c[i]);
2183
            }
2184
        }
2185
        if (nComps <= 4) {
2186
            unsigned int key = 0;
2187
            for (int j = 0; j < nComps; j++) {
2188
                key = (key << 8) + in[j];
2189
            }
2190
            auto it = cmsCache.find(key);
2191
            if (it != cmsCache.end()) {
2192
                unsigned int value = it->second;
2193
                cmyk->c = byteToCol(value >> 24);
2194
                cmyk->m = byteToCol((value >> 16) & 0xff);
2195
                cmyk->y = byteToCol((value >> 8) & 0xff);
2196
                cmyk->k = byteToCol(value & 0xff);
2197
                return;
2198
            }
2199
        }
2200
        transform->doTransform(in, out, 1);
2201
        cmyk->c = byteToCol(out[0]);
2202
        cmyk->m = byteToCol(out[1]);
2203
        cmyk->y = byteToCol(out[2]);
2204
        cmyk->k = byteToCol(out[3]);
2205
        if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2206
            unsigned int key = 0;
2207
            for (int j = 0; j < nComps; j++) {
2208
                key = (key << 8) + in[j];
2209
            }
2210
            unsigned int value = (out[0] << 24) + (out[1] << 16) + (out[2] << 8) + out[3];
2211
            cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2212
        }
2213
    } else if (nComps != 4 && transform != nullptr && transform->getTransformPixelType() == PT_RGB) {
2214
        GfxRGB rgb;
2215
        GfxColorComp c, m, y, k;
2216
2217
        getRGB(color, &rgb);
2218
        c = clip01(gfxColorComp1 - rgb.r);
2219
        m = clip01(gfxColorComp1 - rgb.g);
2220
        y = clip01(gfxColorComp1 - rgb.b);
2221
        k = c;
2222
        if (m < k) {
2223
            k = m;
2224
        }
2225
        if (y < k) {
2226
            k = y;
2227
        }
2228
        cmyk->c = c - k;
2229
        cmyk->m = m - k;
2230
        cmyk->y = y - k;
2231
        cmyk->k = k;
2232
    } else {
2233
        alt->getCMYK(color, cmyk);
2234
    }
2235
#else
2236
0
    alt->getCMYK(color, cmyk);
2237
0
#endif
2238
0
}
2239
2240
bool GfxICCBasedColorSpace::useGetRGBLine() const
2241
0
{
2242
#if USE_CMS
2243
    return lineTransform != nullptr || alt->useGetRGBLine();
2244
#else
2245
0
    return alt->useGetRGBLine();
2246
0
#endif
2247
0
}
2248
2249
bool GfxICCBasedColorSpace::useGetCMYKLine() const
2250
0
{
2251
#if USE_CMS
2252
    return lineTransform != nullptr || alt->useGetCMYKLine();
2253
#else
2254
0
    return alt->useGetCMYKLine();
2255
0
#endif
2256
0
}
2257
2258
bool GfxICCBasedColorSpace::useGetDeviceNLine() const
2259
0
{
2260
#if USE_CMS
2261
    return lineTransform != nullptr || alt->useGetDeviceNLine();
2262
#else
2263
0
    return alt->useGetDeviceNLine();
2264
0
#endif
2265
0
}
2266
2267
void GfxICCBasedColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
2268
0
{
2269
0
    GfxCMYK cmyk;
2270
0
    clearGfxColor(deviceN);
2271
0
    getCMYK(color, &cmyk);
2272
0
    deviceN->c[0] = cmyk.c;
2273
0
    deviceN->c[1] = cmyk.m;
2274
0
    deviceN->c[2] = cmyk.y;
2275
0
    deviceN->c[3] = cmyk.k;
2276
0
}
2277
2278
void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) const
2279
0
{
2280
0
    int i;
2281
2282
0
    for (i = 0; i < nComps; ++i) {
2283
0
        if (rangeMin[i] > 0) {
2284
0
            color->c[i] = dblToCol(rangeMin[i]);
2285
0
        } else if (rangeMax[i] < 0) {
2286
0
            color->c[i] = dblToCol(rangeMax[i]);
2287
0
        } else {
2288
0
            color->c[i] = 0;
2289
0
        }
2290
0
    }
2291
0
}
2292
2293
void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const
2294
0
{
2295
0
    alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
2296
2297
#if 0
2298
  // this is nominally correct, but some PDF files don't set the
2299
  // correct ranges in the ICCBased dict
2300
  int i;
2301
2302
  for (i = 0; i < nComps; ++i) {
2303
    decodeLow[i] = rangeMin[i];
2304
    decodeRange[i] = rangeMax[i] - rangeMin[i];
2305
  }
2306
#endif
2307
0
}
2308
2309
#if USE_CMS
2310
char *GfxICCBasedColorSpace::getPostScriptCSA()
2311
{
2312
    if (psCSA) {
2313
        return psCSA;
2314
    }
2315
2316
    if (!profile) {
2317
        error(errSyntaxWarning, -1, "profile is nullptr");
2318
        return nullptr;
2319
    }
2320
2321
    void *rawprofile = profile.get();
2322
    const int size = cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, nullptr, 0);
2323
    if (size == 0) {
2324
        error(errSyntaxWarning, -1, "PostScript CSA is nullptr");
2325
        return nullptr;
2326
    }
2327
2328
    psCSA = static_cast<char *>(gmalloc(size + 1));
2329
    cmsGetPostScriptCSA(cmsGetProfileContextID(rawprofile), rawprofile, getIntent(), 0, psCSA, size);
2330
    psCSA[size] = 0;
2331
2332
    return psCSA;
2333
}
2334
#endif
2335
2336
//------------------------------------------------------------------------
2337
// GfxIndexedColorSpace
2338
//------------------------------------------------------------------------
2339
2340
0
GfxIndexedColorSpace::GfxIndexedColorSpace(std::unique_ptr<GfxColorSpace> &&baseA, int indexHighA) : base(std::move(baseA))
2341
0
{
2342
0
    indexHigh = indexHighA;
2343
0
    lookup = static_cast<unsigned char *>(gmallocn((indexHigh + 1) * base->getNComps(), sizeof(unsigned char)));
2344
0
    overprintMask = base->getOverprintMask();
2345
0
}
2346
2347
GfxIndexedColorSpace::~GfxIndexedColorSpace()
2348
0
{
2349
0
    gfree(lookup);
2350
0
}
2351
2352
std::unique_ptr<GfxColorSpace> GfxIndexedColorSpace::copy() const
2353
0
{
2354
2355
0
    auto cs = std::make_unique<GfxIndexedColorSpace>(base->copy(), indexHigh);
2356
0
    memcpy(cs->lookup, lookup, (indexHigh + 1) * base->getNComps() * sizeof(unsigned char));
2357
0
    return cs;
2358
0
}
2359
2360
std::unique_ptr<GfxColorSpace> GfxIndexedColorSpace::parse(GfxResources *res, const Array &arr, OutputDev *out, GfxState *state, int recursion)
2361
0
{
2362
0
    std::unique_ptr<GfxColorSpace> baseA;
2363
0
    Object obj1;
2364
2365
0
    if (arr.getLength() != 4) {
2366
0
        error(errSyntaxWarning, -1, "Bad Indexed color space");
2367
0
        return nullptr;
2368
0
    }
2369
0
    obj1 = arr.get(1);
2370
0
    if (!(baseA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2371
0
        error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)");
2372
0
        return {};
2373
0
    }
2374
0
    obj1 = arr.get(2);
2375
0
    if (!obj1.isInt()) {
2376
0
        error(errSyntaxWarning, -1, "Bad Indexed color space (hival)");
2377
0
        return {};
2378
0
    }
2379
0
    int indexHighA = obj1.getInt();
2380
0
    if (indexHighA < 0 || indexHighA > 255) {
2381
        // the PDF spec requires indexHigh to be in [0,255] -- allowing
2382
        // values larger than 255 creates a security hole: if nComps *
2383
        // indexHigh is greater than 2^31, the loop below may overwrite
2384
        // past the end of the array
2385
0
        int previousValue = indexHighA;
2386
0
        if (indexHighA < 0) {
2387
0
            indexHighA = 0;
2388
0
        } else {
2389
0
            indexHighA = 255;
2390
0
        }
2391
0
        error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA);
2392
0
    }
2393
0
    auto cs = std::make_unique<GfxIndexedColorSpace>(std::move(baseA), indexHighA);
2394
0
    obj1 = arr.get(3);
2395
0
    const int n = cs->getBase()->getNComps();
2396
0
    if (obj1.isStream()) {
2397
0
        Stream *stream = obj1.getStream();
2398
0
        if (!stream->rewind()) {
2399
0
            error(errSyntaxWarning, -1, "Bad Indexed color space (stream rewind failed)");
2400
0
            return {};
2401
0
        }
2402
0
        for (int i = 0; i <= indexHighA; ++i) {
2403
0
            const int readChars = stream->doGetChars(n, &cs->lookup[i * n]);
2404
0
            for (int j = readChars; j < n; ++j) {
2405
0
                error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes");
2406
0
                cs->lookup[i * n + j] = 0;
2407
0
            }
2408
0
        }
2409
0
        stream->close();
2410
0
    } else if (obj1.isString()) {
2411
0
        if (obj1.getString().size() < static_cast<size_t>(indexHighA + 1) * n) {
2412
0
            error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)");
2413
0
            return {};
2414
0
        }
2415
0
        const char *s = obj1.getString().c_str();
2416
0
        for (int i = 0; i <= indexHighA; ++i) {
2417
0
            for (int j = 0; j < n; ++j) {
2418
0
                cs->lookup[i * n + j] = static_cast<unsigned char>(*s++);
2419
0
            }
2420
0
        }
2421
0
    } else {
2422
0
        error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)");
2423
0
        return {};
2424
0
    }
2425
0
    return cs;
2426
0
}
2427
2428
GfxColor *GfxIndexedColorSpace::mapColorToBase(const GfxColor &color, GfxColor *baseColor) const
2429
0
{
2430
0
    unsigned char *p;
2431
0
    double low[gfxColorMaxComps], range[gfxColorMaxComps];
2432
0
    int n, i;
2433
2434
0
    n = base->getNComps();
2435
0
    base->getDefaultRanges(low, range, indexHigh);
2436
0
    const int idx = static_cast<int>(colToDbl(color.c[0]) + 0.5) * n;
2437
0
    if (likely((idx + n - 1 < (indexHigh + 1) * base->getNComps()) && idx >= 0)) {
2438
0
        p = &lookup[idx];
2439
0
        for (i = 0; i < n; ++i) {
2440
0
            baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
2441
0
        }
2442
0
    } else {
2443
0
        for (i = 0; i < n; ++i) {
2444
0
            baseColor->c[i] = 0;
2445
0
        }
2446
0
    }
2447
0
    return baseColor;
2448
0
}
2449
2450
void GfxIndexedColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
2451
0
{
2452
0
    GfxColor color2;
2453
2454
0
    base->getGray(*mapColorToBase(color, &color2), gray);
2455
0
}
2456
2457
void GfxIndexedColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
2458
0
{
2459
0
    GfxColor color2;
2460
2461
0
    base->getRGB(*mapColorToBase(color, &color2), rgb);
2462
0
}
2463
2464
void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned int *out, int length)
2465
0
{
2466
0
    unsigned char *line;
2467
0
    int i, j, n;
2468
2469
0
    n = base->getNComps();
2470
0
    line = static_cast<unsigned char *>(gmallocn(length, n));
2471
0
    for (i = 0; i < length; i++) {
2472
0
        for (j = 0; j < n; j++) {
2473
0
            line[i * n + j] = lookup[in[i] * n + j];
2474
0
        }
2475
0
    }
2476
2477
0
    base->getRGBLine(line, out, length);
2478
2479
0
    gfree(line);
2480
0
}
2481
2482
void GfxIndexedColorSpace::getRGBLine(unsigned char *in, unsigned char *out, int length)
2483
0
{
2484
0
    unsigned char *line;
2485
0
    int i, j, n;
2486
2487
0
    n = base->getNComps();
2488
0
    line = static_cast<unsigned char *>(gmallocn(length, n));
2489
0
    for (i = 0; i < length; i++) {
2490
0
        for (j = 0; j < n; j++) {
2491
0
            line[i * n + j] = lookup[in[i] * n + j];
2492
0
        }
2493
0
    }
2494
2495
0
    base->getRGBLine(line, out, length);
2496
2497
0
    gfree(line);
2498
0
}
2499
2500
void GfxIndexedColorSpace::getRGBXLine(unsigned char *in, unsigned char *out, int length)
2501
0
{
2502
0
    unsigned char *line;
2503
0
    int i, j, n;
2504
2505
0
    n = base->getNComps();
2506
0
    line = static_cast<unsigned char *>(gmallocn(length, n));
2507
0
    for (i = 0; i < length; i++) {
2508
0
        for (j = 0; j < n; j++) {
2509
0
            line[i * n + j] = lookup[in[i] * n + j];
2510
0
        }
2511
0
    }
2512
2513
0
    base->getRGBXLine(line, out, length);
2514
2515
0
    gfree(line);
2516
0
}
2517
2518
void GfxIndexedColorSpace::getCMYKLine(unsigned char *in, unsigned char *out, int length)
2519
0
{
2520
0
    unsigned char *line;
2521
0
    int i, j, n;
2522
2523
0
    n = base->getNComps();
2524
0
    line = static_cast<unsigned char *>(gmallocn(length, n));
2525
0
    for (i = 0; i < length; i++) {
2526
0
        for (j = 0; j < n; j++) {
2527
0
            line[i * n + j] = lookup[in[i] * n + j];
2528
0
        }
2529
0
    }
2530
2531
0
    base->getCMYKLine(line, out, length);
2532
2533
0
    gfree(line);
2534
0
}
2535
2536
void GfxIndexedColorSpace::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
2537
0
{
2538
0
    unsigned char *line;
2539
0
    int i, j, n;
2540
2541
0
    n = base->getNComps();
2542
0
    line = static_cast<unsigned char *>(gmallocn(length, n));
2543
0
    for (i = 0; i < length; i++) {
2544
0
        for (j = 0; j < n; j++) {
2545
0
            line[i * n + j] = lookup[in[i] * n + j];
2546
0
        }
2547
0
    }
2548
2549
0
    base->getDeviceNLine(line, out, length);
2550
2551
0
    gfree(line);
2552
0
}
2553
2554
void GfxIndexedColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
2555
0
{
2556
0
    GfxColor color2;
2557
2558
0
    base->getCMYK(*mapColorToBase(color, &color2), cmyk);
2559
0
}
2560
2561
void GfxIndexedColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
2562
0
{
2563
0
    GfxColor color2;
2564
2565
0
    base->getDeviceN(*mapColorToBase(color, &color2), deviceN);
2566
0
}
2567
2568
void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) const
2569
0
{
2570
0
    color->c[0] = 0;
2571
0
}
2572
2573
void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, int maxImgPixel) const
2574
0
{
2575
0
    decodeLow[0] = 0;
2576
0
    decodeRange[0] = maxImgPixel;
2577
0
}
2578
2579
//------------------------------------------------------------------------
2580
// GfxSeparationColorSpace
2581
//------------------------------------------------------------------------
2582
2583
0
GfxSeparationColorSpace::GfxSeparationColorSpace(std::unique_ptr<GooString> &&nameA, std::unique_ptr<GfxColorSpace> &&altA, std::unique_ptr<Function> funcA) : name(std::move(nameA)), alt(std::move(altA))
2584
0
{
2585
0
    func = std::move(funcA);
2586
0
    nonMarking = !name->compare("None");
2587
0
    if (!name->compare("Cyan")) {
2588
0
        overprintMask = 0x01;
2589
0
    } else if (!name->compare("Magenta")) {
2590
0
        overprintMask = 0x02;
2591
0
    } else if (!name->compare("Yellow")) {
2592
0
        overprintMask = 0x04;
2593
0
    } else if (!name->compare("Black")) {
2594
0
        overprintMask = 0x08;
2595
0
    } else if (!name->compare("All")) {
2596
0
        overprintMask = 0xffffffff;
2597
0
    }
2598
0
}
2599
2600
GfxSeparationColorSpace::GfxSeparationColorSpace(std::unique_ptr<GooString> &&nameA, std::unique_ptr<GfxColorSpace> &&altA, std::unique_ptr<Function> funcA, bool nonMarkingA, unsigned int overprintMaskA, const std::vector<int> &mappingA,
2601
                                                 PrivateTag /*unused*/)
2602
0
    : name(std::move(nameA)), alt(std::move(altA))
2603
0
{
2604
0
    func = std::move(funcA);
2605
0
    nonMarking = nonMarkingA;
2606
0
    overprintMask = overprintMaskA;
2607
0
    mapping = mappingA;
2608
0
}
2609
2610
0
GfxSeparationColorSpace::~GfxSeparationColorSpace() = default;
2611
2612
std::unique_ptr<GfxColorSpace> GfxSeparationColorSpace::copy() const
2613
0
{
2614
0
    return copyAsOwnType();
2615
0
}
2616
2617
std::unique_ptr<GfxSeparationColorSpace> GfxSeparationColorSpace::copyAsOwnType() const
2618
0
{
2619
0
    return std::make_unique<GfxSeparationColorSpace>(name->copy(), alt->copy(), func->copy(), nonMarking, overprintMask, mapping);
2620
0
}
2621
2622
//~ handle the 'All' and 'None' colorants
2623
std::unique_ptr<GfxColorSpace> GfxSeparationColorSpace::parse(GfxResources *res, const Array &arr, OutputDev *out, GfxState *state, int recursion)
2624
0
{
2625
0
    std::unique_ptr<GfxColorSpace> altA;
2626
0
    std::unique_ptr<Function> funcA;
2627
0
    Object obj1;
2628
2629
0
    if (arr.getLength() != 4) {
2630
0
        error(errSyntaxWarning, -1, "Bad Separation color space");
2631
0
        return {};
2632
0
    }
2633
0
    obj1 = arr.get(1);
2634
0
    if (!obj1.isName()) {
2635
0
        error(errSyntaxWarning, -1, "Bad Separation color space (name)");
2636
0
        return {};
2637
0
    }
2638
0
    std::unique_ptr<GooString> nameA = std::make_unique<GooString>(obj1.getNameString());
2639
0
    obj1 = arr.get(2);
2640
0
    if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2641
0
        error(errSyntaxWarning, -1, "Bad Separation color space (alternate color space)");
2642
0
        return {};
2643
0
    }
2644
0
    obj1 = arr.get(3);
2645
0
    if (!(funcA = Function::parse(&obj1))) {
2646
0
        return {};
2647
0
    }
2648
0
    if (funcA->getInputSize() != 1) {
2649
0
        error(errSyntaxWarning, -1, "Bad SeparationColorSpace function");
2650
0
        return {};
2651
0
    }
2652
0
    if (altA->getNComps() > funcA->getOutputSize()) {
2653
0
        return {};
2654
0
    }
2655
0
    return std::make_unique<GfxSeparationColorSpace>(std::move(nameA), std::move(altA), std::move(funcA));
2656
0
}
2657
2658
void GfxSeparationColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
2659
0
{
2660
0
    double x;
2661
0
    double c[gfxColorMaxComps];
2662
0
    GfxColor color2;
2663
0
    int i;
2664
2665
0
    if (alt->getMode() == csDeviceGray && name->compare("Black") == 0) {
2666
0
        *gray = clip01(gfxColorComp1 - color.c[0]);
2667
0
    } else {
2668
0
        x = colToDbl(color.c[0]);
2669
0
        func->transform(&x, c);
2670
0
        for (i = 0; i < alt->getNComps(); ++i) {
2671
0
            color2.c[i] = dblToCol(c[i]);
2672
0
        }
2673
0
        alt->getGray(color2, gray);
2674
0
    }
2675
0
}
2676
2677
void GfxSeparationColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
2678
0
{
2679
0
    double x;
2680
0
    double c[gfxColorMaxComps];
2681
0
    GfxColor color2;
2682
0
    int i;
2683
2684
0
    if (alt->getMode() == csDeviceGray && name->compare("Black") == 0) {
2685
0
        rgb->r = clip01(gfxColorComp1 - color.c[0]);
2686
0
        rgb->g = clip01(gfxColorComp1 - color.c[0]);
2687
0
        rgb->b = clip01(gfxColorComp1 - color.c[0]);
2688
0
    } else {
2689
0
        x = colToDbl(color.c[0]);
2690
0
        func->transform(&x, c);
2691
0
        const int altNComps = alt->getNComps();
2692
0
        for (i = 0; i < altNComps; ++i) {
2693
0
            color2.c[i] = dblToCol(c[i]);
2694
0
        }
2695
0
        alt->getRGB(color2, rgb);
2696
0
    }
2697
0
}
2698
2699
void GfxSeparationColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
2700
0
{
2701
0
    double x;
2702
0
    double c[gfxColorMaxComps];
2703
0
    GfxColor color2;
2704
0
    int i;
2705
2706
0
    if (name->compare("Black") == 0) {
2707
0
        cmyk->c = 0;
2708
0
        cmyk->m = 0;
2709
0
        cmyk->y = 0;
2710
0
        cmyk->k = color.c[0];
2711
0
    } else if (name->compare("Cyan") == 0) {
2712
0
        cmyk->c = color.c[0];
2713
0
        cmyk->m = 0;
2714
0
        cmyk->y = 0;
2715
0
        cmyk->k = 0;
2716
0
    } else if (name->compare("Magenta") == 0) {
2717
0
        cmyk->c = 0;
2718
0
        cmyk->m = color.c[0];
2719
0
        cmyk->y = 0;
2720
0
        cmyk->k = 0;
2721
0
    } else if (name->compare("Yellow") == 0) {
2722
0
        cmyk->c = 0;
2723
0
        cmyk->m = 0;
2724
0
        cmyk->y = color.c[0];
2725
0
        cmyk->k = 0;
2726
0
    } else {
2727
0
        x = colToDbl(color.c[0]);
2728
0
        func->transform(&x, c);
2729
0
        for (i = 0; i < alt->getNComps(); ++i) {
2730
0
            color2.c[i] = dblToCol(c[i]);
2731
0
        }
2732
0
        alt->getCMYK(color2, cmyk);
2733
0
    }
2734
0
}
2735
2736
void GfxSeparationColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
2737
0
{
2738
0
    clearGfxColor(deviceN);
2739
0
    if (mapping.empty() || mapping[0] == -1) {
2740
0
        GfxCMYK cmyk;
2741
2742
0
        getCMYK(color, &cmyk);
2743
0
        deviceN->c[0] = cmyk.c;
2744
0
        deviceN->c[1] = cmyk.m;
2745
0
        deviceN->c[2] = cmyk.y;
2746
0
        deviceN->c[3] = cmyk.k;
2747
0
    } else {
2748
0
        deviceN->c[mapping[0]] = color.c[0];
2749
0
    }
2750
0
}
2751
2752
void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) const
2753
0
{
2754
0
    color->c[0] = gfxColorComp1;
2755
0
}
2756
2757
void GfxSeparationColorSpace::createMapping(std::vector<std::unique_ptr<GfxSeparationColorSpace>> *separationList, size_t maxSepComps)
2758
0
{
2759
0
    if (nonMarking) {
2760
0
        return;
2761
0
    }
2762
0
    mapping.resize(1);
2763
0
    switch (overprintMask) {
2764
0
    case 0x01:
2765
0
        mapping[0] = 0;
2766
0
        break;
2767
0
    case 0x02:
2768
0
        mapping[0] = 1;
2769
0
        break;
2770
0
    case 0x04:
2771
0
        mapping[0] = 2;
2772
0
        break;
2773
0
    case 0x08:
2774
0
        mapping[0] = 3;
2775
0
        break;
2776
0
    default:
2777
0
        unsigned int newOverprintMask = 0x10;
2778
0
        for (std::size_t i = 0; i < separationList->size(); i++) {
2779
0
            const std::unique_ptr<GfxSeparationColorSpace> &sepCS = (*separationList)[i];
2780
0
            if (!sepCS->getName()->compare(name->toStr())) {
2781
0
                if (sepCS->getFunc()->hasDifferentResultSet(func.get())) {
2782
0
                    error(errSyntaxWarning, -1, "Different functions found for '{0:t}', convert immediately", name.get());
2783
0
                    mapping.clear();
2784
0
                    return;
2785
0
                }
2786
0
                mapping[0] = i + 4;
2787
0
                overprintMask = newOverprintMask;
2788
0
                return;
2789
0
            }
2790
0
            newOverprintMask <<= 1;
2791
0
        }
2792
0
        if (separationList->size() == maxSepComps) {
2793
0
            error(errSyntaxWarning, -1, "Too many ({0:ulld}) spots, convert '{1:t}' immediately", static_cast<unsigned long long>(maxSepComps), name.get());
2794
0
            mapping.clear();
2795
0
            return;
2796
0
        }
2797
0
        mapping[0] = separationList->size() + 4;
2798
0
        separationList->push_back(copyAsOwnType());
2799
0
        overprintMask = newOverprintMask;
2800
0
        break;
2801
0
    }
2802
0
}
2803
2804
//------------------------------------------------------------------------
2805
// GfxDeviceNColorSpace
2806
//------------------------------------------------------------------------
2807
2808
GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, std::vector<std::string> &&namesA, std::unique_ptr<GfxColorSpace> &&altA, std::unique_ptr<Function> funcA, std::vector<std::unique_ptr<GfxSeparationColorSpace>> &&sepsCSA)
2809
0
    : nComps(nCompsA), names(std::move(namesA)), alt(std::move(altA))
2810
0
{
2811
0
    func = std::move(funcA);
2812
0
    sepsCS = std::move(sepsCSA);
2813
0
    nonMarking = true;
2814
0
    overprintMask = 0;
2815
0
    for (int i = 0; i < nComps; ++i) {
2816
0
        if (names[i] != "None") {
2817
0
            nonMarking = false;
2818
0
        }
2819
0
        if (names[i] == "Cyan") {
2820
0
            overprintMask |= 0x01;
2821
0
        } else if (names[i] == "Magenta") {
2822
0
            overprintMask |= 0x02;
2823
0
        } else if (names[i] == "Yellow") {
2824
0
            overprintMask |= 0x04;
2825
0
        } else if (names[i] == "Black") {
2826
0
            overprintMask |= 0x08;
2827
0
        } else if (names[i] == "All") {
2828
0
            overprintMask = 0xffffffff;
2829
0
        } else if (names[i] != "None") {
2830
0
            overprintMask = 0x0f;
2831
0
        }
2832
0
    }
2833
0
}
2834
2835
GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, const std::vector<std::string> &namesA, std::unique_ptr<GfxColorSpace> &&altA, std::unique_ptr<Function> funcA, std::vector<std::unique_ptr<GfxSeparationColorSpace>> &&sepsCSA,
2836
                                           const std::vector<int> &mappingA, bool nonMarkingA, unsigned int overprintMaskA, PrivateTag /*unused*/)
2837
0
    : nComps(nCompsA), names(namesA), alt(std::move(altA))
2838
0
{
2839
0
    func = std::move(funcA);
2840
0
    sepsCS = std::move(sepsCSA);
2841
0
    mapping = mappingA;
2842
0
    nonMarking = nonMarkingA;
2843
0
    overprintMask = overprintMaskA;
2844
0
}
2845
2846
0
GfxDeviceNColorSpace::~GfxDeviceNColorSpace() = default;
2847
2848
std::unique_ptr<GfxColorSpace> GfxDeviceNColorSpace::copy() const
2849
0
{
2850
0
    std::vector<std::unique_ptr<GfxSeparationColorSpace>> sepsCSA;
2851
0
    sepsCSA.reserve(sepsCS.size());
2852
0
    for (const std::unique_ptr<GfxSeparationColorSpace> &scs : sepsCS) {
2853
0
        if (likely(scs != nullptr)) {
2854
0
            sepsCSA.push_back(scs->copyAsOwnType());
2855
0
        }
2856
0
    }
2857
0
    return std::make_unique<GfxDeviceNColorSpace>(nComps, names, alt->copy(), func->copy(), std::move(sepsCSA), mapping, nonMarking, overprintMask);
2858
0
}
2859
2860
//~ handle the 'None' colorant
2861
std::unique_ptr<GfxColorSpace> GfxDeviceNColorSpace::parse(GfxResources *res, const Array &arr, OutputDev *out, GfxState *state, int recursion)
2862
0
{
2863
0
    int nCompsA;
2864
0
    std::vector<std::string> namesA;
2865
0
    std::unique_ptr<GfxColorSpace> altA;
2866
0
    std::unique_ptr<Function> funcA;
2867
0
    Object obj1;
2868
0
    std::vector<std::unique_ptr<GfxSeparationColorSpace>> separationList;
2869
2870
0
    if (arr.getLength() != 4 && arr.getLength() != 5) {
2871
0
        error(errSyntaxWarning, -1, "Bad DeviceN color space");
2872
0
        return nullptr;
2873
0
    }
2874
0
    obj1 = arr.get(1);
2875
0
    if (!obj1.isArray()) {
2876
0
        error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
2877
0
        return nullptr;
2878
0
    }
2879
0
    nCompsA = obj1.arrayGetLength();
2880
0
    if (nCompsA > gfxColorMaxComps) {
2881
0
        error(errSyntaxWarning, -1, "DeviceN color space with too many ({0:d} > {1:d}) components", nCompsA, gfxColorMaxComps);
2882
0
        nCompsA = gfxColorMaxComps;
2883
0
    }
2884
0
    for (int i = 0; i < nCompsA; ++i) {
2885
0
        Object obj2 = obj1.arrayGet(i);
2886
0
        if (!obj2.isName()) {
2887
0
            error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
2888
0
            nCompsA = i;
2889
0
            return nullptr;
2890
0
        }
2891
0
        namesA.emplace_back(obj2.getNameString());
2892
0
    }
2893
0
    obj1 = arr.get(2);
2894
0
    if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2895
0
        error(errSyntaxWarning, -1, "Bad DeviceN color space (alternate color space)");
2896
0
        return nullptr;
2897
0
    }
2898
0
    obj1 = arr.get(3);
2899
0
    if (!(funcA = Function::parse(&obj1))) {
2900
0
        return nullptr;
2901
0
    }
2902
0
    if (arr.getLength() == 5) {
2903
0
        obj1 = arr.get(4);
2904
0
        if (!obj1.isDict()) {
2905
0
            error(errSyntaxWarning, -1, "Bad DeviceN color space (attributes)");
2906
0
            return nullptr;
2907
0
        }
2908
0
        Dict *attribs = obj1.getDict();
2909
0
        Object obj2 = attribs->lookup("Colorants");
2910
0
        if (obj2.isDict()) {
2911
0
            Dict *colorants = obj2.getDict();
2912
0
            for (int i = 0; i < colorants->getLength(); i++) {
2913
0
                Object obj3 = colorants->getVal(i);
2914
0
                if (obj3.isArray()) {
2915
0
                    auto cs = GfxSeparationColorSpace::parse(res, *obj3.getArray(), out, state, recursion);
2916
0
                    if (cs) {
2917
0
                        separationList.push_back(std::unique_ptr<GfxSeparationColorSpace>(static_cast<GfxSeparationColorSpace *>(cs.release())));
2918
0
                    }
2919
0
                } else {
2920
0
                    error(errSyntaxWarning, -1, "Bad DeviceN color space (colorant value entry is not an Array)");
2921
0
                    return nullptr;
2922
0
                }
2923
0
            }
2924
0
        }
2925
0
    }
2926
2927
0
    if (likely(nCompsA >= funcA->getInputSize() && altA->getNComps() <= funcA->getOutputSize())) {
2928
0
        return std::make_unique<GfxDeviceNColorSpace>(nCompsA, std::move(namesA), std::move(altA), std::move(funcA), std::move(separationList));
2929
0
    }
2930
0
    return nullptr;
2931
0
}
2932
2933
void GfxDeviceNColorSpace::getGray(const GfxColor &color, GfxGray *gray) const
2934
0
{
2935
0
    double x[gfxColorMaxComps], c[gfxColorMaxComps];
2936
0
    GfxColor color2;
2937
0
    int i;
2938
2939
0
    for (i = 0; i < nComps; ++i) {
2940
0
        x[i] = colToDbl(color.c[i]);
2941
0
    }
2942
0
    func->transform(x, c);
2943
0
    for (i = 0; i < alt->getNComps(); ++i) {
2944
0
        color2.c[i] = dblToCol(c[i]);
2945
0
    }
2946
0
    alt->getGray(color2, gray);
2947
0
}
2948
2949
void GfxDeviceNColorSpace::getRGB(const GfxColor &color, GfxRGB *rgb) const
2950
0
{
2951
0
    double x[gfxColorMaxComps], c[gfxColorMaxComps];
2952
0
    GfxColor color2;
2953
0
    int i;
2954
2955
0
    for (i = 0; i < nComps; ++i) {
2956
0
        x[i] = colToDbl(color.c[i]);
2957
0
    }
2958
0
    func->transform(x, c);
2959
0
    for (i = 0; i < alt->getNComps(); ++i) {
2960
0
        color2.c[i] = dblToCol(c[i]);
2961
0
    }
2962
0
    alt->getRGB(color2, rgb);
2963
0
}
2964
2965
void GfxDeviceNColorSpace::getCMYK(const GfxColor &color, GfxCMYK *cmyk) const
2966
0
{
2967
0
    double x[gfxColorMaxComps], c[gfxColorMaxComps];
2968
0
    GfxColor color2;
2969
0
    int i;
2970
2971
0
    for (i = 0; i < nComps; ++i) {
2972
0
        x[i] = colToDbl(color.c[i]);
2973
0
    }
2974
0
    func->transform(x, c);
2975
0
    for (i = 0; i < alt->getNComps(); ++i) {
2976
0
        color2.c[i] = dblToCol(c[i]);
2977
0
    }
2978
0
    alt->getCMYK(color2, cmyk);
2979
0
}
2980
2981
void GfxDeviceNColorSpace::getDeviceN(const GfxColor &color, GfxColor *deviceN) const
2982
0
{
2983
0
    clearGfxColor(deviceN);
2984
0
    if (mapping.empty()) {
2985
0
        GfxCMYK cmyk;
2986
2987
0
        getCMYK(color, &cmyk);
2988
0
        deviceN->c[0] = cmyk.c;
2989
0
        deviceN->c[1] = cmyk.m;
2990
0
        deviceN->c[2] = cmyk.y;
2991
0
        deviceN->c[3] = cmyk.k;
2992
0
    } else {
2993
0
        for (int j = 0; j < nComps; j++) {
2994
0
            if (mapping[j] != -1) {
2995
0
                deviceN->c[mapping[j]] = color.c[j];
2996
0
            }
2997
0
        }
2998
0
    }
2999
0
}
3000
3001
void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) const
3002
0
{
3003
0
    int i;
3004
3005
0
    for (i = 0; i < nComps; ++i) {
3006
0
        color->c[i] = gfxColorComp1;
3007
0
    }
3008
0
}
3009
3010
void GfxDeviceNColorSpace::createMapping(std::vector<std::unique_ptr<GfxSeparationColorSpace>> *separationList, size_t maxSepComps)
3011
0
{
3012
0
    if (nonMarking) { // None
3013
0
        return;
3014
0
    }
3015
0
    mapping.resize(nComps);
3016
0
    unsigned int newOverprintMask = 0;
3017
0
    for (int i = 0; i < nComps; i++) {
3018
0
        const std::string &name = names[i];
3019
0
        if (name == "None") {
3020
0
            mapping[i] = -1;
3021
0
        } else if (name == "Cyan") {
3022
0
            newOverprintMask |= 0x01;
3023
0
            mapping[i] = 0;
3024
0
        } else if (name == "Magenta") {
3025
0
            newOverprintMask |= 0x02;
3026
0
            mapping[i] = 1;
3027
0
        } else if (name == "Yellow") {
3028
0
            newOverprintMask |= 0x04;
3029
0
            mapping[i] = 2;
3030
0
        } else if (name == "Black") {
3031
0
            newOverprintMask |= 0x08;
3032
0
            mapping[i] = 3;
3033
0
        } else {
3034
0
            unsigned int startOverprintMask = 0x10;
3035
0
            bool found = false;
3036
0
            const Function *sepFunc = nullptr;
3037
0
            if (nComps == 1) {
3038
0
                sepFunc = func.get();
3039
0
            } else {
3040
0
                for (const std::unique_ptr<GfxSeparationColorSpace> &sepCS : sepsCS) {
3041
0
                    if (!sepCS->getName()->compare(name)) {
3042
0
                        sepFunc = sepCS->getFunc();
3043
0
                        break;
3044
0
                    }
3045
0
                }
3046
0
            }
3047
0
            for (std::size_t j = 0; j < separationList->size(); j++) {
3048
0
                const std::unique_ptr<GfxSeparationColorSpace> &sepCS = (*separationList)[j];
3049
0
                if (!sepCS->getName()->compare(name)) {
3050
0
                    if (sepFunc != nullptr && sepCS->getFunc()->hasDifferentResultSet(sepFunc)) {
3051
0
                        error(errSyntaxWarning, -1, "Different functions found for '{0:r}', convert immediately", &name);
3052
0
                        mapping.clear();
3053
0
                        overprintMask = 0xffffffff;
3054
0
                        return;
3055
0
                    }
3056
0
                    mapping[i] = j + 4;
3057
0
                    newOverprintMask |= startOverprintMask;
3058
0
                    found = true;
3059
0
                    break;
3060
0
                }
3061
0
                startOverprintMask <<= 1;
3062
0
            }
3063
0
            if (!found) {
3064
0
                if (separationList->size() == maxSepComps) {
3065
0
                    error(errSyntaxWarning, -1, "Too many ({0:ulld}) spots, convert '{1:r}' immediately", static_cast<unsigned long long>(maxSepComps), &name);
3066
0
                    mapping.clear();
3067
0
                    overprintMask = 0xffffffff;
3068
0
                    return;
3069
0
                }
3070
0
                mapping[i] = separationList->size() + 4;
3071
0
                newOverprintMask |= startOverprintMask;
3072
0
                if (nComps == 1) {
3073
0
                    separationList->push_back(std::make_unique<GfxSeparationColorSpace>(std::make_unique<GooString>(name), alt->copy(), func->copy()));
3074
0
                } else {
3075
0
                    for (const std::unique_ptr<GfxSeparationColorSpace> &sepCS : sepsCS) {
3076
0
                        if (!sepCS->getName()->compare(name)) {
3077
0
                            found = true;
3078
0
                            separationList->push_back(sepCS->copyAsOwnType());
3079
0
                            break;
3080
0
                        }
3081
0
                    }
3082
0
                    if (!found) {
3083
0
                        error(errSyntaxWarning, -1, "DeviceN has no suitable colorant");
3084
0
                        mapping.clear();
3085
0
                        overprintMask = 0xffffffff;
3086
0
                        return;
3087
0
                    }
3088
0
                }
3089
0
            }
3090
0
        }
3091
0
    }
3092
0
    overprintMask = newOverprintMask;
3093
0
}
3094
3095
//------------------------------------------------------------------------
3096
// GfxPatternColorSpace
3097
//------------------------------------------------------------------------
3098
3099
0
GfxPatternColorSpace::GfxPatternColorSpace(std::unique_ptr<GfxColorSpace> &&underA) : under(std::move(underA)) { }
3100
3101
0
GfxPatternColorSpace::~GfxPatternColorSpace() = default;
3102
3103
std::unique_ptr<GfxColorSpace> GfxPatternColorSpace::copy() const
3104
0
{
3105
0
    return std::make_unique<GfxPatternColorSpace>(under ? under->copy() : nullptr);
3106
0
}
3107
3108
std::unique_ptr<GfxColorSpace> GfxPatternColorSpace::parse(GfxResources *res, const Array &arr, OutputDev *out, GfxState *state, int recursion)
3109
0
{
3110
0
    Object obj1;
3111
3112
0
    if (arr.getLength() != 1 && arr.getLength() != 2) {
3113
0
        error(errSyntaxWarning, -1, "Bad Pattern color space");
3114
0
        return {};
3115
0
    }
3116
0
    std::unique_ptr<GfxColorSpace> underA;
3117
0
    if (arr.getLength() == 2) {
3118
0
        obj1 = arr.get(1);
3119
0
        if (!(underA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
3120
0
            error(errSyntaxWarning, -1, "Bad Pattern color space (underlying color space)");
3121
0
            return {};
3122
0
        }
3123
0
    }
3124
0
    return std::make_unique<GfxPatternColorSpace>(std::move(underA));
3125
0
}
3126
3127
void GfxPatternColorSpace::getGray(const GfxColor & /*color*/, GfxGray *gray) const
3128
0
{
3129
0
    *gray = 0;
3130
0
}
3131
3132
void GfxPatternColorSpace::getRGB(const GfxColor & /*color*/, GfxRGB *rgb) const
3133
0
{
3134
0
    rgb->r = rgb->g = rgb->b = 0;
3135
0
}
3136
3137
void GfxPatternColorSpace::getCMYK(const GfxColor & /*color*/, GfxCMYK *cmyk) const
3138
0
{
3139
0
    cmyk->c = cmyk->m = cmyk->y = 0;
3140
0
    cmyk->k = 1;
3141
0
}
3142
3143
void GfxPatternColorSpace::getDeviceN(const GfxColor & /*color*/, GfxColor *deviceN) const
3144
0
{
3145
0
    clearGfxColor(deviceN);
3146
0
    deviceN->c[3] = 1;
3147
0
}
3148
3149
void GfxPatternColorSpace::getDefaultColor(GfxColor *color) const
3150
0
{
3151
0
    color->c[0] = 0;
3152
0
}
3153
3154
//------------------------------------------------------------------------
3155
// Pattern
3156
//------------------------------------------------------------------------
3157
3158
0
GfxPattern::GfxPattern(int typeA, int patternRefNumA) : type(typeA), patternRefNum(patternRefNumA) { }
3159
3160
0
GfxPattern::~GfxPattern() = default;
3161
3162
std::unique_ptr<GfxPattern> GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state, int patternRefNum)
3163
0
{
3164
0
    Object obj1;
3165
3166
0
    if (obj->isDict()) {
3167
0
        obj1 = obj->dictLookup("PatternType");
3168
0
    } else if (obj->isStream()) {
3169
0
        obj1 = obj->getStream()->getDict()->lookup("PatternType");
3170
0
    } else {
3171
0
        return {};
3172
0
    }
3173
0
    if (obj1.isInt() && obj1.getInt() == 1) {
3174
0
        return GfxTilingPattern::parse(obj, patternRefNum);
3175
0
    }
3176
0
    if (obj1.isInt() && obj1.getInt() == 2) {
3177
0
        return GfxShadingPattern::parse(res, obj, out, state, patternRefNum);
3178
0
    }
3179
0
    return {};
3180
0
}
3181
3182
//------------------------------------------------------------------------
3183
// GfxTilingPattern
3184
//------------------------------------------------------------------------
3185
3186
std::unique_ptr<GfxTilingPattern> GfxTilingPattern::parse(Object *patObj, int patternRefNum)
3187
0
{
3188
0
    Dict *dict;
3189
0
    int paintTypeA, tilingTypeA;
3190
0
    double xStepA, yStepA;
3191
0
    Object resDictA;
3192
0
    Object obj1;
3193
0
    int i;
3194
3195
0
    if (!patObj->isStream()) {
3196
0
        return nullptr;
3197
0
    }
3198
0
    dict = patObj->getStream()->getDict();
3199
3200
0
    obj1 = dict->lookup("PaintType");
3201
0
    if (obj1.isInt()) {
3202
0
        paintTypeA = obj1.getInt();
3203
0
    } else {
3204
0
        paintTypeA = 1;
3205
0
        error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
3206
0
    }
3207
0
    obj1 = dict->lookup("TilingType");
3208
0
    if (obj1.isInt()) {
3209
0
        tilingTypeA = obj1.getInt();
3210
0
    } else {
3211
0
        tilingTypeA = 1;
3212
0
        error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
3213
0
    }
3214
0
    std::array<double, 4> bboxA;
3215
0
    bboxA[0] = bboxA[1] = 0;
3216
0
    bboxA[2] = bboxA[3] = 1;
3217
0
    obj1 = dict->lookup("BBox");
3218
0
    if (obj1.isArrayOfLength(4)) {
3219
0
        for (i = 0; i < 4; ++i) {
3220
0
            Object obj2 = obj1.arrayGet(i);
3221
0
            if (obj2.isNum()) {
3222
0
                bboxA[i] = obj2.getNum();
3223
0
            }
3224
0
        }
3225
0
    } else {
3226
0
        error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern");
3227
0
    }
3228
0
    obj1 = dict->lookup("XStep");
3229
0
    if (obj1.isNum()) {
3230
0
        xStepA = obj1.getNum();
3231
0
    } else {
3232
0
        xStepA = 1;
3233
0
        error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern");
3234
0
    }
3235
0
    obj1 = dict->lookup("YStep");
3236
0
    if (obj1.isNum()) {
3237
0
        yStepA = obj1.getNum();
3238
0
    } else {
3239
0
        yStepA = 1;
3240
0
        error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern");
3241
0
    }
3242
0
    resDictA = dict->lookup("Resources");
3243
0
    if (!resDictA.isDict()) {
3244
0
        error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern");
3245
0
    }
3246
0
    std::array<double, 6> matrixA;
3247
0
    matrixA[0] = 1;
3248
0
    matrixA[1] = 0;
3249
0
    matrixA[2] = 0;
3250
0
    matrixA[3] = 1;
3251
0
    matrixA[4] = 0;
3252
0
    matrixA[5] = 0;
3253
0
    obj1 = dict->lookup("Matrix");
3254
0
    if (obj1.isArrayOfLength(6)) {
3255
0
        for (i = 0; i < 6; ++i) {
3256
0
            Object obj2 = obj1.arrayGet(i);
3257
0
            if (obj2.isNum()) {
3258
0
                matrixA[i] = obj2.getNum();
3259
0
            }
3260
0
        }
3261
0
    }
3262
3263
0
    auto *pattern = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, &resDictA, matrixA, patObj, patternRefNum);
3264
0
    return std::unique_ptr<GfxTilingPattern>(pattern);
3265
0
}
3266
3267
GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, const std::array<double, 4> &bboxA, double xStepA, double yStepA, const Object *resDictA, const std::array<double, 6> &matrixA, const Object *contentStreamA,
3268
                                   int patternRefNumA)
3269
0
    : GfxPattern(1, patternRefNumA), bbox(bboxA), matrix(matrixA)
3270
0
{
3271
0
    paintType = paintTypeA;
3272
0
    tilingType = tilingTypeA;
3273
0
    xStep = xStepA;
3274
0
    yStep = yStepA;
3275
0
    resDict = resDictA->copy();
3276
0
    contentStream = contentStreamA->copy();
3277
0
}
3278
3279
0
GfxTilingPattern::~GfxTilingPattern() = default;
3280
3281
std::unique_ptr<GfxPattern> GfxTilingPattern::copy() const
3282
0
{
3283
0
    auto *pattern = new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, &resDict, matrix, &contentStream, getPatternRefNum());
3284
0
    return std::unique_ptr<GfxTilingPattern>(pattern);
3285
0
}
3286
3287
//------------------------------------------------------------------------
3288
// GfxShadingPattern
3289
//------------------------------------------------------------------------
3290
3291
std::unique_ptr<GfxShadingPattern> GfxShadingPattern::parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state, int patternRefNum)
3292
0
{
3293
0
    Dict *dict;
3294
0
    Object obj1;
3295
0
    int i;
3296
3297
0
    if (!patObj->isDict()) {
3298
0
        return {};
3299
0
    }
3300
0
    dict = patObj->getDict();
3301
3302
0
    obj1 = dict->lookup("Shading");
3303
0
    std::unique_ptr<GfxShading> shadingA = GfxShading::parse(res, &obj1, out, state);
3304
0
    if (!shadingA) {
3305
0
        return {};
3306
0
    }
3307
3308
0
    std::array<double, 6> matrixA;
3309
0
    matrixA[0] = 1;
3310
0
    matrixA[1] = 0;
3311
0
    matrixA[2] = 0;
3312
0
    matrixA[3] = 1;
3313
0
    matrixA[4] = 0;
3314
0
    matrixA[5] = 0;
3315
0
    obj1 = dict->lookup("Matrix");
3316
0
    if (obj1.isArrayOfLength(6)) {
3317
0
        for (i = 0; i < 6; ++i) {
3318
0
            Object obj2 = obj1.arrayGet(i);
3319
0
            if (obj2.isNum()) {
3320
0
                matrixA[i] = obj2.getNum();
3321
0
            }
3322
0
        }
3323
0
    }
3324
3325
0
    auto *pattern = new GfxShadingPattern(std::move(shadingA), matrixA, patternRefNum);
3326
0
    return std::unique_ptr<GfxShadingPattern>(pattern);
3327
0
}
3328
3329
0
GfxShadingPattern::GfxShadingPattern(std::unique_ptr<GfxShading> &&shadingA, const std::array<double, 6> &matrixA, int patternRefNumA) : GfxPattern(2, patternRefNumA), shading(std::move(shadingA)), matrix(matrixA) { }
3330
3331
0
GfxShadingPattern::~GfxShadingPattern() = default;
3332
3333
std::unique_ptr<GfxPattern> GfxShadingPattern::copy() const
3334
0
{
3335
0
    auto *pattern = new GfxShadingPattern(shading->copy(), matrix, getPatternRefNum());
3336
0
    return std::unique_ptr<GfxShadingPattern>(pattern);
3337
0
}
3338
3339
//------------------------------------------------------------------------
3340
// GfxShading
3341
//------------------------------------------------------------------------
3342
3343
GfxShading::GfxShading(int typeA)
3344
0
{
3345
0
    type = static_cast<ShadingType>(typeA);
3346
0
}
3347
3348
GfxShading::GfxShading(const GfxShading *shading)
3349
0
{
3350
0
    int i;
3351
3352
0
    type = shading->type;
3353
0
    colorSpace = shading->colorSpace->copy();
3354
0
    for (i = 0; i < gfxColorMaxComps; ++i) {
3355
0
        background.c[i] = shading->background.c[i];
3356
0
    }
3357
0
    hasBackground = shading->hasBackground;
3358
0
    bbox_xMin = shading->bbox_xMin;
3359
0
    bbox_yMin = shading->bbox_yMin;
3360
0
    bbox_xMax = shading->bbox_xMax;
3361
0
    bbox_yMax = shading->bbox_yMax;
3362
0
    hasBBox = shading->hasBBox;
3363
0
}
3364
3365
0
GfxShading::~GfxShading() = default;
3366
3367
std::unique_ptr<GfxShading> GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state)
3368
0
{
3369
0
    Dict *dict;
3370
0
    int typeA;
3371
0
    Object obj1;
3372
3373
0
    if (obj->isDict()) {
3374
0
        dict = obj->getDict();
3375
0
    } else if (obj->isStream()) {
3376
0
        dict = obj->getStream()->getDict();
3377
0
    } else {
3378
0
        return {};
3379
0
    }
3380
3381
0
    obj1 = dict->lookup("ShadingType");
3382
0
    if (!obj1.isInt()) {
3383
0
        error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary");
3384
0
        return {};
3385
0
    }
3386
0
    typeA = obj1.getInt();
3387
3388
0
    switch (typeA) {
3389
0
    case 1:
3390
0
        return GfxFunctionShading::parse(res, dict, out, state);
3391
0
        break;
3392
0
    case 2:
3393
0
        return GfxAxialShading::parse(res, dict, out, state);
3394
0
        break;
3395
0
    case 3:
3396
0
        return GfxRadialShading::parse(res, dict, out, state);
3397
0
        break;
3398
0
    case 4:
3399
0
        if (obj->isStream()) {
3400
0
            return GfxGouraudTriangleShading::parse(res, 4, dict, obj->getStream(), out, state);
3401
0
        } else {
3402
0
            error(errSyntaxWarning, -1, "Invalid Type 4 shading object");
3403
0
        }
3404
0
        break;
3405
0
    case 5:
3406
0
        if (obj->isStream()) {
3407
0
            return GfxGouraudTriangleShading::parse(res, 5, dict, obj->getStream(), out, state);
3408
0
        } else {
3409
0
            error(errSyntaxWarning, -1, "Invalid Type 5 shading object");
3410
0
        }
3411
0
        break;
3412
0
    case 6:
3413
0
        if (obj->isStream()) {
3414
0
            return GfxPatchMeshShading::parse(res, 6, dict, obj->getStream(), out, state);
3415
0
        } else {
3416
0
            error(errSyntaxWarning, -1, "Invalid Type 6 shading object");
3417
0
        }
3418
0
        break;
3419
0
    case 7:
3420
0
        if (obj->isStream()) {
3421
0
            return GfxPatchMeshShading::parse(res, 7, dict, obj->getStream(), out, state);
3422
0
        } else {
3423
0
            error(errSyntaxWarning, -1, "Invalid Type 7 shading object");
3424
0
        }
3425
0
        break;
3426
0
    default:
3427
0
        error(errSyntaxWarning, -1, "Unimplemented shading type {0:d}", typeA);
3428
0
    }
3429
0
    return {};
3430
0
}
3431
3432
bool GfxShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
3433
0
{
3434
0
    Object obj1;
3435
0
    int i;
3436
3437
0
    obj1 = dict->lookup("ColorSpace");
3438
0
    if (!(colorSpace = GfxColorSpace::parse(res, &obj1, out, state))) {
3439
0
        error(errSyntaxWarning, -1, "Bad color space in shading dictionary");
3440
0
        return false;
3441
0
    }
3442
3443
0
    for (i = 0; i < gfxColorMaxComps; ++i) {
3444
0
        background.c[i] = 0;
3445
0
    }
3446
0
    hasBackground = false;
3447
0
    obj1 = dict->lookup("Background");
3448
0
    if (obj1.isArray()) {
3449
0
        if (obj1.arrayGetLength() == colorSpace->getNComps()) {
3450
0
            hasBackground = true;
3451
0
            for (i = 0; i < colorSpace->getNComps(); ++i) {
3452
0
                Object obj2 = obj1.arrayGet(i);
3453
0
                background.c[i] = dblToCol(obj2.getNum(&hasBackground));
3454
0
            }
3455
0
            if (!hasBackground) {
3456
0
                error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
3457
0
            }
3458
0
        } else {
3459
0
            error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
3460
0
        }
3461
0
    }
3462
3463
0
    bbox_xMin = bbox_yMin = bbox_xMax = bbox_yMax = 0;
3464
0
    hasBBox = false;
3465
0
    obj1 = dict->lookup("BBox");
3466
0
    if (obj1.isArray()) {
3467
0
        if (obj1.arrayGetLength() == 4) {
3468
0
            hasBBox = true;
3469
0
            bbox_xMin = obj1.arrayGet(0).getNum(&hasBBox);
3470
0
            bbox_yMin = obj1.arrayGet(1).getNum(&hasBBox);
3471
0
            bbox_xMax = obj1.arrayGet(2).getNum(&hasBBox);
3472
0
            bbox_yMax = obj1.arrayGet(3).getNum(&hasBBox);
3473
0
            if (!hasBBox) {
3474
0
                error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)");
3475
0
            }
3476
0
        } else {
3477
0
            error(errSyntaxWarning, -1, "Bad BBox in shading dictionary");
3478
0
        }
3479
0
    }
3480
3481
0
    return true;
3482
0
}
3483
3484
//------------------------------------------------------------------------
3485
// GfxFunctionShading
3486
//------------------------------------------------------------------------
3487
3488
0
GfxFunctionShading::GfxFunctionShading(double x0A, double y0A, double x1A, double y1A, const std::array<double, 6> &matrixA, std::vector<std::unique_ptr<Function>> &&funcsA) : GfxShading(1), matrix(matrixA), funcs(std::move(funcsA))
3489
0
{
3490
0
    x0 = x0A;
3491
0
    y0 = y0A;
3492
0
    x1 = x1A;
3493
0
    y1 = y1A;
3494
0
}
3495
3496
0
GfxFunctionShading::GfxFunctionShading(const GfxFunctionShading *shading) : GfxShading(shading), matrix(shading->matrix)
3497
0
{
3498
0
    x0 = shading->x0;
3499
0
    y0 = shading->y0;
3500
0
    x1 = shading->x1;
3501
0
    y1 = shading->y1;
3502
0
    for (const auto &f : shading->funcs) {
3503
0
        funcs.emplace_back(f->copy());
3504
0
    }
3505
0
}
3506
3507
0
GfxFunctionShading::~GfxFunctionShading() = default;
3508
3509
std::unique_ptr<GfxFunctionShading> GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
3510
0
{
3511
0
    double x0A, y0A, x1A, y1A;
3512
0
    std::vector<std::unique_ptr<Function>> funcsA;
3513
0
    Object obj1;
3514
0
    int i;
3515
3516
0
    x0A = y0A = 0;
3517
0
    x1A = y1A = 1;
3518
0
    obj1 = dict->lookup("Domain");
3519
0
    if (obj1.isArrayOfLength(4)) {
3520
0
        bool decodeOk = true;
3521
0
        x0A = obj1.arrayGet(0).getNum(&decodeOk);
3522
0
        x1A = obj1.arrayGet(1).getNum(&decodeOk);
3523
0
        y0A = obj1.arrayGet(2).getNum(&decodeOk);
3524
0
        y1A = obj1.arrayGet(3).getNum(&decodeOk);
3525
3526
0
        if (!decodeOk) {
3527
0
            error(errSyntaxWarning, -1, "Invalid Domain array in function shading dictionary");
3528
0
            return {};
3529
0
        }
3530
0
    }
3531
3532
0
    std::array<double, 6> matrixA;
3533
0
    matrixA[0] = 1;
3534
0
    matrixA[1] = 0;
3535
0
    matrixA[2] = 0;
3536
0
    matrixA[3] = 1;
3537
0
    matrixA[4] = 0;
3538
0
    matrixA[5] = 0;
3539
0
    obj1 = dict->lookup("Matrix");
3540
0
    if (obj1.isArrayOfLength(6)) {
3541
0
        bool decodeOk = true;
3542
0
        matrixA[0] = obj1.arrayGet(0).getNum(&decodeOk);
3543
0
        matrixA[1] = obj1.arrayGet(1).getNum(&decodeOk);
3544
0
        matrixA[2] = obj1.arrayGet(2).getNum(&decodeOk);
3545
0
        matrixA[3] = obj1.arrayGet(3).getNum(&decodeOk);
3546
0
        matrixA[4] = obj1.arrayGet(4).getNum(&decodeOk);
3547
0
        matrixA[5] = obj1.arrayGet(5).getNum(&decodeOk);
3548
3549
0
        if (!decodeOk) {
3550
0
            error(errSyntaxWarning, -1, "Invalid Matrix array in function shading dictionary");
3551
0
            return {};
3552
0
        }
3553
0
    }
3554
3555
0
    obj1 = dict->lookup("Function");
3556
0
    if (obj1.isArray()) {
3557
0
        const int nFuncsA = obj1.arrayGetLength();
3558
0
        if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) {
3559
0
            error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3560
0
            return {};
3561
0
        }
3562
0
        for (i = 0; i < nFuncsA; ++i) {
3563
0
            Object obj2 = obj1.arrayGet(i);
3564
0
            std::unique_ptr<Function> f = Function::parse(&obj2);
3565
0
            if (!f) {
3566
0
                return {};
3567
0
            }
3568
0
            funcsA.emplace_back(std::move(f));
3569
0
        }
3570
0
    } else {
3571
0
        std::unique_ptr<Function> f = Function::parse(&obj1);
3572
0
        if (!f) {
3573
0
            return {};
3574
0
        }
3575
0
        funcsA.emplace_back(std::move(f));
3576
0
    }
3577
3578
0
    auto shading = std::make_unique<GfxFunctionShading>(x0A, y0A, x1A, y1A, matrixA, std::move(funcsA));
3579
0
    if (!shading->init(res, dict, out, state)) {
3580
0
        return {};
3581
0
    }
3582
0
    return shading;
3583
0
}
3584
3585
bool GfxFunctionShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
3586
0
{
3587
0
    const bool parentInit = GfxShading::init(res, dict, out, state);
3588
0
    if (!parentInit) {
3589
0
        return false;
3590
0
    }
3591
3592
    // funcs needs to be one of the two:
3593
    //  * One function 2-in -> nComps-out
3594
    //  * nComps functions 2-in -> 1-out
3595
0
    const int nComps = colorSpace->getNComps();
3596
0
    const int nFuncs = funcs.size();
3597
0
    if (nFuncs == 1) {
3598
0
        if (funcs[0]->getInputSize() != 2) {
3599
0
            error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2");
3600
0
            return false;
3601
0
        }
3602
0
        if (funcs[0]->getOutputSize() != nComps) {
3603
0
            error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size");
3604
0
            return false;
3605
0
        }
3606
0
    } else if (nFuncs == nComps) {
3607
0
        for (const std::unique_ptr<Function> &f : funcs) {
3608
0
            if (f->getInputSize() != 2) {
3609
0
                error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2");
3610
0
                return false;
3611
0
            }
3612
0
            if (f->getOutputSize() != 1) {
3613
0
                error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size");
3614
0
                return false;
3615
0
            }
3616
0
        }
3617
0
    } else {
3618
0
        return false;
3619
0
    }
3620
3621
0
    return true;
3622
0
}
3623
3624
std::unique_ptr<GfxShading> GfxFunctionShading::copy() const
3625
0
{
3626
0
    return std::make_unique<GfxFunctionShading>(this);
3627
0
}
3628
3629
void GfxFunctionShading::getColor(double x, double y, GfxColor *color) const
3630
0
{
3631
0
    double in[2], out[gfxColorMaxComps];
3632
3633
    // NB: there can be one function with n outputs or n functions with
3634
    // one output each (where n = number of color components)
3635
0
    for (double &i : out) {
3636
0
        i = 0;
3637
0
    }
3638
0
    in[0] = x;
3639
0
    in[1] = y;
3640
0
    for (int i = 0; i < getNFuncs(); ++i) {
3641
0
        funcs[i]->transform(in, &out[i]);
3642
0
    }
3643
0
    for (int i = 0; i < gfxColorMaxComps; ++i) {
3644
0
        color->c[i] = dblToCol(out[i]);
3645
0
    }
3646
0
}
3647
3648
//------------------------------------------------------------------------
3649
// GfxUnivariateShading
3650
//------------------------------------------------------------------------
3651
3652
0
GfxUnivariateShading::GfxUnivariateShading(int typeA, double t0A, double t1A, std::vector<std::unique_ptr<Function>> &&funcsA, bool extend0A, bool extend1A) : GfxShading(typeA), funcs(std::move(funcsA))
3653
0
{
3654
0
    t0 = t0A;
3655
0
    t1 = t1A;
3656
0
    extend0 = extend0A;
3657
0
    extend1 = extend1A;
3658
3659
0
    cacheSize = 0;
3660
0
    lastMatch = 0;
3661
0
    cacheBounds = nullptr;
3662
0
    cacheCoeff = nullptr;
3663
0
    cacheValues = nullptr;
3664
0
}
3665
3666
0
GfxUnivariateShading::GfxUnivariateShading(const GfxUnivariateShading *shading) : GfxShading(shading)
3667
0
{
3668
0
    t0 = shading->t0;
3669
0
    t1 = shading->t1;
3670
0
    for (const auto &f : shading->funcs) {
3671
0
        funcs.emplace_back(f->copy());
3672
0
    }
3673
0
    extend0 = shading->extend0;
3674
0
    extend1 = shading->extend1;
3675
3676
0
    cacheSize = 0;
3677
0
    lastMatch = 0;
3678
0
    cacheBounds = nullptr;
3679
0
    cacheCoeff = nullptr;
3680
0
    cacheValues = nullptr;
3681
0
}
3682
3683
GfxUnivariateShading::~GfxUnivariateShading()
3684
0
{
3685
0
    gfree(cacheBounds);
3686
0
}
3687
3688
int GfxUnivariateShading::getColor(double t, GfxColor *color)
3689
0
{
3690
0
    double out[gfxColorMaxComps];
3691
3692
    // NB: there can be one function with n outputs or n functions with
3693
    // one output each (where n = number of color components)
3694
0
    const int nComps = getNFuncs() * funcs[0]->getOutputSize();
3695
3696
0
    if (cacheSize > 0) {
3697
0
        double x, ix, *l, *u, *upper;
3698
3699
0
        if (cacheBounds[lastMatch - 1] >= t) {
3700
0
            upper = std::lower_bound(cacheBounds, cacheBounds + lastMatch - 1, t);
3701
0
            lastMatch = static_cast<int>(upper - cacheBounds);
3702
0
            lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3703
0
        } else if (cacheBounds[lastMatch] < t) {
3704
0
            upper = std::lower_bound(cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t);
3705
0
            lastMatch = static_cast<int>(upper - cacheBounds);
3706
0
            lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3707
0
        }
3708
3709
0
        x = (t - cacheBounds[lastMatch - 1]) * cacheCoeff[lastMatch];
3710
0
        ix = 1.0 - x;
3711
0
        u = cacheValues + lastMatch * nComps;
3712
0
        l = u - nComps;
3713
3714
0
        for (int i = 0; i < nComps; ++i) {
3715
0
            out[i] = ix * l[i] + x * u[i];
3716
0
        }
3717
0
    } else {
3718
0
        for (int i = 0; i < nComps; ++i) {
3719
0
            out[i] = 0;
3720
0
        }
3721
0
        for (int i = 0; i < getNFuncs(); ++i) {
3722
0
            funcs[i]->transform(&t, &out[i]);
3723
0
        }
3724
0
    }
3725
3726
0
    for (int i = 0; i < nComps; ++i) {
3727
0
        color->c[i] = dblToCol(out[i]);
3728
0
    }
3729
0
    return nComps;
3730
0
}
3731
3732
void GfxUnivariateShading::setupCache(const Matrix *ctm, double xMin, double yMin, double xMax, double yMax)
3733
0
{
3734
0
    double sMin, sMax, tMin, tMax, upperBound;
3735
0
    int i, j, nComps, maxSize;
3736
3737
0
    gfree(cacheBounds);
3738
0
    cacheBounds = nullptr;
3739
0
    cacheSize = 0;
3740
3741
0
    if (unlikely(getNFuncs() < 1)) {
3742
0
        return;
3743
0
    }
3744
3745
    // NB: there can be one function with n outputs or n functions with
3746
    // one output each (where n = number of color components)
3747
0
    nComps = getNFuncs() * funcs[0]->getOutputSize();
3748
3749
0
    getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
3750
0
    upperBound = ctm->norm() * getDistance(sMin, sMax);
3751
0
    maxSize = static_cast<int>(ceil(upperBound));
3752
0
    maxSize = std::max<int>(maxSize, 2);
3753
3754
0
    {
3755
0
        double x[4], y[4];
3756
3757
0
        ctm->transform(xMin, yMin, &x[0], &y[0]);
3758
0
        ctm->transform(xMax, yMin, &x[1], &y[1]);
3759
0
        ctm->transform(xMin, yMax, &x[2], &y[2]);
3760
0
        ctm->transform(xMax, yMax, &x[3], &y[3]);
3761
3762
0
        xMin = xMax = x[0];
3763
0
        yMin = yMax = y[0];
3764
0
        for (i = 1; i < 4; i++) {
3765
0
            xMin = std::min<double>(xMin, x[i]);
3766
0
            yMin = std::min<double>(yMin, y[i]);
3767
0
            xMax = std::max<double>(xMax, x[i]);
3768
0
            yMax = std::max<double>(yMax, y[i]);
3769
0
        }
3770
0
    }
3771
3772
0
    if (maxSize > (xMax - xMin) * (yMax - yMin)) {
3773
0
        return;
3774
0
    }
3775
3776
0
    if (t0 < t1) {
3777
0
        tMin = t0 + sMin * (t1 - t0);
3778
0
        tMax = t0 + sMax * (t1 - t0);
3779
0
    } else {
3780
0
        tMin = t0 + sMax * (t1 - t0);
3781
0
        tMax = t0 + sMin * (t1 - t0);
3782
0
    }
3783
3784
0
    cacheBounds = static_cast<double *>(gmallocn_checkoverflow(maxSize, sizeof(double) * (nComps + 2)));
3785
0
    if (unlikely(!cacheBounds)) {
3786
0
        return;
3787
0
    }
3788
0
    cacheCoeff = cacheBounds + maxSize;
3789
0
    cacheValues = cacheCoeff + maxSize;
3790
3791
0
    if (cacheSize != 0) {
3792
0
        for (j = 0; j < cacheSize; ++j) {
3793
0
            cacheCoeff[j] = 1 / (cacheBounds[j + 1] - cacheBounds[j]);
3794
0
        }
3795
0
    } else if (tMax != tMin) {
3796
0
        double step = (tMax - tMin) / (maxSize - 1);
3797
0
        double coeff = (maxSize - 1) / (tMax - tMin);
3798
3799
0
        cacheSize = maxSize;
3800
3801
0
        for (j = 0; j < cacheSize; ++j) {
3802
0
            cacheBounds[j] = tMin + j * step;
3803
0
            cacheCoeff[j] = coeff;
3804
3805
0
            for (i = 0; i < nComps; ++i) {
3806
0
                cacheValues[j * nComps + i] = 0;
3807
0
            }
3808
0
            for (i = 0; i < getNFuncs(); ++i) {
3809
0
                funcs[i]->transform(&cacheBounds[j], &cacheValues[j * nComps + i]);
3810
0
            }
3811
0
        }
3812
0
    }
3813
3814
0
    lastMatch = 1;
3815
0
}
3816
3817
bool GfxUnivariateShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
3818
0
{
3819
0
    const bool parentInit = GfxShading::init(res, dict, out, state);
3820
0
    if (!parentInit) {
3821
0
        return false;
3822
0
    }
3823
3824
    // funcs needs to be one of the two:
3825
    //  * One function 1-in -> nComps-out
3826
    //  * nComps functions 1-in -> 1-out
3827
0
    const int nComps = colorSpace->getNComps();
3828
0
    const int nFuncs = funcs.size();
3829
0
    if (nFuncs == 1) {
3830
0
        if (funcs[0]->getInputSize() != 1) {
3831
0
            error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2");
3832
0
            return false;
3833
0
        }
3834
0
        if (funcs[0]->getOutputSize() != nComps) {
3835
0
            error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size");
3836
0
            return false;
3837
0
        }
3838
0
    } else if (nFuncs == nComps) {
3839
0
        for (const std::unique_ptr<Function> &f : funcs) {
3840
0
            if (f->getInputSize() != 1) {
3841
0
                error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2");
3842
0
                return false;
3843
0
            }
3844
0
            if (f->getOutputSize() != 1) {
3845
0
                error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size");
3846
0
                return false;
3847
0
            }
3848
0
        }
3849
0
    } else {
3850
0
        return false;
3851
0
    }
3852
3853
0
    return true;
3854
0
}
3855
3856
//------------------------------------------------------------------------
3857
// GfxAxialShading
3858
//------------------------------------------------------------------------
3859
3860
GfxAxialShading::GfxAxialShading(double x0A, double y0A, double x1A, double y1A, double t0A, double t1A, std::vector<std::unique_ptr<Function>> &&funcsA, bool extend0A, bool extend1A)
3861
0
    : GfxUnivariateShading(2, t0A, t1A, std::move(funcsA), extend0A, extend1A)
3862
0
{
3863
0
    x0 = x0A;
3864
0
    y0 = y0A;
3865
0
    x1 = x1A;
3866
0
    y1 = y1A;
3867
0
}
3868
3869
0
GfxAxialShading::GfxAxialShading(const GfxAxialShading *shading) : GfxUnivariateShading(shading)
3870
0
{
3871
0
    x0 = shading->x0;
3872
0
    y0 = shading->y0;
3873
0
    x1 = shading->x1;
3874
0
    y1 = shading->y1;
3875
0
}
3876
3877
GfxAxialShading::~GfxAxialShading() = default;
3878
3879
std::unique_ptr<GfxAxialShading> GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
3880
0
{
3881
0
    double x0A, y0A, x1A, y1A;
3882
0
    double t0A, t1A;
3883
0
    std::vector<std::unique_ptr<Function>> funcsA;
3884
0
    bool extend0A, extend1A;
3885
0
    Object obj1;
3886
3887
0
    x0A = y0A = x1A = y1A = 0;
3888
0
    obj1 = dict->lookup("Coords");
3889
0
    if (obj1.isArrayOfLength(4)) {
3890
0
        x0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
3891
0
        y0A = obj1.arrayGet(1).getNumWithDefaultValue(0);
3892
0
        x1A = obj1.arrayGet(2).getNumWithDefaultValue(0);
3893
0
        y1A = obj1.arrayGet(3).getNumWithDefaultValue(0);
3894
0
    } else {
3895
0
        error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
3896
0
        return {};
3897
0
    }
3898
3899
0
    t0A = 0;
3900
0
    t1A = 1;
3901
0
    obj1 = dict->lookup("Domain");
3902
0
    if (obj1.isArrayOfLength(2)) {
3903
0
        t0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
3904
0
        t1A = obj1.arrayGet(1).getNumWithDefaultValue(1);
3905
0
    }
3906
3907
0
    obj1 = dict->lookup("Function");
3908
0
    if (obj1.isArray()) {
3909
0
        const int nFuncsA = obj1.arrayGetLength();
3910
0
        if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) {
3911
0
            error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3912
0
            return {};
3913
0
        }
3914
0
        for (int i = 0; i < nFuncsA; ++i) {
3915
0
            Object obj2 = obj1.arrayGet(i);
3916
0
            std::unique_ptr<Function> f = Function::parse(&obj2);
3917
0
            if (!f) {
3918
0
                return {};
3919
0
            }
3920
0
            funcsA.emplace_back(std::move(f));
3921
0
        }
3922
0
    } else {
3923
0
        std::unique_ptr<Function> f = Function::parse(&obj1);
3924
0
        if (!f) {
3925
0
            return {};
3926
0
        }
3927
0
        funcsA.emplace_back(std::move(f));
3928
0
    }
3929
3930
0
    extend0A = extend1A = false;
3931
0
    obj1 = dict->lookup("Extend");
3932
0
    if (obj1.isArrayOfLength(2)) {
3933
0
        Object obj2 = obj1.arrayGet(0);
3934
0
        if (obj2.isBool()) {
3935
0
            extend0A = obj2.getBool();
3936
0
        } else {
3937
0
            error(errSyntaxWarning, -1, "Invalid axial shading extend (0)");
3938
0
        }
3939
0
        obj2 = obj1.arrayGet(1);
3940
0
        if (obj2.isBool()) {
3941
0
            extend1A = obj2.getBool();
3942
0
        } else {
3943
0
            error(errSyntaxWarning, -1, "Invalid axial shading extend (1)");
3944
0
        }
3945
0
    }
3946
3947
0
    auto shading = std::make_unique<GfxAxialShading>(x0A, y0A, x1A, y1A, t0A, t1A, std::move(funcsA), extend0A, extend1A);
3948
0
    if (!shading->init(res, dict, out, state)) {
3949
0
        return {};
3950
0
    }
3951
0
    return shading;
3952
0
}
3953
3954
std::unique_ptr<GfxShading> GfxAxialShading::copy() const
3955
0
{
3956
0
    return std::make_unique<GfxAxialShading>(this);
3957
0
}
3958
3959
double GfxAxialShading::getDistance(double sMin, double sMax) const
3960
0
{
3961
0
    double xMin, yMin, xMax, yMax;
3962
3963
0
    xMin = x0 + sMin * (x1 - x0);
3964
0
    yMin = y0 + sMin * (y1 - y0);
3965
0
    xMax = x0 + sMax * (x1 - x0);
3966
0
    yMax = y0 + sMax * (y1 - y0);
3967
3968
0
    return hypot(xMax - xMin, yMax - yMin);
3969
0
}
3970
3971
void GfxAxialShading::getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax)
3972
0
{
3973
0
    double pdx, pdy, invsqnorm, tdx, tdy, t, range[2];
3974
3975
    // Linear gradients are orthogonal to the line passing through their
3976
    // extremes. Because of convexity, the parameter range can be
3977
    // computed as the convex hull (one the real line) of the parameter
3978
    // values of the 4 corners of the box.
3979
    //
3980
    // The parameter value t for a point (x,y) can be computed as:
3981
    //
3982
    //   t = (p2 - p1) . (x,y) / |p2 - p1|^2
3983
    //
3984
    // t0  is the t value for the top left corner
3985
    // tdx is the difference between left and right corners
3986
    // tdy is the difference between top and bottom corners
3987
3988
0
    pdx = x1 - x0;
3989
0
    pdy = y1 - y0;
3990
0
    const double invsqnorm_denominator = (pdx * pdx + pdy * pdy);
3991
0
    if (unlikely(invsqnorm_denominator == 0)) {
3992
0
        *lower = 0;
3993
0
        *upper = 0;
3994
0
        return;
3995
0
    }
3996
0
    invsqnorm = 1.0 / invsqnorm_denominator;
3997
0
    pdx *= invsqnorm;
3998
0
    pdy *= invsqnorm;
3999
4000
0
    t = (xMin - x0) * pdx + (yMin - y0) * pdy;
4001
0
    tdx = (xMax - xMin) * pdx;
4002
0
    tdy = (yMax - yMin) * pdy;
4003
4004
    // Because of the linearity of the t value, tdx can simply be added
4005
    // the t0 to move along the top edge. After this, *lower and *upper
4006
    // represent the parameter range for the top edge, so extending it
4007
    // to include the whole box simply requires adding tdy to the
4008
    // correct extreme.
4009
4010
0
    range[0] = range[1] = t;
4011
0
    if (tdx < 0) {
4012
0
        range[0] += tdx;
4013
0
    } else {
4014
0
        range[1] += tdx;
4015
0
    }
4016
4017
0
    if (tdy < 0) {
4018
0
        range[0] += tdy;
4019
0
    } else {
4020
0
        range[1] += tdy;
4021
0
    }
4022
4023
0
    *lower = std::max<double>(0., std::min<double>(1., range[0]));
4024
0
    *upper = std::max<double>(0., std::min<double>(1., range[1]));
4025
0
}
4026
4027
//------------------------------------------------------------------------
4028
// GfxRadialShading
4029
//------------------------------------------------------------------------
4030
4031
#ifndef RADIAL_EPSILON
4032
0
#    define RADIAL_EPSILON (1. / 1024 / 1024)
4033
#endif
4034
4035
GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A, double x1A, double y1A, double r1A, double t0A, double t1A, std::vector<std::unique_ptr<Function>> &&funcsA, bool extend0A, bool extend1A)
4036
0
    : GfxUnivariateShading(3, t0A, t1A, std::move(funcsA), extend0A, extend1A)
4037
0
{
4038
0
    x0 = x0A;
4039
0
    y0 = y0A;
4040
0
    r0 = r0A;
4041
0
    x1 = x1A;
4042
0
    y1 = y1A;
4043
0
    r1 = r1A;
4044
0
}
4045
4046
0
GfxRadialShading::GfxRadialShading(const GfxRadialShading *shading) : GfxUnivariateShading(shading)
4047
0
{
4048
0
    x0 = shading->x0;
4049
0
    y0 = shading->y0;
4050
0
    r0 = shading->r0;
4051
0
    x1 = shading->x1;
4052
0
    y1 = shading->y1;
4053
0
    r1 = shading->r1;
4054
0
}
4055
4056
GfxRadialShading::~GfxRadialShading() = default;
4057
4058
std::unique_ptr<GfxRadialShading> GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
4059
0
{
4060
0
    double x0A, y0A, r0A, x1A, y1A, r1A;
4061
0
    double t0A, t1A;
4062
0
    std::vector<std::unique_ptr<Function>> funcsA;
4063
0
    bool extend0A, extend1A;
4064
0
    Object obj1;
4065
0
    int i;
4066
4067
0
    x0A = y0A = r0A = x1A = y1A = r1A = 0;
4068
0
    obj1 = dict->lookup("Coords");
4069
0
    if (obj1.isArrayOfLength(6)) {
4070
0
        x0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
4071
0
        y0A = obj1.arrayGet(1).getNumWithDefaultValue(0);
4072
0
        r0A = obj1.arrayGet(2).getNumWithDefaultValue(0);
4073
0
        x1A = obj1.arrayGet(3).getNumWithDefaultValue(0);
4074
0
        y1A = obj1.arrayGet(4).getNumWithDefaultValue(0);
4075
0
        r1A = obj1.arrayGet(5).getNumWithDefaultValue(0);
4076
0
    } else {
4077
0
        error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
4078
0
        return {};
4079
0
    }
4080
4081
0
    t0A = 0;
4082
0
    t1A = 1;
4083
0
    obj1 = dict->lookup("Domain");
4084
0
    if (obj1.isArrayOfLength(2)) {
4085
0
        t0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
4086
0
        t1A = obj1.arrayGet(1).getNumWithDefaultValue(1);
4087
0
    }
4088
4089
0
    obj1 = dict->lookup("Function");
4090
0
    if (obj1.isArray()) {
4091
0
        const int nFuncsA = obj1.arrayGetLength();
4092
0
        if (nFuncsA > gfxColorMaxComps) {
4093
0
            error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4094
0
            return {};
4095
0
        }
4096
0
        for (i = 0; i < nFuncsA; ++i) {
4097
0
            Object obj2 = obj1.arrayGet(i);
4098
0
            std::unique_ptr<Function> f = Function::parse(&obj2);
4099
0
            if (!f) {
4100
0
                return {};
4101
0
            }
4102
0
            funcsA.emplace_back(std::move(f));
4103
0
        }
4104
0
    } else {
4105
0
        std::unique_ptr<Function> f = Function::parse(&obj1);
4106
0
        if (!f) {
4107
0
            return {};
4108
0
        }
4109
0
        funcsA.emplace_back(std::move(f));
4110
0
    }
4111
4112
0
    extend0A = extend1A = false;
4113
0
    obj1 = dict->lookup("Extend");
4114
0
    if (obj1.isArrayOfLength(2)) {
4115
0
        extend0A = obj1.arrayGet(0).getBoolWithDefaultValue(false);
4116
0
        extend1A = obj1.arrayGet(1).getBoolWithDefaultValue(false);
4117
0
    }
4118
4119
0
    auto shading = std::make_unique<GfxRadialShading>(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A, std::move(funcsA), extend0A, extend1A);
4120
0
    if (!shading->init(res, dict, out, state)) {
4121
0
        return {};
4122
0
    }
4123
0
    return shading;
4124
0
}
4125
4126
std::unique_ptr<GfxShading> GfxRadialShading::copy() const
4127
0
{
4128
0
    return std::make_unique<GfxRadialShading>(this);
4129
0
}
4130
4131
double GfxRadialShading::getDistance(double sMin, double sMax) const
4132
0
{
4133
0
    double xMin, yMin, rMin, xMax, yMax, rMax;
4134
4135
0
    xMin = x0 + sMin * (x1 - x0);
4136
0
    yMin = y0 + sMin * (y1 - y0);
4137
0
    rMin = r0 + sMin * (r1 - r0);
4138
4139
0
    xMax = x0 + sMax * (x1 - x0);
4140
0
    yMax = y0 + sMax * (y1 - y0);
4141
0
    rMax = r0 + sMax * (r1 - r0);
4142
4143
0
    return hypot(xMax - xMin, yMax - yMin) + fabs(rMax - rMin);
4144
0
}
4145
4146
// extend range, adapted from cairo, radialExtendRange
4147
static bool radialExtendRange(double range[2], double value, bool valid)
4148
0
{
4149
0
    if (!valid) {
4150
0
        range[0] = range[1] = value;
4151
0
    } else if (value < range[0]) {
4152
0
        range[0] = value;
4153
0
    } else if (value > range[1]) {
4154
0
        range[1] = value;
4155
0
    }
4156
4157
0
    return true;
4158
0
}
4159
4160
inline void radialEdge(double num, double den, double delta, double lower, double upper, double dr, double mindr, bool &valid, double *range)
4161
0
{
4162
0
    if (fabs(den) >= RADIAL_EPSILON) {
4163
0
        double t_edge, v;
4164
0
        t_edge = num / den;
4165
0
        v = t_edge * delta;
4166
0
        if (t_edge * dr >= mindr && lower <= v && v <= upper) {
4167
0
            valid = radialExtendRange(range, t_edge, valid);
4168
0
        }
4169
0
    }
4170
0
}
4171
4172
inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr, double dr, double mindr, bool &valid, double *range)
4173
0
{
4174
0
    b = x * dx + y * dy + cr * dr;
4175
0
    if (fabs(b) >= RADIAL_EPSILON) {
4176
0
        double t_corner;
4177
0
        double x2 = x * x;
4178
0
        double y2 = y * y;
4179
0
        double cr2 = cr * cr;
4180
0
        double c = x2 + y2 - cr2;
4181
4182
0
        t_corner = 0.5 * c / b;
4183
0
        if (t_corner * dr >= mindr) {
4184
0
            valid = radialExtendRange(range, t_corner, valid);
4185
0
        }
4186
0
    }
4187
0
}
4188
4189
inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr, double inva, double dr, double mindr, bool &valid, double *range)
4190
0
{
4191
0
    b = x * dx + y * dy + cr * dr;
4192
0
    c = x * x + y * y - cr * cr;
4193
0
    d = b * b - a * c;
4194
0
    if (d >= 0) {
4195
0
        double t_corner;
4196
4197
0
        d = sqrt(d);
4198
0
        t_corner = (b + d) * inva;
4199
0
        if (t_corner * dr >= mindr) {
4200
0
            valid = radialExtendRange(range, t_corner, valid);
4201
0
        }
4202
0
        t_corner = (b - d) * inva;
4203
0
        if (t_corner * dr >= mindr) {
4204
0
            valid = radialExtendRange(range, t_corner, valid);
4205
0
        }
4206
0
    }
4207
0
}
4208
void GfxRadialShading::getParameterRange(double *lower, double *upper, double xMin, double yMin, double xMax, double yMax)
4209
0
{
4210
0
    double cx, cy, cr, dx, dy, dr;
4211
0
    double a, x_focus, y_focus;
4212
0
    double mindr, minx, miny, maxx, maxy;
4213
0
    double range[2];
4214
0
    bool valid;
4215
4216
    // A radial pattern is considered degenerate if it can be
4217
    // represented as a solid or clear pattern.  This corresponds to one
4218
    // of the two cases:
4219
    //
4220
    // 1) The radii are both very small:
4221
    //      |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
4222
    //
4223
    // 2) The two circles have about the same radius and are very
4224
    //    close to each other (approximately a cylinder gradient that
4225
    //    doesn't move with the parameter):
4226
    //      |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
4227
4228
0
    if (xMin >= xMax || yMin >= yMax || (fabs(r0 - r1) < RADIAL_EPSILON && (std::min<double>(r0, r1) < RADIAL_EPSILON || std::max<double>(fabs(x0 - x1), fabs(y0 - y1)) < 2 * RADIAL_EPSILON))) {
4229
0
        *lower = *upper = 0;
4230
0
        return;
4231
0
    }
4232
4233
0
    range[0] = range[1] = 0;
4234
0
    valid = false;
4235
4236
0
    x_focus = y_focus = 0; // silence gcc
4237
4238
0
    cx = x0;
4239
0
    cy = y0;
4240
0
    cr = r0;
4241
0
    dx = x1 - cx;
4242
0
    dy = y1 - cy;
4243
0
    dr = r1 - cr;
4244
4245
    // translate by -(cx, cy) to simplify computations
4246
0
    xMin -= cx;
4247
0
    yMin -= cy;
4248
0
    xMax -= cx;
4249
0
    yMax -= cy;
4250
4251
    // enlarge boundaries slightly to avoid rounding problems in the
4252
    // parameter range computation
4253
0
    xMin -= RADIAL_EPSILON;
4254
0
    yMin -= RADIAL_EPSILON;
4255
0
    xMax += RADIAL_EPSILON;
4256
0
    yMax += RADIAL_EPSILON;
4257
4258
    // enlarge boundaries even more to avoid rounding problems when
4259
    // testing if a point belongs to the box
4260
0
    minx = xMin - RADIAL_EPSILON;
4261
0
    miny = yMin - RADIAL_EPSILON;
4262
0
    maxx = xMax + RADIAL_EPSILON;
4263
0
    maxy = yMax + RADIAL_EPSILON;
4264
4265
    // we dont' allow negative radiuses, so we will be checking that
4266
    // t*dr >= mindr to consider t valid
4267
0
    mindr = -(cr + RADIAL_EPSILON);
4268
4269
    // After the previous transformations, the start circle is centered
4270
    // in the origin and has radius cr. A 1-unit change in the t
4271
    // parameter corresponds to dx,dy,dr changes in the x,y,r of the
4272
    // circle (center coordinates, radius).
4273
    //
4274
    // To compute the minimum range needed to correctly draw the
4275
    // pattern, we start with an empty range and extend it to include
4276
    // the circles touching the bounding box or within it.
4277
4278
    // Focus, the point where the circle has radius == 0.
4279
    //
4280
    // r = cr + t * dr = 0
4281
    // t = -cr / dr
4282
    //
4283
    // If the radius is constant (dr == 0) there is no focus (the
4284
    // gradient represents a cylinder instead of a cone).
4285
0
    if (fabs(dr) >= RADIAL_EPSILON) {
4286
0
        double t_focus;
4287
4288
0
        t_focus = -cr / dr;
4289
0
        x_focus = t_focus * dx;
4290
0
        y_focus = t_focus * dy;
4291
0
        if (minx <= x_focus && x_focus <= maxx && miny <= y_focus && y_focus <= maxy) {
4292
0
            valid = radialExtendRange(range, t_focus, valid);
4293
0
        }
4294
0
    }
4295
4296
    // Circles externally tangent to box edges.
4297
    //
4298
    // All circles have center in (dx, dy) * t
4299
    //
4300
    // If the circle is tangent to the line defined by the edge of the
4301
    // box, then at least one of the following holds true:
4302
    //
4303
    //   (dx*t) + (cr + dr*t) == x0 (left   edge)
4304
    //   (dx*t) - (cr + dr*t) == x1 (right  edge)
4305
    //   (dy*t) + (cr + dr*t) == y0 (top    edge)
4306
    //   (dy*t) - (cr + dr*t) == y1 (bottom edge)
4307
    //
4308
    // The solution is only valid if the tangent point is actually on
4309
    // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
4310
    // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
4311
    //
4312
    // For the first equation:
4313
    //
4314
    //   (dx + dr) * t = x0 - cr
4315
    //   t = (x0 - cr) / (dx + dr)
4316
    //   y = dy * t
4317
    //
4318
    // in the code this becomes:
4319
    //
4320
    //   t_edge = (num) / (den)
4321
    //   v = (delta) * t_edge
4322
    //
4323
    // If the denominator in t is 0, the pattern is tangent to a line
4324
    // parallel to the edge under examination. The corner-case where the
4325
    // boundary line is the same as the edge is handled by the focus
4326
    // point case and/or by the a==0 case.
4327
4328
    // circles tangent (externally) to left/right/top/bottom edge
4329
0
    radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range);
4330
0
    radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range);
4331
0
    radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range);
4332
0
    radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range);
4333
4334
    // Circles passing through a corner.
4335
    //
4336
    // A circle passing through the point (x,y) satisfies:
4337
    //
4338
    // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
4339
    //
4340
    // If we set:
4341
    //   a = dx^2 + dy^2 - dr^2
4342
    //   b = x*dx + y*dy + cr*dr
4343
    //   c = x^2 + y^2 - cr^2
4344
    // we have:
4345
    //   a*t^2 - 2*b*t + c == 0
4346
4347
0
    a = dx * dx + dy * dy - dr * dr;
4348
0
    if (fabs(a) < RADIAL_EPSILON * RADIAL_EPSILON) {
4349
0
        double b;
4350
4351
        // Ensure that gradients with both a and dr small are
4352
        // considered degenerate.
4353
        // The floating point version of the degeneracy test implemented
4354
        // in _radial_pattern_is_degenerate() is:
4355
        //
4356
        //  1) The circles are practically the same size:
4357
        //     |dr| < RADIAL_EPSILON
4358
        //  AND
4359
        //  2a) The circles are both very small:
4360
        //      min (r0, r1) < RADIAL_EPSILON
4361
        //   OR
4362
        //  2b) The circles are very close to each other:
4363
        //      max (|dx|, |dy|) < 2 * RADIAL_EPSILON
4364
        //
4365
        // Assuming that the gradient is not degenerate, we want to
4366
        // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
4367
        //
4368
        // If the gradient is not degenerate yet it has |dr| <
4369
        // RADIAL_EPSILON, (2b) is false, thus:
4370
        //
4371
        //   max (|dx|, |dy|) >= 2*RADIAL_EPSILON
4372
        // which implies:
4373
        //   4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
4374
        //
4375
        // From the definition of a, we get:
4376
        //   a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
4377
        //   dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
4378
        //   3*RADIAL_EPSILON^2 < dr^2
4379
        //
4380
        // which is inconsistent with the hypotheses, thus |dr| <
4381
        // RADIAL_EPSILON is false or the gradient is degenerate.
4382
4383
0
        assert(fabs(dr) >= RADIAL_EPSILON);
4384
4385
        // If a == 0, all the circles are tangent to a line in the
4386
        // focus point. If this line is within the box extents, we
4387
        // should add the circle with infinite radius, but this would
4388
        // make the range unbounded. We will be limiting the range to
4389
        // [0,1] anyway, so we simply add the biggest legitimate
4390
        // circle (it happens for 0 or for 1).
4391
0
        if (dr < 0) {
4392
0
            valid = radialExtendRange(range, 0, valid);
4393
0
        } else {
4394
0
            valid = radialExtendRange(range, 1, valid);
4395
0
        }
4396
4397
        // Nondegenerate, nonlimit circles passing through the corners.
4398
        //
4399
        // a == 0 && a*t^2 - 2*b*t + c == 0
4400
        //
4401
        // t = c / (2*b)
4402
        //
4403
        // The b == 0 case has just been handled, so we only have to
4404
        // compute this if b != 0.
4405
4406
        // circles touching each corner
4407
0
        radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4408
0
        radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4409
0
        radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4410
0
        radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4411
0
    } else {
4412
0
        double inva, b, c, d;
4413
4414
0
        inva = 1 / a;
4415
4416
        // Nondegenerate, nonlimit circles passing through the corners.
4417
        //
4418
        // a != 0 && a*t^2 - 2*b*t + c == 0
4419
        //
4420
        // t = (b +- sqrt (b*b - a*c)) / a
4421
        //
4422
        // If the argument of sqrt() is negative, then no circle
4423
        // passes through the corner.
4424
4425
        // circles touching each corner
4426
0
        radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4427
0
        radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4428
0
        radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4429
0
        radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4430
0
    }
4431
4432
0
    *lower = std::max<double>(0., std::min<double>(1., range[0]));
4433
0
    *upper = std::max<double>(0., std::min<double>(1., range[1]));
4434
0
}
4435
4436
//------------------------------------------------------------------------
4437
// GfxShadingBitBuf
4438
//------------------------------------------------------------------------
4439
4440
class GfxShadingBitBuf
4441
{
4442
public:
4443
    explicit GfxShadingBitBuf(Stream *strA);
4444
    ~GfxShadingBitBuf();
4445
    GfxShadingBitBuf(const GfxShadingBitBuf &) = delete;
4446
    GfxShadingBitBuf &operator=(const GfxShadingBitBuf &) = delete;
4447
    bool getBits(int n, unsigned int *val);
4448
    void flushBits();
4449
4450
private:
4451
    Stream *str;
4452
    int bitBuf;
4453
    int nBits;
4454
};
4455
4456
GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA)
4457
0
{
4458
0
    str = strA;
4459
0
    (void)str->rewind();
4460
0
    bitBuf = 0;
4461
0
    nBits = 0;
4462
0
}
4463
4464
GfxShadingBitBuf::~GfxShadingBitBuf()
4465
0
{
4466
0
    str->close();
4467
0
}
4468
4469
bool GfxShadingBitBuf::getBits(int n, unsigned int *val)
4470
0
{
4471
0
    unsigned int x;
4472
4473
0
    if (nBits >= n) {
4474
0
        x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
4475
0
        nBits -= n;
4476
0
    } else {
4477
0
        x = 0;
4478
0
        if (nBits > 0) {
4479
0
            x = bitBuf & ((1 << nBits) - 1);
4480
0
            n -= nBits;
4481
0
            nBits = 0;
4482
0
        }
4483
0
        while (n > 0) {
4484
0
            if ((bitBuf = str->getChar()) == EOF) {
4485
0
                nBits = 0;
4486
0
                return false;
4487
0
            }
4488
0
            if (n >= 8) {
4489
0
                x = (x << 8) | bitBuf;
4490
0
                n -= 8;
4491
0
            } else {
4492
0
                x = (x << n) | (bitBuf >> (8 - n));
4493
0
                nBits = 8 - n;
4494
0
                n = 0;
4495
0
            }
4496
0
        }
4497
0
    }
4498
0
    *val = x;
4499
0
    return true;
4500
0
}
4501
4502
void GfxShadingBitBuf::flushBits()
4503
0
{
4504
0
    bitBuf = 0;
4505
0
    nBits = 0;
4506
0
}
4507
4508
//------------------------------------------------------------------------
4509
// GfxGouraudTriangleShading
4510
//------------------------------------------------------------------------
4511
4512
GfxGouraudTriangleShading::GfxGouraudTriangleShading(int typeA, GfxGouraudVertex *verticesA, int nVerticesA, int (*trianglesA)[3], int nTrianglesA, std::vector<std::unique_ptr<Function>> &&funcsA)
4513
0
    : GfxShading(typeA), funcs(std::move(funcsA))
4514
0
{
4515
0
    vertices = verticesA;
4516
0
    nVertices = nVerticesA;
4517
0
    triangles = trianglesA;
4518
0
    nTriangles = nTrianglesA;
4519
0
}
4520
4521
0
GfxGouraudTriangleShading::GfxGouraudTriangleShading(const GfxGouraudTriangleShading *shading) : GfxShading(shading)
4522
0
{
4523
0
    nVertices = shading->nVertices;
4524
0
    vertices = static_cast<GfxGouraudVertex *>(gmallocn(nVertices, sizeof(GfxGouraudVertex)));
4525
0
    memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
4526
0
    nTriangles = shading->nTriangles;
4527
0
    triangles = static_cast<int (*)[3]>(gmallocn(nTriangles * 3, sizeof(int)));
4528
0
    memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
4529
0
    for (const auto &f : shading->funcs) {
4530
0
        funcs.emplace_back(f->copy());
4531
0
    }
4532
0
}
4533
4534
GfxGouraudTriangleShading::~GfxGouraudTriangleShading()
4535
0
{
4536
0
    gfree(vertices);
4537
0
    gfree(triangles);
4538
0
}
4539
4540
std::unique_ptr<GfxGouraudTriangleShading> GfxGouraudTriangleShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *gfxState)
4541
0
{
4542
0
    std::vector<std::unique_ptr<Function>> funcsA;
4543
0
    int coordBits, compBits, flagBits, vertsPerRow, nRows;
4544
0
    double xMin, xMax, yMin, yMax;
4545
0
    double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
4546
0
    double xMul, yMul;
4547
0
    double cMul[gfxColorMaxComps];
4548
0
    GfxGouraudVertex *verticesA;
4549
0
    int (*trianglesA)[3];
4550
0
    int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
4551
0
    unsigned int x, y, flag;
4552
0
    unsigned int c[gfxColorMaxComps];
4553
0
    GfxShadingBitBuf *bitBuf;
4554
0
    Object obj1;
4555
0
    int i, j, k, state;
4556
4557
0
    obj1 = dict->lookup("BitsPerCoordinate");
4558
0
    if (obj1.isInt()) {
4559
0
        coordBits = obj1.getInt();
4560
0
    } else {
4561
0
        error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4562
0
        return {};
4563
0
    }
4564
0
    if (unlikely(coordBits <= 0)) {
4565
0
        error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary");
4566
0
        return {};
4567
0
    }
4568
0
    obj1 = dict->lookup("BitsPerComponent");
4569
0
    if (obj1.isInt()) {
4570
0
        compBits = obj1.getInt();
4571
0
    } else {
4572
0
        error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4573
0
        return {};
4574
0
    }
4575
0
    if (unlikely(compBits <= 0 || compBits > 31)) {
4576
0
        error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary");
4577
0
        return {};
4578
0
    }
4579
0
    flagBits = vertsPerRow = 0; // make gcc happy
4580
0
    if (typeA == 4) {
4581
0
        obj1 = dict->lookup("BitsPerFlag");
4582
0
        if (obj1.isInt()) {
4583
0
            flagBits = obj1.getInt();
4584
0
        } else {
4585
0
            error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4586
0
            return {};
4587
0
        }
4588
0
    } else {
4589
0
        obj1 = dict->lookup("VerticesPerRow");
4590
0
        if (obj1.isInt()) {
4591
0
            vertsPerRow = obj1.getInt();
4592
0
        } else {
4593
0
            error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary");
4594
0
            return {};
4595
0
        }
4596
0
    }
4597
0
    obj1 = dict->lookup("Decode");
4598
0
    if (obj1.isArrayOfLengthAtLeast(6)) {
4599
0
        bool decodeOk = true;
4600
0
        xMin = obj1.arrayGet(0).getNum(&decodeOk);
4601
0
        xMax = obj1.arrayGet(1).getNum(&decodeOk);
4602
0
        xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
4603
0
        yMin = obj1.arrayGet(2).getNum(&decodeOk);
4604
0
        yMax = obj1.arrayGet(3).getNum(&decodeOk);
4605
0
        yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4606
0
        for (i = 0; 5 + 2 * i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4607
0
            cMin[i] = obj1.arrayGet(4 + 2 * i).getNum(&decodeOk);
4608
0
            cMax[i] = obj1.arrayGet(5 + 2 * i).getNum(&decodeOk);
4609
0
            cMul[i] = (cMax[i] - cMin[i]) / static_cast<double>((1U << compBits) - 1);
4610
0
        }
4611
0
        nComps = i;
4612
4613
0
        if (!decodeOk) {
4614
0
            error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4615
0
            return {};
4616
0
        }
4617
0
    } else {
4618
0
        error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4619
0
        return {};
4620
0
    }
4621
4622
0
    obj1 = dict->lookup("Function");
4623
0
    if (!obj1.isNull()) {
4624
0
        if (obj1.isArray()) {
4625
0
            const int nFuncsA = obj1.arrayGetLength();
4626
0
            if (nFuncsA > gfxColorMaxComps) {
4627
0
                error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4628
0
                return {};
4629
0
            }
4630
0
            for (i = 0; i < nFuncsA; ++i) {
4631
0
                Object obj2 = obj1.arrayGet(i);
4632
0
                std::unique_ptr<Function> f = Function::parse(&obj2);
4633
0
                if (!f) {
4634
0
                    return {};
4635
0
                }
4636
0
                funcsA.emplace_back(std::move(f));
4637
0
            }
4638
0
        } else {
4639
0
            std::unique_ptr<Function> f = Function::parse(&obj1);
4640
0
            if (!f) {
4641
0
                return {};
4642
0
            }
4643
0
            funcsA.emplace_back(std::move(f));
4644
0
        }
4645
0
    }
4646
4647
0
    nVerticesA = nTrianglesA = 0;
4648
0
    verticesA = nullptr;
4649
0
    trianglesA = nullptr;
4650
0
    vertSize = triSize = 0;
4651
0
    state = 0;
4652
0
    flag = 0; // make gcc happy
4653
0
    bitBuf = new GfxShadingBitBuf(str);
4654
0
    while (true) {
4655
0
        if (typeA == 4) {
4656
0
            if (!bitBuf->getBits(flagBits, &flag)) {
4657
0
                break;
4658
0
            }
4659
0
        }
4660
0
        if (!bitBuf->getBits(coordBits, &x) || !bitBuf->getBits(coordBits, &y)) {
4661
0
            break;
4662
0
        }
4663
0
        for (i = 0; i < nComps; ++i) {
4664
0
            if (!bitBuf->getBits(compBits, &c[i])) {
4665
0
                break;
4666
0
            }
4667
0
        }
4668
0
        if (i < nComps) {
4669
0
            break;
4670
0
        }
4671
0
        if (nVerticesA == vertSize) {
4672
0
            int oldVertSize = vertSize;
4673
0
            vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
4674
0
            verticesA = static_cast<GfxGouraudVertex *>(greallocn_checkoverflow(verticesA, vertSize, sizeof(GfxGouraudVertex)));
4675
0
            if (unlikely(!verticesA)) {
4676
0
                error(errSyntaxWarning, -1, "GfxGouraudTriangleShading::parse: vertices size overflow");
4677
0
                gfree(trianglesA);
4678
0
                delete bitBuf;
4679
0
                return nullptr;
4680
0
            }
4681
0
            memset(verticesA + oldVertSize, 0, (vertSize - oldVertSize) * sizeof(GfxGouraudVertex));
4682
0
        }
4683
0
        verticesA[nVerticesA].x = xMin + xMul * static_cast<double>(x);
4684
0
        verticesA[nVerticesA].y = yMin + yMul * static_cast<double>(y);
4685
0
        for (i = 0; i < nComps; ++i) {
4686
0
            verticesA[nVerticesA].color.c[i] = dblToCol(cMin[i] + cMul[i] * static_cast<double>(c[i]));
4687
0
        }
4688
0
        ++nVerticesA;
4689
0
        bitBuf->flushBits();
4690
0
        if (typeA == 4) {
4691
0
            if (state == 0 || state == 1) {
4692
0
                ++state;
4693
0
            } else if (state == 2 || flag > 0) {
4694
0
                if (nTrianglesA == triSize) {
4695
0
                    triSize = (triSize == 0) ? 16 : 2 * triSize;
4696
0
                    trianglesA = static_cast<int (*)[3]>(greallocn(trianglesA, triSize * 3, sizeof(int)));
4697
0
                }
4698
0
                if (state == 2) {
4699
0
                    trianglesA[nTrianglesA][0] = nVerticesA - 3;
4700
0
                    trianglesA[nTrianglesA][1] = nVerticesA - 2;
4701
0
                    trianglesA[nTrianglesA][2] = nVerticesA - 1;
4702
0
                    ++state;
4703
0
                } else if (flag == 1) {
4704
0
                    trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
4705
0
                    trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
4706
0
                    trianglesA[nTrianglesA][2] = nVerticesA - 1;
4707
0
                } else { // flag == 2
4708
0
                    trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
4709
0
                    trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
4710
0
                    trianglesA[nTrianglesA][2] = nVerticesA - 1;
4711
0
                }
4712
0
                ++nTrianglesA;
4713
0
            } else { // state == 3 && flag == 0
4714
0
                state = 1;
4715
0
            }
4716
0
        }
4717
0
    }
4718
0
    delete bitBuf;
4719
0
    if (typeA == 5 && nVerticesA > 0 && vertsPerRow > 0) {
4720
0
        nRows = nVerticesA / vertsPerRow;
4721
0
        nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
4722
0
        trianglesA = static_cast<int (*)[3]>(gmallocn_checkoverflow(nTrianglesA * 3, sizeof(int)));
4723
0
        if (unlikely(!trianglesA)) {
4724
0
            gfree(verticesA);
4725
0
            return nullptr;
4726
0
        }
4727
0
        k = 0;
4728
0
        for (i = 0; i < nRows - 1; ++i) {
4729
0
            for (j = 0; j < vertsPerRow - 1; ++j) {
4730
0
                trianglesA[k][0] = i * vertsPerRow + j;
4731
0
                trianglesA[k][1] = i * vertsPerRow + j + 1;
4732
0
                trianglesA[k][2] = (i + 1) * vertsPerRow + j;
4733
0
                ++k;
4734
0
                trianglesA[k][0] = i * vertsPerRow + j + 1;
4735
0
                trianglesA[k][1] = (i + 1) * vertsPerRow + j;
4736
0
                trianglesA[k][2] = (i + 1) * vertsPerRow + j + 1;
4737
0
                ++k;
4738
0
            }
4739
0
        }
4740
0
    }
4741
4742
0
    auto shading = std::make_unique<GfxGouraudTriangleShading>(typeA, verticesA, nVerticesA, trianglesA, nTrianglesA, std::move(funcsA));
4743
0
    if (!shading->init(res, dict, out, gfxState)) {
4744
0
        return {};
4745
0
    }
4746
0
    return shading;
4747
0
}
4748
4749
bool GfxGouraudTriangleShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
4750
0
{
4751
0
    const bool parentInit = GfxShading::init(res, dict, out, state);
4752
0
    if (!parentInit) {
4753
0
        return false;
4754
0
    }
4755
4756
    // funcs needs to be one of the three:
4757
    //  * One function 1-in -> nComps-out
4758
    //  * nComps functions 1-in -> 1-out
4759
    //  * empty
4760
0
    const int nComps = colorSpace->getNComps();
4761
0
    const int nFuncs = funcs.size();
4762
0
    if (nFuncs == 1) {
4763
0
        if (funcs[0]->getInputSize() != 1) {
4764
0
            error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with input size != 2");
4765
0
            return false;
4766
0
        }
4767
0
        if (funcs[0]->getOutputSize() != nComps) {
4768
0
            error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with wrong output size");
4769
0
            return false;
4770
0
        }
4771
0
    } else if (nFuncs == nComps) {
4772
0
        for (const std::unique_ptr<Function> &f : funcs) {
4773
0
            if (f->getInputSize() != 1) {
4774
0
                error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with input size != 2");
4775
0
                return false;
4776
0
            }
4777
0
            if (f->getOutputSize() != 1) {
4778
0
                error(errSyntaxWarning, -1, "GfxGouraudTriangleShading: function with wrong output size");
4779
0
                return false;
4780
0
            }
4781
0
        }
4782
0
    } else if (nFuncs != 0) {
4783
0
        return false;
4784
0
    }
4785
4786
0
    return true;
4787
0
}
4788
4789
std::unique_ptr<GfxShading> GfxGouraudTriangleShading::copy() const
4790
0
{
4791
0
    return std::make_unique<GfxGouraudTriangleShading>(this);
4792
0
}
4793
4794
void GfxGouraudTriangleShading::getTriangle(int i, double *x0, double *y0, GfxColor *color0, double *x1, double *y1, GfxColor *color1, double *x2, double *y2, GfxColor *color2) const
4795
0
{
4796
0
    int v;
4797
4798
0
    assert(!isParameterized());
4799
4800
0
    v = triangles[i][0];
4801
0
    *x0 = vertices[v].x;
4802
0
    *y0 = vertices[v].y;
4803
0
    *color0 = vertices[v].color;
4804
0
    v = triangles[i][1];
4805
0
    *x1 = vertices[v].x;
4806
0
    *y1 = vertices[v].y;
4807
0
    *color1 = vertices[v].color;
4808
0
    v = triangles[i][2];
4809
0
    *x2 = vertices[v].x;
4810
0
    *y2 = vertices[v].y;
4811
0
    *color2 = vertices[v].color;
4812
0
}
4813
4814
void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) const
4815
0
{
4816
0
    double out[gfxColorMaxComps];
4817
4818
0
    for (unsigned int j = 0; j < funcs.size(); ++j) {
4819
0
        funcs[j]->transform(&t, &out[j]);
4820
0
    }
4821
0
    for (int j = 0; j < gfxColorMaxComps; ++j) {
4822
0
        color->c[j] = dblToCol(out[j]);
4823
0
    }
4824
0
}
4825
4826
void GfxGouraudTriangleShading::getTriangle(int i, double *x0, double *y0, double *color0, double *x1, double *y1, double *color1, double *x2, double *y2, double *color2) const
4827
0
{
4828
0
    int v;
4829
4830
0
    assert(isParameterized());
4831
4832
0
    v = triangles[i][0];
4833
0
    if (likely(v >= 0 && v < nVertices)) {
4834
0
        *x0 = vertices[v].x;
4835
0
        *y0 = vertices[v].y;
4836
0
        *color0 = colToDbl(vertices[v].color.c[0]);
4837
0
    }
4838
0
    v = triangles[i][1];
4839
0
    if (likely(v >= 0 && v < nVertices)) {
4840
0
        *x1 = vertices[v].x;
4841
0
        *y1 = vertices[v].y;
4842
0
        *color1 = colToDbl(vertices[v].color.c[0]);
4843
0
    }
4844
0
    v = triangles[i][2];
4845
0
    if (likely(v >= 0 && v < nVertices)) {
4846
0
        *x2 = vertices[v].x;
4847
0
        *y2 = vertices[v].y;
4848
0
        *color2 = colToDbl(vertices[v].color.c[0]);
4849
0
    }
4850
0
}
4851
4852
//------------------------------------------------------------------------
4853
// GfxPatchMeshShading
4854
//------------------------------------------------------------------------
4855
4856
0
GfxPatchMeshShading::GfxPatchMeshShading(int typeA, GfxPatch *patchesA, int nPatchesA, std::vector<std::unique_ptr<Function>> &&funcsA) : GfxShading(typeA), funcs(std::move(funcsA))
4857
0
{
4858
0
    patches = patchesA;
4859
0
    nPatches = nPatchesA;
4860
0
}
4861
4862
0
GfxPatchMeshShading::GfxPatchMeshShading(const GfxPatchMeshShading *shading) : GfxShading(shading)
4863
0
{
4864
0
    nPatches = shading->nPatches;
4865
0
    patches = static_cast<GfxPatch *>(gmallocn(nPatches, sizeof(GfxPatch)));
4866
0
    memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
4867
0
    for (const auto &f : shading->funcs) {
4868
0
        funcs.emplace_back(f->copy());
4869
0
    }
4870
0
}
4871
4872
GfxPatchMeshShading::~GfxPatchMeshShading()
4873
0
{
4874
0
    gfree(patches);
4875
0
}
4876
4877
std::unique_ptr<GfxPatchMeshShading> GfxPatchMeshShading::parse(GfxResources *res, int typeA, Dict *dict, Stream *str, OutputDev *out, GfxState *state)
4878
0
{
4879
0
    std::vector<std::unique_ptr<Function>> funcsA;
4880
0
    int coordBits, compBits, flagBits;
4881
0
    double xMin, xMax, yMin, yMax;
4882
0
    double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
4883
0
    double xMul, yMul;
4884
0
    double cMul[gfxColorMaxComps];
4885
0
    GfxPatch *patchesA, *p;
4886
0
    int nComps, nPatchesA, patchesSize, nPts, nColors;
4887
0
    unsigned int flag;
4888
0
    double x[16], y[16];
4889
0
    unsigned int xi, yi;
4890
0
    double c[4][gfxColorMaxComps];
4891
0
    unsigned int ci;
4892
0
    Object obj1;
4893
0
    int i, j;
4894
4895
0
    obj1 = dict->lookup("BitsPerCoordinate");
4896
0
    if (obj1.isInt()) {
4897
0
        coordBits = obj1.getInt();
4898
0
    } else {
4899
0
        error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4900
0
        return {};
4901
0
    }
4902
0
    if (unlikely(coordBits <= 0)) {
4903
0
        error(errSyntaxWarning, -1, "Invalid BitsPerCoordinate in shading dictionary");
4904
0
        return {};
4905
0
    }
4906
0
    obj1 = dict->lookup("BitsPerComponent");
4907
0
    if (obj1.isInt()) {
4908
0
        compBits = obj1.getInt();
4909
0
    } else {
4910
0
        error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4911
0
        return {};
4912
0
    }
4913
0
    if (unlikely(compBits <= 0 || compBits > 31)) {
4914
0
        error(errSyntaxWarning, -1, "Invalid BitsPerComponent in shading dictionary");
4915
0
        return {};
4916
0
    }
4917
0
    obj1 = dict->lookup("BitsPerFlag");
4918
0
    if (obj1.isInt()) {
4919
0
        flagBits = obj1.getInt();
4920
0
    } else {
4921
0
        error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4922
0
        return {};
4923
0
    }
4924
0
    obj1 = dict->lookup("Decode");
4925
0
    if (obj1.isArrayOfLengthAtLeast(6)) {
4926
0
        bool decodeOk = true;
4927
0
        xMin = obj1.arrayGet(0).getNum(&decodeOk);
4928
0
        xMax = obj1.arrayGet(1).getNum(&decodeOk);
4929
0
        xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
4930
0
        yMin = obj1.arrayGet(2).getNum(&decodeOk);
4931
0
        yMax = obj1.arrayGet(3).getNum(&decodeOk);
4932
0
        yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4933
0
        for (i = 0; 5 + 2 * i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4934
0
            cMin[i] = obj1.arrayGet(4 + 2 * i).getNum(&decodeOk);
4935
0
            cMax[i] = obj1.arrayGet(5 + 2 * i).getNum(&decodeOk);
4936
0
            cMul[i] = (cMax[i] - cMin[i]) / static_cast<double>((1U << compBits) - 1);
4937
0
        }
4938
0
        nComps = i;
4939
4940
0
        if (!decodeOk) {
4941
0
            error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4942
0
            return {};
4943
0
        }
4944
0
    } else {
4945
0
        error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4946
0
        return {};
4947
0
    }
4948
4949
0
    obj1 = dict->lookup("Function");
4950
0
    if (!obj1.isNull()) {
4951
0
        if (obj1.isArray()) {
4952
0
            const int nFuncsA = obj1.arrayGetLength();
4953
0
            if (nFuncsA > gfxColorMaxComps) {
4954
0
                error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4955
0
                return {};
4956
0
            }
4957
0
            for (i = 0; i < nFuncsA; ++i) {
4958
0
                Object obj2 = obj1.arrayGet(i);
4959
0
                std::unique_ptr<Function> f = Function::parse(&obj2);
4960
0
                if (!f) {
4961
0
                    return {};
4962
0
                }
4963
0
                funcsA.emplace_back(std::move(f));
4964
0
            }
4965
0
        } else {
4966
0
            std::unique_ptr<Function> f = Function::parse(&obj1);
4967
0
            if (!f) {
4968
0
                return {};
4969
0
            }
4970
0
            funcsA.emplace_back(std::move(f));
4971
0
        }
4972
0
    }
4973
4974
0
    nPatchesA = 0;
4975
0
    patchesA = nullptr;
4976
0
    patchesSize = 0;
4977
0
    auto bitBuf = std::make_unique<GfxShadingBitBuf>(str);
4978
0
    while (true) {
4979
0
        if (!bitBuf->getBits(flagBits, &flag)) {
4980
0
            break;
4981
0
        }
4982
0
        if (typeA == 6) {
4983
0
            switch (flag) {
4984
0
            case 0:
4985
0
                nPts = 12;
4986
0
                nColors = 4;
4987
0
                break;
4988
0
            case 1:
4989
0
            case 2:
4990
0
            case 3:
4991
0
            default:
4992
0
                nPts = 8;
4993
0
                nColors = 2;
4994
0
                break;
4995
0
            }
4996
0
        } else {
4997
0
            switch (flag) {
4998
0
            case 0:
4999
0
                nPts = 16;
5000
0
                nColors = 4;
5001
0
                break;
5002
0
            case 1:
5003
0
            case 2:
5004
0
            case 3:
5005
0
            default:
5006
0
                nPts = 12;
5007
0
                nColors = 2;
5008
0
                break;
5009
0
            }
5010
0
        }
5011
0
        for (i = 0; i < nPts; ++i) {
5012
0
            if (!bitBuf->getBits(coordBits, &xi) || !bitBuf->getBits(coordBits, &yi)) {
5013
0
                break;
5014
0
            }
5015
0
            x[i] = xMin + xMul * static_cast<double>(xi);
5016
0
            y[i] = yMin + yMul * static_cast<double>(yi);
5017
0
        }
5018
0
        if (i < nPts) {
5019
0
            break;
5020
0
        }
5021
0
        for (i = 0; i < nColors; ++i) {
5022
0
            for (j = 0; j < nComps; ++j) {
5023
0
                if (!bitBuf->getBits(compBits, &ci)) {
5024
0
                    break;
5025
0
                }
5026
0
                c[i][j] = cMin[j] + cMul[j] * static_cast<double>(ci);
5027
0
                if (funcsA.empty()) {
5028
                    // ... and colorspace values can also be stored into doubles.
5029
                    // They will be casted later.
5030
0
                    c[i][j] = dblToCol(c[i][j]);
5031
0
                }
5032
0
            }
5033
0
            if (j < nComps) {
5034
0
                break;
5035
0
            }
5036
0
        }
5037
0
        if (i < nColors) {
5038
0
            break;
5039
0
        }
5040
0
        if (nPatchesA == patchesSize) {
5041
0
            int oldPatchesSize = patchesSize;
5042
0
            patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
5043
0
            patchesA = static_cast<GfxPatch *>(greallocn_checkoverflow(patchesA, patchesSize, sizeof(GfxPatch)));
5044
0
            if (unlikely(!patchesA)) {
5045
0
                return {};
5046
0
            }
5047
0
            memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch));
5048
0
        }
5049
0
        p = &patchesA[nPatchesA];
5050
0
        if (typeA == 6) {
5051
0
            switch (flag) {
5052
0
            case 0:
5053
0
                p->x[0][0] = x[0];
5054
0
                p->y[0][0] = y[0];
5055
0
                p->x[0][1] = x[1];
5056
0
                p->y[0][1] = y[1];
5057
0
                p->x[0][2] = x[2];
5058
0
                p->y[0][2] = y[2];
5059
0
                p->x[0][3] = x[3];
5060
0
                p->y[0][3] = y[3];
5061
0
                p->x[1][3] = x[4];
5062
0
                p->y[1][3] = y[4];
5063
0
                p->x[2][3] = x[5];
5064
0
                p->y[2][3] = y[5];
5065
0
                p->x[3][3] = x[6];
5066
0
                p->y[3][3] = y[6];
5067
0
                p->x[3][2] = x[7];
5068
0
                p->y[3][2] = y[7];
5069
0
                p->x[3][1] = x[8];
5070
0
                p->y[3][1] = y[8];
5071
0
                p->x[3][0] = x[9];
5072
0
                p->y[3][0] = y[9];
5073
0
                p->x[2][0] = x[10];
5074
0
                p->y[2][0] = y[10];
5075
0
                p->x[1][0] = x[11];
5076
0
                p->y[1][0] = y[11];
5077
0
                for (j = 0; j < nComps; ++j) {
5078
0
                    p->color[0][0].c[j] = c[0][j];
5079
0
                    p->color[0][1].c[j] = c[1][j];
5080
0
                    p->color[1][1].c[j] = c[2][j];
5081
0
                    p->color[1][0].c[j] = c[3][j];
5082
0
                }
5083
0
                break;
5084
0
            case 1:
5085
0
                if (nPatchesA == 0) {
5086
0
                    gfree(patchesA);
5087
0
                    return nullptr;
5088
0
                }
5089
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[0][3];
5090
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[0][3];
5091
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[1][3];
5092
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[1][3];
5093
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[2][3];
5094
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[2][3];
5095
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[3][3];
5096
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[3][3];
5097
0
                p->x[1][3] = x[0];
5098
0
                p->y[1][3] = y[0];
5099
0
                p->x[2][3] = x[1];
5100
0
                p->y[2][3] = y[1];
5101
0
                p->x[3][3] = x[2];
5102
0
                p->y[3][3] = y[2];
5103
0
                p->x[3][2] = x[3];
5104
0
                p->y[3][2] = y[3];
5105
0
                p->x[3][1] = x[4];
5106
0
                p->y[3][1] = y[4];
5107
0
                p->x[3][0] = x[5];
5108
0
                p->y[3][0] = y[5];
5109
0
                p->x[2][0] = x[6];
5110
0
                p->y[2][0] = y[6];
5111
0
                p->x[1][0] = x[7];
5112
0
                p->y[1][0] = y[7];
5113
0
                for (j = 0; j < nComps; ++j) {
5114
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[0][1].c[j];
5115
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j];
5116
0
                    p->color[1][1].c[j] = c[0][j];
5117
0
                    p->color[1][0].c[j] = c[1][j];
5118
0
                }
5119
0
                break;
5120
0
            case 2:
5121
0
                if (nPatchesA == 0) {
5122
0
                    gfree(patchesA);
5123
0
                    return {};
5124
0
                }
5125
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[3][3];
5126
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[3][3];
5127
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[3][2];
5128
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[3][2];
5129
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[3][1];
5130
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[3][1];
5131
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[3][0];
5132
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[3][0];
5133
0
                p->x[1][3] = x[0];
5134
0
                p->y[1][3] = y[0];
5135
0
                p->x[2][3] = x[1];
5136
0
                p->y[2][3] = y[1];
5137
0
                p->x[3][3] = x[2];
5138
0
                p->y[3][3] = y[2];
5139
0
                p->x[3][2] = x[3];
5140
0
                p->y[3][2] = y[3];
5141
0
                p->x[3][1] = x[4];
5142
0
                p->y[3][1] = y[4];
5143
0
                p->x[3][0] = x[5];
5144
0
                p->y[3][0] = y[5];
5145
0
                p->x[2][0] = x[6];
5146
0
                p->y[2][0] = y[6];
5147
0
                p->x[1][0] = x[7];
5148
0
                p->y[1][0] = y[7];
5149
0
                for (j = 0; j < nComps; ++j) {
5150
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j];
5151
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j];
5152
0
                    p->color[1][1].c[j] = c[0][j];
5153
0
                    p->color[1][0].c[j] = c[1][j];
5154
0
                }
5155
0
                break;
5156
0
            case 3:
5157
0
                if (nPatchesA == 0) {
5158
0
                    gfree(patchesA);
5159
0
                    return {};
5160
0
                }
5161
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[3][0];
5162
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[3][0];
5163
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[2][0];
5164
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[2][0];
5165
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[1][0];
5166
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[1][0];
5167
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[0][0];
5168
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[0][0];
5169
0
                p->x[1][3] = x[0];
5170
0
                p->y[1][3] = y[0];
5171
0
                p->x[2][3] = x[1];
5172
0
                p->y[2][3] = y[1];
5173
0
                p->x[3][3] = x[2];
5174
0
                p->y[3][3] = y[2];
5175
0
                p->x[3][2] = x[3];
5176
0
                p->y[3][2] = y[3];
5177
0
                p->x[3][1] = x[4];
5178
0
                p->y[3][1] = y[4];
5179
0
                p->x[3][0] = x[5];
5180
0
                p->y[3][0] = y[5];
5181
0
                p->x[2][0] = x[6];
5182
0
                p->y[2][0] = y[6];
5183
0
                p->x[1][0] = x[7];
5184
0
                p->y[1][0] = y[7];
5185
0
                for (j = 0; j < nComps; ++j) {
5186
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j];
5187
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[0][0].c[j];
5188
0
                    p->color[1][1].c[j] = c[0][j];
5189
0
                    p->color[1][0].c[j] = c[1][j];
5190
0
                }
5191
0
                break;
5192
0
            }
5193
0
        } else {
5194
0
            switch (flag) {
5195
0
            case 0:
5196
0
                p->x[0][0] = x[0];
5197
0
                p->y[0][0] = y[0];
5198
0
                p->x[0][1] = x[1];
5199
0
                p->y[0][1] = y[1];
5200
0
                p->x[0][2] = x[2];
5201
0
                p->y[0][2] = y[2];
5202
0
                p->x[0][3] = x[3];
5203
0
                p->y[0][3] = y[3];
5204
0
                p->x[1][3] = x[4];
5205
0
                p->y[1][3] = y[4];
5206
0
                p->x[2][3] = x[5];
5207
0
                p->y[2][3] = y[5];
5208
0
                p->x[3][3] = x[6];
5209
0
                p->y[3][3] = y[6];
5210
0
                p->x[3][2] = x[7];
5211
0
                p->y[3][2] = y[7];
5212
0
                p->x[3][1] = x[8];
5213
0
                p->y[3][1] = y[8];
5214
0
                p->x[3][0] = x[9];
5215
0
                p->y[3][0] = y[9];
5216
0
                p->x[2][0] = x[10];
5217
0
                p->y[2][0] = y[10];
5218
0
                p->x[1][0] = x[11];
5219
0
                p->y[1][0] = y[11];
5220
0
                p->x[1][1] = x[12];
5221
0
                p->y[1][1] = y[12];
5222
0
                p->x[1][2] = x[13];
5223
0
                p->y[1][2] = y[13];
5224
0
                p->x[2][2] = x[14];
5225
0
                p->y[2][2] = y[14];
5226
0
                p->x[2][1] = x[15];
5227
0
                p->y[2][1] = y[15];
5228
0
                for (j = 0; j < nComps; ++j) {
5229
0
                    p->color[0][0].c[j] = c[0][j];
5230
0
                    p->color[0][1].c[j] = c[1][j];
5231
0
                    p->color[1][1].c[j] = c[2][j];
5232
0
                    p->color[1][0].c[j] = c[3][j];
5233
0
                }
5234
0
                break;
5235
0
            case 1:
5236
0
                if (nPatchesA == 0) {
5237
0
                    gfree(patchesA);
5238
0
                    return {};
5239
0
                }
5240
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[0][3];
5241
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[0][3];
5242
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[1][3];
5243
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[1][3];
5244
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[2][3];
5245
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[2][3];
5246
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[3][3];
5247
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[3][3];
5248
0
                p->x[1][3] = x[0];
5249
0
                p->y[1][3] = y[0];
5250
0
                p->x[2][3] = x[1];
5251
0
                p->y[2][3] = y[1];
5252
0
                p->x[3][3] = x[2];
5253
0
                p->y[3][3] = y[2];
5254
0
                p->x[3][2] = x[3];
5255
0
                p->y[3][2] = y[3];
5256
0
                p->x[3][1] = x[4];
5257
0
                p->y[3][1] = y[4];
5258
0
                p->x[3][0] = x[5];
5259
0
                p->y[3][0] = y[5];
5260
0
                p->x[2][0] = x[6];
5261
0
                p->y[2][0] = y[6];
5262
0
                p->x[1][0] = x[7];
5263
0
                p->y[1][0] = y[7];
5264
0
                p->x[1][1] = x[8];
5265
0
                p->y[1][1] = y[8];
5266
0
                p->x[1][2] = x[9];
5267
0
                p->y[1][2] = y[9];
5268
0
                p->x[2][2] = x[10];
5269
0
                p->y[2][2] = y[10];
5270
0
                p->x[2][1] = x[11];
5271
0
                p->y[2][1] = y[11];
5272
0
                for (j = 0; j < nComps; ++j) {
5273
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[0][1].c[j];
5274
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j];
5275
0
                    p->color[1][1].c[j] = c[0][j];
5276
0
                    p->color[1][0].c[j] = c[1][j];
5277
0
                }
5278
0
                break;
5279
0
            case 2:
5280
0
                if (nPatchesA == 0) {
5281
0
                    gfree(patchesA);
5282
0
                    return {};
5283
0
                }
5284
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[3][3];
5285
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[3][3];
5286
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[3][2];
5287
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[3][2];
5288
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[3][1];
5289
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[3][1];
5290
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[3][0];
5291
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[3][0];
5292
0
                p->x[1][3] = x[0];
5293
0
                p->y[1][3] = y[0];
5294
0
                p->x[2][3] = x[1];
5295
0
                p->y[2][3] = y[1];
5296
0
                p->x[3][3] = x[2];
5297
0
                p->y[3][3] = y[2];
5298
0
                p->x[3][2] = x[3];
5299
0
                p->y[3][2] = y[3];
5300
0
                p->x[3][1] = x[4];
5301
0
                p->y[3][1] = y[4];
5302
0
                p->x[3][0] = x[5];
5303
0
                p->y[3][0] = y[5];
5304
0
                p->x[2][0] = x[6];
5305
0
                p->y[2][0] = y[6];
5306
0
                p->x[1][0] = x[7];
5307
0
                p->y[1][0] = y[7];
5308
0
                p->x[1][1] = x[8];
5309
0
                p->y[1][1] = y[8];
5310
0
                p->x[1][2] = x[9];
5311
0
                p->y[1][2] = y[9];
5312
0
                p->x[2][2] = x[10];
5313
0
                p->y[2][2] = y[10];
5314
0
                p->x[2][1] = x[11];
5315
0
                p->y[2][1] = y[11];
5316
0
                for (j = 0; j < nComps; ++j) {
5317
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][1].c[j];
5318
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j];
5319
0
                    p->color[1][1].c[j] = c[0][j];
5320
0
                    p->color[1][0].c[j] = c[1][j];
5321
0
                }
5322
0
                break;
5323
0
            case 3:
5324
0
                if (nPatchesA == 0) {
5325
0
                    gfree(patchesA);
5326
0
                    return {};
5327
0
                }
5328
0
                p->x[0][0] = patchesA[nPatchesA - 1].x[3][0];
5329
0
                p->y[0][0] = patchesA[nPatchesA - 1].y[3][0];
5330
0
                p->x[0][1] = patchesA[nPatchesA - 1].x[2][0];
5331
0
                p->y[0][1] = patchesA[nPatchesA - 1].y[2][0];
5332
0
                p->x[0][2] = patchesA[nPatchesA - 1].x[1][0];
5333
0
                p->y[0][2] = patchesA[nPatchesA - 1].y[1][0];
5334
0
                p->x[0][3] = patchesA[nPatchesA - 1].x[0][0];
5335
0
                p->y[0][3] = patchesA[nPatchesA - 1].y[0][0];
5336
0
                p->x[1][3] = x[0];
5337
0
                p->y[1][3] = y[0];
5338
0
                p->x[2][3] = x[1];
5339
0
                p->y[2][3] = y[1];
5340
0
                p->x[3][3] = x[2];
5341
0
                p->y[3][3] = y[2];
5342
0
                p->x[3][2] = x[3];
5343
0
                p->y[3][2] = y[3];
5344
0
                p->x[3][1] = x[4];
5345
0
                p->y[3][1] = y[4];
5346
0
                p->x[3][0] = x[5];
5347
0
                p->y[3][0] = y[5];
5348
0
                p->x[2][0] = x[6];
5349
0
                p->y[2][0] = y[6];
5350
0
                p->x[1][0] = x[7];
5351
0
                p->y[1][0] = y[7];
5352
0
                p->x[1][1] = x[8];
5353
0
                p->y[1][1] = y[8];
5354
0
                p->x[1][2] = x[9];
5355
0
                p->y[1][2] = y[9];
5356
0
                p->x[2][2] = x[10];
5357
0
                p->y[2][2] = y[10];
5358
0
                p->x[2][1] = x[11];
5359
0
                p->y[2][1] = y[11];
5360
0
                for (j = 0; j < nComps; ++j) {
5361
0
                    p->color[0][0].c[j] = patchesA[nPatchesA - 1].color[1][0].c[j];
5362
0
                    p->color[0][1].c[j] = patchesA[nPatchesA - 1].color[0][0].c[j];
5363
0
                    p->color[1][1].c[j] = c[0][j];
5364
0
                    p->color[1][0].c[j] = c[1][j];
5365
0
                }
5366
0
                break;
5367
0
            }
5368
0
        }
5369
0
        ++nPatchesA;
5370
0
        bitBuf->flushBits();
5371
0
    }
5372
5373
0
    if (typeA == 6) {
5374
0
        for (i = 0; i < nPatchesA; ++i) {
5375
0
            p = &patchesA[i];
5376
0
            p->x[1][1] = (-4 * p->x[0][0] + 6 * (p->x[0][1] + p->x[1][0]) - 2 * (p->x[0][3] + p->x[3][0]) + 3 * (p->x[3][1] + p->x[1][3]) - p->x[3][3]) / 9;
5377
0
            p->y[1][1] = (-4 * p->y[0][0] + 6 * (p->y[0][1] + p->y[1][0]) - 2 * (p->y[0][3] + p->y[3][0]) + 3 * (p->y[3][1] + p->y[1][3]) - p->y[3][3]) / 9;
5378
0
            p->x[1][2] = (-4 * p->x[0][3] + 6 * (p->x[0][2] + p->x[1][3]) - 2 * (p->x[0][0] + p->x[3][3]) + 3 * (p->x[3][2] + p->x[1][0]) - p->x[3][0]) / 9;
5379
0
            p->y[1][2] = (-4 * p->y[0][3] + 6 * (p->y[0][2] + p->y[1][3]) - 2 * (p->y[0][0] + p->y[3][3]) + 3 * (p->y[3][2] + p->y[1][0]) - p->y[3][0]) / 9;
5380
0
            p->x[2][1] = (-4 * p->x[3][0] + 6 * (p->x[3][1] + p->x[2][0]) - 2 * (p->x[3][3] + p->x[0][0]) + 3 * (p->x[0][1] + p->x[2][3]) - p->x[0][3]) / 9;
5381
0
            p->y[2][1] = (-4 * p->y[3][0] + 6 * (p->y[3][1] + p->y[2][0]) - 2 * (p->y[3][3] + p->y[0][0]) + 3 * (p->y[0][1] + p->y[2][3]) - p->y[0][3]) / 9;
5382
0
            p->x[2][2] = (-4 * p->x[3][3] + 6 * (p->x[3][2] + p->x[2][3]) - 2 * (p->x[3][0] + p->x[0][3]) + 3 * (p->x[0][2] + p->x[2][0]) - p->x[0][0]) / 9;
5383
0
            p->y[2][2] = (-4 * p->y[3][3] + 6 * (p->y[3][2] + p->y[2][3]) - 2 * (p->y[3][0] + p->y[0][3]) + 3 * (p->y[0][2] + p->y[2][0]) - p->y[0][0]) / 9;
5384
0
        }
5385
0
    }
5386
5387
0
    auto shading = std::make_unique<GfxPatchMeshShading>(typeA, patchesA, nPatchesA, std::move(funcsA));
5388
0
    if (!shading->init(res, dict, out, state)) {
5389
0
        return {};
5390
0
    }
5391
0
    return shading;
5392
0
}
5393
5394
bool GfxPatchMeshShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state)
5395
0
{
5396
0
    const bool parentInit = GfxShading::init(res, dict, out, state);
5397
0
    if (!parentInit) {
5398
0
        return false;
5399
0
    }
5400
5401
    // funcs needs to be one of the three:
5402
    //  * One function 1-in -> nComps-out
5403
    //  * nComps functions 1-in -> 1-out
5404
    //  * empty
5405
0
    const int nComps = colorSpace->getNComps();
5406
0
    const int nFuncs = funcs.size();
5407
0
    if (nFuncs == 1) {
5408
0
        if (funcs[0]->getInputSize() != 1) {
5409
0
            error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with input size != 2");
5410
0
            return false;
5411
0
        }
5412
0
        if (funcs[0]->getOutputSize() != nComps) {
5413
0
            error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with wrong output size");
5414
0
            return false;
5415
0
        }
5416
0
    } else if (nFuncs == nComps) {
5417
0
        for (const std::unique_ptr<Function> &f : funcs) {
5418
0
            if (f->getInputSize() != 1) {
5419
0
                error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with input size != 2");
5420
0
                return false;
5421
0
            }
5422
0
            if (f->getOutputSize() != 1) {
5423
0
                error(errSyntaxWarning, -1, "GfxPatchMeshShading: function with wrong output size");
5424
0
                return false;
5425
0
            }
5426
0
        }
5427
0
    } else if (nFuncs != 0) {
5428
0
        return false;
5429
0
    }
5430
5431
0
    return true;
5432
0
}
5433
5434
void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) const
5435
0
{
5436
0
    double out[gfxColorMaxComps] = {};
5437
5438
0
    for (unsigned int j = 0; j < funcs.size(); ++j) {
5439
0
        funcs[j]->transform(&t, &out[j]);
5440
0
    }
5441
0
    for (int j = 0; j < gfxColorMaxComps; ++j) {
5442
0
        color->c[j] = dblToCol(out[j]);
5443
0
    }
5444
0
}
5445
5446
std::unique_ptr<GfxShading> GfxPatchMeshShading::copy() const
5447
0
{
5448
0
    return std::make_unique<GfxPatchMeshShading>(this);
5449
0
}
5450
5451
//------------------------------------------------------------------------
5452
// GfxImageColorMap
5453
//------------------------------------------------------------------------
5454
5455
0
GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode, std::unique_ptr<GfxColorSpace> &&colorSpaceA) : colorSpace(std::move(colorSpaceA))
5456
0
{
5457
0
    int maxPixel, indexHigh;
5458
0
    unsigned char *indexedLookup;
5459
0
    const Function *sepFunc;
5460
0
    double x[gfxColorMaxComps];
5461
0
    double y[gfxColorMaxComps] = {};
5462
0
    int i, j, k;
5463
0
    double mapped;
5464
0
    bool useByteLookup;
5465
5466
0
    ok = true;
5467
0
    useMatte = false;
5468
5469
    // initialize
5470
0
    for (k = 0; k < gfxColorMaxComps; ++k) {
5471
0
        lookup[k] = nullptr;
5472
0
        lookup2[k] = nullptr;
5473
0
    }
5474
0
    byte_lookup = nullptr;
5475
5476
    // bits per component and color space
5477
0
    if (unlikely(bitsA <= 0 || bitsA > 30)) {
5478
0
        goto err1;
5479
0
    }
5480
5481
0
    bits = bitsA;
5482
0
    maxPixel = (1 << bits) - 1;
5483
5484
    // this is a hack to support 16 bits images, everywhere
5485
    // we assume a component fits in 8 bits, with this hack
5486
    // we treat 16 bit images as 8 bit ones until it's fixed correctly.
5487
    // The hack has another part on ImageStream::getLine
5488
0
    if (maxPixel > 255) {
5489
0
        maxPixel = 255;
5490
0
    }
5491
5492
    // get decode map
5493
0
    if (decode->isNull()) {
5494
0
        nComps = colorSpace->getNComps();
5495
0
        colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
5496
0
    } else if (decode->isArray()) {
5497
0
        nComps = decode->arrayGetLength() / 2;
5498
0
        if (nComps < colorSpace->getNComps()) {
5499
0
            goto err1;
5500
0
        }
5501
0
        if (nComps > colorSpace->getNComps()) {
5502
0
            error(errSyntaxWarning, -1, "Too many elements in Decode array");
5503
0
            nComps = colorSpace->getNComps();
5504
0
        }
5505
0
        for (i = 0; i < nComps; ++i) {
5506
0
            Object obj = decode->arrayGet(2 * i);
5507
0
            if (!obj.isNum()) {
5508
0
                goto err1;
5509
0
            }
5510
0
            decodeLow[i] = obj.getNum();
5511
0
            obj = decode->arrayGet(2 * i + 1);
5512
0
            if (!obj.isNum()) {
5513
0
                goto err1;
5514
0
            }
5515
0
            decodeRange[i] = obj.getNum() - decodeLow[i];
5516
0
        }
5517
0
    } else {
5518
0
        goto err1;
5519
0
    }
5520
5521
    // Construct a lookup table -- this stores pre-computed decoded
5522
    // values for each component, i.e., the result of applying the
5523
    // decode mapping to each possible image pixel component value.
5524
0
    for (k = 0; k < nComps; ++k) {
5525
0
        lookup[k] = static_cast<GfxColorComp *>(gmallocn(maxPixel + 1, sizeof(GfxColorComp)));
5526
0
        for (i = 0; i <= maxPixel; ++i) {
5527
0
            lookup[k][i] = dblToCol(decodeLow[k] + (i * decodeRange[k]) / maxPixel);
5528
0
        }
5529
0
    }
5530
5531
    // Optimization: for Indexed and Separation color spaces (which have
5532
    // only one component), we pre-compute a second lookup table with
5533
    // color values
5534
0
    colorSpace2 = nullptr;
5535
0
    nComps2 = 0;
5536
0
    useByteLookup = false;
5537
0
    switch (colorSpace->getMode()) {
5538
0
    case csIndexed: {
5539
        // Note that indexHigh may not be the same as maxPixel --
5540
        // Distiller will remove unused palette entries, resulting in
5541
        // indexHigh < maxPixel.
5542
0
        auto *indexedCS = static_cast<GfxIndexedColorSpace *>(colorSpace.get());
5543
0
        colorSpace2 = indexedCS->getBase();
5544
0
        indexHigh = indexedCS->getIndexHigh();
5545
0
        nComps2 = colorSpace2->getNComps();
5546
0
        indexedLookup = indexedCS->getLookup();
5547
0
        colorSpace2->getDefaultRanges(x, y, indexHigh);
5548
0
        if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5549
0
            byte_lookup = static_cast<unsigned char *>(gmallocn((maxPixel + 1), nComps2));
5550
0
            useByteLookup = true;
5551
0
        }
5552
0
        for (k = 0; k < nComps2; ++k) {
5553
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(maxPixel + 1, sizeof(GfxColorComp)));
5554
0
            for (i = 0; i <= maxPixel; ++i) {
5555
0
                j = static_cast<int>(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
5556
0
                if (j < 0) {
5557
0
                    j = 0;
5558
0
                } else if (j > indexHigh) {
5559
0
                    j = indexHigh;
5560
0
                }
5561
5562
0
                mapped = x[k] + (indexedLookup[j * nComps2 + k] / 255.0) * y[k];
5563
0
                lookup2[k][i] = dblToCol(mapped);
5564
0
                if (useByteLookup) {
5565
0
                    byte_lookup[i * nComps2 + k] = static_cast<unsigned char>(mapped * 255);
5566
0
                }
5567
0
            }
5568
0
        }
5569
0
        break;
5570
0
    }
5571
0
    case csSeparation: {
5572
0
        auto *sepCS = static_cast<GfxSeparationColorSpace *>(colorSpace.get());
5573
0
        colorSpace2 = sepCS->getAlt();
5574
0
        nComps2 = colorSpace2->getNComps();
5575
0
        sepFunc = sepCS->getFunc();
5576
0
        if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5577
0
            byte_lookup = static_cast<unsigned char *>(gmallocn((maxPixel + 1), nComps2));
5578
0
            useByteLookup = true;
5579
0
        }
5580
0
        for (k = 0; k < nComps2; ++k) {
5581
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(maxPixel + 1, sizeof(GfxColorComp)));
5582
0
            for (i = 0; i <= maxPixel; ++i) {
5583
0
                x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
5584
0
                sepFunc->transform(x, y);
5585
0
                lookup2[k][i] = dblToCol(y[k]);
5586
0
                if (useByteLookup) {
5587
0
                    byte_lookup[i * nComps2 + k] = static_cast<unsigned char>(y[k] * 255);
5588
0
                }
5589
0
            }
5590
0
        }
5591
0
        break;
5592
0
    }
5593
0
    default:
5594
0
        if ((!decode->isNull() || maxPixel != 255) && (colorSpace->useGetGrayLine() || (colorSpace->useGetRGBLine() && !decode->isNull()) || colorSpace->useGetCMYKLine() || colorSpace->useGetDeviceNLine())) {
5595
0
            byte_lookup = static_cast<unsigned char *>(gmallocn((maxPixel + 1), nComps));
5596
0
            useByteLookup = true;
5597
0
        }
5598
0
        for (k = 0; k < nComps; ++k) {
5599
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(maxPixel + 1, sizeof(GfxColorComp)));
5600
0
            for (i = 0; i <= maxPixel; ++i) {
5601
0
                mapped = decodeLow[k] + (i * decodeRange[k]) / maxPixel;
5602
0
                lookup2[k][i] = dblToCol(mapped);
5603
0
                if (useByteLookup) {
5604
0
                    int byte;
5605
5606
0
                    byte = static_cast<int>(mapped * 255.0 + 0.5);
5607
0
                    if (byte < 0) {
5608
0
                        byte = 0;
5609
0
                    } else if (byte > 255) {
5610
0
                        byte = 255;
5611
0
                    }
5612
0
                    byte_lookup[i * nComps + k] = byte;
5613
0
                }
5614
0
            }
5615
0
        }
5616
0
    }
5617
5618
0
    return;
5619
5620
0
err1:
5621
0
    ok = false;
5622
0
}
5623
5624
GfxImageColorMap::GfxImageColorMap(const GfxImageColorMap *colorMap)
5625
0
{
5626
0
    int n, i, k;
5627
5628
0
    colorSpace = colorMap->colorSpace->copy();
5629
0
    bits = colorMap->bits;
5630
0
    nComps = colorMap->nComps;
5631
0
    nComps2 = colorMap->nComps2;
5632
0
    useMatte = colorMap->useMatte;
5633
0
    matteColor = colorMap->matteColor;
5634
0
    colorSpace2 = nullptr;
5635
0
    for (k = 0; k < gfxColorMaxComps; ++k) {
5636
0
        lookup[k] = nullptr;
5637
0
        lookup2[k] = nullptr;
5638
0
    }
5639
0
    byte_lookup = nullptr;
5640
0
    n = 1 << bits;
5641
0
    for (k = 0; k < nComps; ++k) {
5642
0
        lookup[k] = static_cast<GfxColorComp *>(gmallocn(n, sizeof(GfxColorComp)));
5643
0
        memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
5644
0
    }
5645
0
    if (colorSpace->getMode() == csIndexed) {
5646
0
        colorSpace2 = (static_cast<GfxIndexedColorSpace *>(colorSpace.get()))->getBase();
5647
0
        for (k = 0; k < nComps2; ++k) {
5648
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(n, sizeof(GfxColorComp)));
5649
0
            memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp));
5650
0
        }
5651
0
    } else if (colorSpace->getMode() == csSeparation) {
5652
0
        colorSpace2 = (static_cast<GfxSeparationColorSpace *>(colorSpace.get()))->getAlt();
5653
0
        for (k = 0; k < nComps2; ++k) {
5654
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(n, sizeof(GfxColorComp)));
5655
0
            memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp));
5656
0
        }
5657
0
    } else {
5658
0
        for (k = 0; k < nComps; ++k) {
5659
0
            lookup2[k] = static_cast<GfxColorComp *>(gmallocn(n, sizeof(GfxColorComp)));
5660
0
            memcpy(lookup2[k], colorMap->lookup2[k], n * sizeof(GfxColorComp));
5661
0
        }
5662
0
    }
5663
0
    if (colorMap->byte_lookup) {
5664
0
        int nc = colorSpace2 ? nComps2 : nComps;
5665
5666
0
        byte_lookup = static_cast<unsigned char *>(gmallocn(n, nc));
5667
0
        memcpy(byte_lookup, colorMap->byte_lookup, n * nc);
5668
0
    }
5669
0
    for (i = 0; i < nComps; ++i) {
5670
0
        decodeLow[i] = colorMap->decodeLow[i];
5671
0
        decodeRange[i] = colorMap->decodeRange[i];
5672
0
    }
5673
0
    ok = true;
5674
0
}
5675
5676
GfxImageColorMap::~GfxImageColorMap()
5677
0
{
5678
0
    int i;
5679
5680
0
    for (i = 0; i < gfxColorMaxComps; ++i) {
5681
0
        gfree(lookup[i]);
5682
0
        gfree(lookup2[i]);
5683
0
    }
5684
0
    gfree(byte_lookup);
5685
0
}
5686
5687
void GfxImageColorMap::getGray(const unsigned char *x, GfxGray *gray) const
5688
0
{
5689
0
    GfxColor color;
5690
0
    int i;
5691
5692
0
    if (colorSpace2) {
5693
0
        for (i = 0; i < nComps2; ++i) {
5694
0
            color.c[i] = lookup2[i][x[0]];
5695
0
        }
5696
0
        colorSpace2->getGray(color, gray);
5697
0
    } else {
5698
0
        for (i = 0; i < nComps; ++i) {
5699
0
            color.c[i] = lookup2[i][x[i]];
5700
0
        }
5701
0
        colorSpace->getGray(color, gray);
5702
0
    }
5703
0
}
5704
5705
void GfxImageColorMap::getRGB(const unsigned char *x, GfxRGB *rgb)
5706
0
{
5707
0
    GfxColor color;
5708
0
    int i;
5709
5710
0
    if (colorSpace2) {
5711
0
        for (i = 0; i < nComps2; ++i) {
5712
0
            color.c[i] = lookup2[i][x[0]];
5713
0
        }
5714
0
        colorSpace2->getRGB(color, rgb);
5715
0
    } else {
5716
0
        for (i = 0; i < nComps; ++i) {
5717
0
            color.c[i] = lookup2[i][x[i]];
5718
0
        }
5719
0
        colorSpace->getRGB(color, rgb);
5720
0
    }
5721
0
}
5722
5723
void GfxImageColorMap::getGrayLine(unsigned char *in, unsigned char *out, int length)
5724
0
{
5725
0
    int i, j;
5726
0
    unsigned char *inp, *tmp_line;
5727
5728
0
    if ((colorSpace2 && !colorSpace2->useGetGrayLine()) || (!colorSpace2 && !colorSpace->useGetGrayLine())) {
5729
0
        GfxGray gray;
5730
5731
0
        inp = in;
5732
0
        for (i = 0; i < length; i++) {
5733
0
            getGray(inp, &gray);
5734
0
            out[i] = colToByte(gray);
5735
0
            inp += nComps;
5736
0
        }
5737
0
        return;
5738
0
    }
5739
5740
0
    switch (colorSpace->getMode()) {
5741
0
    case csIndexed:
5742
0
    case csSeparation:
5743
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5744
0
        for (i = 0; i < length; i++) {
5745
0
            for (j = 0; j < nComps2; j++) {
5746
0
                unsigned char c = in[i];
5747
0
                if (byte_lookup) {
5748
0
                    c = byte_lookup[c * nComps2 + j];
5749
0
                }
5750
0
                tmp_line[i * nComps2 + j] = c;
5751
0
            }
5752
0
        }
5753
0
        colorSpace2->getGrayLine(tmp_line, out, length);
5754
0
        gfree(tmp_line);
5755
0
        break;
5756
5757
0
    default:
5758
0
        if (byte_lookup) {
5759
0
            inp = in;
5760
0
            for (j = 0; j < length; j++) {
5761
0
                for (i = 0; i < nComps; i++) {
5762
0
                    *inp = byte_lookup[*inp * nComps + i];
5763
0
                    inp++;
5764
0
                }
5765
0
            }
5766
0
        }
5767
0
        colorSpace->getGrayLine(in, out, length);
5768
0
        break;
5769
0
    }
5770
0
}
5771
5772
void GfxImageColorMap::getRGBLine(unsigned char *in, unsigned int *out, int length)
5773
0
{
5774
0
    int i, j;
5775
0
    unsigned char *inp, *tmp_line;
5776
5777
0
    if (!useRGBLine()) {
5778
0
        GfxRGB rgb;
5779
5780
0
        inp = in;
5781
0
        for (i = 0; i < length; i++) {
5782
0
            getRGB(inp, &rgb);
5783
0
            out[i] = (static_cast<int>(colToByte(rgb.r)) << 16) | (static_cast<int>(colToByte(rgb.g)) << 8) | (static_cast<int>(colToByte(rgb.b)) << 0);
5784
0
            inp += nComps;
5785
0
        }
5786
0
        return;
5787
0
    }
5788
5789
0
    switch (colorSpace->getMode()) {
5790
0
    case csIndexed:
5791
0
    case csSeparation:
5792
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5793
0
        for (i = 0; i < length; i++) {
5794
0
            for (j = 0; j < nComps2; j++) {
5795
0
                unsigned char c = in[i];
5796
0
                if (byte_lookup) {
5797
0
                    c = byte_lookup[c * nComps2 + j];
5798
0
                }
5799
0
                tmp_line[i * nComps2 + j] = c;
5800
0
            }
5801
0
        }
5802
0
        colorSpace2->getRGBLine(tmp_line, out, length);
5803
0
        gfree(tmp_line);
5804
0
        break;
5805
5806
0
    default:
5807
0
        if (byte_lookup) {
5808
0
            inp = in;
5809
0
            for (j = 0; j < length; j++) {
5810
0
                for (i = 0; i < nComps; i++) {
5811
0
                    *inp = byte_lookup[*inp * nComps + i];
5812
0
                    inp++;
5813
0
                }
5814
0
            }
5815
0
        }
5816
0
        colorSpace->getRGBLine(in, out, length);
5817
0
        break;
5818
0
    }
5819
0
}
5820
5821
void GfxImageColorMap::getRGBLine(unsigned char *in, unsigned char *out, int length)
5822
0
{
5823
0
    int i, j;
5824
0
    unsigned char *inp, *tmp_line;
5825
5826
0
    if (!useRGBLine()) {
5827
0
        GfxRGB rgb;
5828
5829
0
        inp = in;
5830
0
        for (i = 0; i < length; i++) {
5831
0
            getRGB(inp, &rgb);
5832
0
            *out++ = colToByte(rgb.r);
5833
0
            *out++ = colToByte(rgb.g);
5834
0
            *out++ = colToByte(rgb.b);
5835
0
            inp += nComps;
5836
0
        }
5837
0
        return;
5838
0
    }
5839
5840
0
    switch (colorSpace->getMode()) {
5841
0
    case csIndexed:
5842
0
    case csSeparation:
5843
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5844
0
        for (i = 0; i < length; i++) {
5845
0
            for (j = 0; j < nComps2; j++) {
5846
0
                unsigned char c = in[i];
5847
0
                if (byte_lookup) {
5848
0
                    c = byte_lookup[c * nComps2 + j];
5849
0
                }
5850
0
                tmp_line[i * nComps2 + j] = c;
5851
0
            }
5852
0
        }
5853
0
        colorSpace2->getRGBLine(tmp_line, out, length);
5854
0
        gfree(tmp_line);
5855
0
        break;
5856
5857
0
    default:
5858
0
        if (byte_lookup) {
5859
0
            inp = in;
5860
0
            for (j = 0; j < length; j++) {
5861
0
                for (i = 0; i < nComps; i++) {
5862
0
                    *inp = byte_lookup[*inp * nComps + i];
5863
0
                    inp++;
5864
0
                }
5865
0
            }
5866
0
        }
5867
0
        colorSpace->getRGBLine(in, out, length);
5868
0
        break;
5869
0
    }
5870
0
}
5871
5872
void GfxImageColorMap::getRGBXLine(unsigned char *in, unsigned char *out, int length)
5873
0
{
5874
0
    int i, j;
5875
0
    unsigned char *inp, *tmp_line;
5876
5877
0
    if (!useRGBLine()) {
5878
0
        GfxRGB rgb;
5879
5880
0
        inp = in;
5881
0
        for (i = 0; i < length; i++) {
5882
0
            getRGB(inp, &rgb);
5883
0
            *out++ = colToByte(rgb.r);
5884
0
            *out++ = colToByte(rgb.g);
5885
0
            *out++ = colToByte(rgb.b);
5886
0
            *out++ = 255;
5887
0
            inp += nComps;
5888
0
        }
5889
0
        return;
5890
0
    }
5891
5892
0
    switch (colorSpace->getMode()) {
5893
0
    case csIndexed:
5894
0
    case csSeparation:
5895
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5896
0
        for (i = 0; i < length; i++) {
5897
0
            for (j = 0; j < nComps2; j++) {
5898
0
                unsigned char c = in[i];
5899
0
                if (byte_lookup) {
5900
0
                    c = byte_lookup[c * nComps2 + j];
5901
0
                }
5902
0
                tmp_line[i * nComps2 + j] = c;
5903
0
            }
5904
0
        }
5905
0
        colorSpace2->getRGBXLine(tmp_line, out, length);
5906
0
        gfree(tmp_line);
5907
0
        break;
5908
5909
0
    default:
5910
0
        if (byte_lookup) {
5911
0
            inp = in;
5912
0
            for (j = 0; j < length; j++) {
5913
0
                for (i = 0; i < nComps; i++) {
5914
0
                    *inp = byte_lookup[*inp * nComps + i];
5915
0
                    inp++;
5916
0
                }
5917
0
            }
5918
0
        }
5919
0
        colorSpace->getRGBXLine(in, out, length);
5920
0
        break;
5921
0
    }
5922
0
}
5923
5924
void GfxImageColorMap::getCMYKLine(unsigned char *in, unsigned char *out, int length)
5925
0
{
5926
0
    int i, j;
5927
0
    unsigned char *inp, *tmp_line;
5928
5929
0
    if (!useCMYKLine()) {
5930
0
        GfxCMYK cmyk;
5931
5932
0
        inp = in;
5933
0
        for (i = 0; i < length; i++) {
5934
0
            getCMYK(inp, &cmyk);
5935
0
            *out++ = colToByte(cmyk.c);
5936
0
            *out++ = colToByte(cmyk.m);
5937
0
            *out++ = colToByte(cmyk.y);
5938
0
            *out++ = colToByte(cmyk.k);
5939
0
            inp += nComps;
5940
0
        }
5941
0
        return;
5942
0
    }
5943
5944
0
    switch (colorSpace->getMode()) {
5945
0
    case csIndexed:
5946
0
    case csSeparation:
5947
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5948
0
        for (i = 0; i < length; i++) {
5949
0
            for (j = 0; j < nComps2; j++) {
5950
0
                unsigned char c = in[i];
5951
0
                if (byte_lookup) {
5952
0
                    c = byte_lookup[c * nComps2 + j];
5953
0
                }
5954
0
                tmp_line[i * nComps2 + j] = c;
5955
0
            }
5956
0
        }
5957
0
        colorSpace2->getCMYKLine(tmp_line, out, length);
5958
0
        gfree(tmp_line);
5959
0
        break;
5960
5961
0
    default:
5962
0
        if (byte_lookup) {
5963
0
            inp = in;
5964
0
            for (j = 0; j < length; j++) {
5965
0
                for (i = 0; i < nComps; i++) {
5966
0
                    *inp = byte_lookup[*inp * nComps + i];
5967
0
                    inp++;
5968
0
                }
5969
0
            }
5970
0
        }
5971
0
        colorSpace->getCMYKLine(in, out, length);
5972
0
        break;
5973
0
    }
5974
0
}
5975
5976
void GfxImageColorMap::getDeviceNLine(unsigned char *in, unsigned char *out, int length)
5977
0
{
5978
0
    unsigned char *inp, *tmp_line;
5979
5980
0
    if (!useDeviceNLine()) {
5981
0
        GfxColor deviceN;
5982
5983
0
        inp = in;
5984
0
        for (int i = 0; i < length; i++) {
5985
0
            getDeviceN(inp, &deviceN);
5986
0
            for (int j = 0; j < SPOT_NCOMPS + 4; j++) {
5987
0
                *out++ = deviceN.c[j];
5988
0
            }
5989
0
            inp += nComps;
5990
0
        }
5991
0
        return;
5992
0
    }
5993
5994
0
    switch (colorSpace->getMode()) {
5995
0
    case csIndexed:
5996
0
    case csSeparation:
5997
0
        tmp_line = static_cast<unsigned char *>(gmallocn(length, nComps2));
5998
0
        for (int i = 0; i < length; i++) {
5999
0
            for (int j = 0; j < nComps2; j++) {
6000
0
                unsigned char c = in[i];
6001
0
                if (byte_lookup) {
6002
0
                    c = byte_lookup[c * nComps2 + j];
6003
0
                }
6004
0
                tmp_line[i * nComps2 + j] = c;
6005
0
            }
6006
0
        }
6007
0
        colorSpace2->getDeviceNLine(tmp_line, out, length);
6008
0
        gfree(tmp_line);
6009
0
        break;
6010
6011
0
    default:
6012
0
        if (byte_lookup) {
6013
0
            inp = in;
6014
0
            for (int j = 0; j < length; j++) {
6015
0
                for (int i = 0; i < nComps; i++) {
6016
0
                    *inp = byte_lookup[*inp * nComps + i];
6017
0
                    inp++;
6018
0
                }
6019
0
            }
6020
0
        }
6021
0
        colorSpace->getDeviceNLine(in, out, length);
6022
0
        break;
6023
0
    }
6024
0
}
6025
6026
void GfxImageColorMap::getCMYK(const unsigned char *x, GfxCMYK *cmyk) const
6027
0
{
6028
0
    GfxColor color;
6029
0
    int i;
6030
6031
0
    if (colorSpace2) {
6032
0
        for (i = 0; i < nComps2; ++i) {
6033
0
            color.c[i] = lookup2[i][x[0]];
6034
0
        }
6035
0
        colorSpace2->getCMYK(color, cmyk);
6036
0
    } else {
6037
0
        for (i = 0; i < nComps; ++i) {
6038
0
            color.c[i] = lookup[i][x[i]];
6039
0
        }
6040
0
        colorSpace->getCMYK(color, cmyk);
6041
0
    }
6042
0
}
6043
6044
void GfxImageColorMap::getDeviceN(const unsigned char *x, GfxColor *deviceN)
6045
0
{
6046
0
    GfxColor color;
6047
0
    int i;
6048
6049
0
    if (colorSpace2 && (colorSpace->getMapping().empty() || colorSpace->getMapping()[0] == -1)) {
6050
0
        for (i = 0; i < nComps2; ++i) {
6051
0
            color.c[i] = lookup2[i][x[0]];
6052
0
        }
6053
0
        colorSpace2->getDeviceN(color, deviceN);
6054
0
    } else {
6055
0
        for (i = 0; i < nComps; ++i) {
6056
0
            color.c[i] = lookup[i][x[i]];
6057
0
        }
6058
0
        colorSpace->getDeviceN(color, deviceN);
6059
0
    }
6060
0
}
6061
6062
void GfxImageColorMap::getColor(const unsigned char *x, GfxColor *color)
6063
0
{
6064
0
    int maxPixel, i;
6065
6066
0
    maxPixel = (1 << bits) - 1;
6067
0
    for (i = 0; i < nComps; ++i) {
6068
0
        color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
6069
0
    }
6070
0
}
6071
6072
//------------------------------------------------------------------------
6073
// GfxSubpath and GfxPath
6074
//------------------------------------------------------------------------
6075
6076
GfxSubpath::GfxSubpath(double x1, double y1)
6077
13.6k
{
6078
13.6k
    size = 16;
6079
13.6k
    x = static_cast<double *>(gmallocn(size, sizeof(double)));
6080
13.6k
    y = static_cast<double *>(gmallocn(size, sizeof(double)));
6081
13.6k
    curve = static_cast<bool *>(gmallocn(size, sizeof(bool)));
6082
13.6k
    n = 1;
6083
13.6k
    x[0] = x1;
6084
13.6k
    y[0] = y1;
6085
13.6k
    curve[0] = false;
6086
13.6k
    closed = false;
6087
13.6k
}
6088
6089
GfxSubpath::~GfxSubpath()
6090
13.6k
{
6091
13.6k
    gfree(x);
6092
13.6k
    gfree(y);
6093
13.6k
    gfree(curve);
6094
13.6k
}
6095
6096
// Used for copy().
6097
GfxSubpath::GfxSubpath(const GfxSubpath *subpath)
6098
14
{
6099
14
    size = subpath->size;
6100
14
    n = subpath->n;
6101
14
    x = static_cast<double *>(gmallocn(size, sizeof(double)));
6102
14
    y = static_cast<double *>(gmallocn(size, sizeof(double)));
6103
14
    curve = static_cast<bool *>(gmallocn(size, sizeof(bool)));
6104
14
    memcpy(x, subpath->x, n * sizeof(double));
6105
14
    memcpy(y, subpath->y, n * sizeof(double));
6106
14
    memcpy(curve, subpath->curve, n * sizeof(bool));
6107
14
    closed = subpath->closed;
6108
14
}
6109
6110
void GfxSubpath::lineTo(double x1, double y1)
6111
54.0k
{
6112
54.0k
    if (n >= size) {
6113
0
        size *= 2;
6114
0
        x = static_cast<double *>(greallocn(x, size, sizeof(double)));
6115
0
        y = static_cast<double *>(greallocn(y, size, sizeof(double)));
6116
0
        curve = static_cast<bool *>(greallocn(curve, size, sizeof(bool)));
6117
0
    }
6118
54.0k
    x[n] = x1;
6119
54.0k
    y[n] = y1;
6120
54.0k
    curve[n] = false;
6121
54.0k
    ++n;
6122
54.0k
}
6123
6124
void GfxSubpath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3)
6125
185
{
6126
185
    if (n + 3 > size) {
6127
7
        size *= 2;
6128
7
        x = static_cast<double *>(greallocn(x, size, sizeof(double)));
6129
7
        y = static_cast<double *>(greallocn(y, size, sizeof(double)));
6130
7
        curve = static_cast<bool *>(greallocn(curve, size, sizeof(bool)));
6131
7
    }
6132
185
    x[n] = x1;
6133
185
    y[n] = y1;
6134
185
    x[n + 1] = x2;
6135
185
    y[n + 1] = y2;
6136
185
    x[n + 2] = x3;
6137
185
    y[n + 2] = y3;
6138
185
    curve[n] = curve[n + 1] = true;
6139
185
    curve[n + 2] = false;
6140
185
    n += 3;
6141
185
}
6142
6143
void GfxSubpath::close()
6144
14.2k
{
6145
14.2k
    if (x[n - 1] != x[0] || y[n - 1] != y[0]) {
6146
13.4k
        lineTo(x[0], y[0]);
6147
13.4k
    }
6148
14.2k
    closed = true;
6149
14.2k
}
6150
6151
void GfxSubpath::offset(double dx, double dy)
6152
0
{
6153
0
    int i;
6154
6155
0
    for (i = 0; i < n; ++i) {
6156
0
        x[i] += dx;
6157
0
        y[i] += dy;
6158
0
    }
6159
0
}
6160
6161
GfxPath::GfxPath()
6162
20.5k
{
6163
20.5k
    justMoved = false;
6164
20.5k
    size = 16;
6165
20.5k
    n = 0;
6166
20.5k
    firstX = firstY = 0;
6167
20.5k
    subpaths = static_cast<GfxSubpath **>(gmallocn(size, sizeof(GfxSubpath *)));
6168
20.5k
}
6169
6170
GfxPath::~GfxPath()
6171
21.3k
{
6172
21.3k
    int i;
6173
6174
35.0k
    for (i = 0; i < n; ++i) {
6175
13.6k
        delete subpaths[i];
6176
13.6k
    }
6177
21.3k
    gfree(static_cast<void *>(subpaths));
6178
21.3k
}
6179
6180
// Used for copy().
6181
GfxPath::GfxPath(bool justMoved1, double firstX1, double firstY1, GfxSubpath **subpaths1, int n1, int size1)
6182
776
{
6183
776
    int i;
6184
6185
776
    justMoved = justMoved1;
6186
776
    firstX = firstX1;
6187
776
    firstY = firstY1;
6188
776
    size = size1;
6189
776
    n = n1;
6190
776
    subpaths = static_cast<GfxSubpath **>(gmallocn(size, sizeof(GfxSubpath *)));
6191
790
    for (i = 0; i < n; ++i) {
6192
14
        subpaths[i] = subpaths1[i]->copy();
6193
14
    }
6194
776
}
6195
6196
void GfxPath::moveTo(double x, double y)
6197
13.5k
{
6198
13.5k
    justMoved = true;
6199
13.5k
    firstX = x;
6200
13.5k
    firstY = y;
6201
13.5k
}
6202
6203
void GfxPath::lineTo(double x, double y)
6204
40.5k
{
6205
40.5k
    if (justMoved || (n > 0 && subpaths[n - 1]->isClosed())) {
6206
13.5k
        if (n >= size) {
6207
19
            size *= 2;
6208
19
            subpaths = static_cast<GfxSubpath **>(greallocn(static_cast<void *>(subpaths), size, sizeof(GfxSubpath *)));
6209
19
        }
6210
13.5k
        if (justMoved) {
6211
13.5k
            subpaths[n] = new GfxSubpath(firstX, firstY);
6212
13.5k
        } else {
6213
0
            subpaths[n] = new GfxSubpath(subpaths[n - 1]->getLastX(), subpaths[n - 1]->getLastY());
6214
0
        }
6215
13.5k
        ++n;
6216
13.5k
        justMoved = false;
6217
13.5k
    }
6218
40.5k
    subpaths[n - 1]->lineTo(x, y);
6219
40.5k
}
6220
6221
void GfxPath::curveTo(double x1, double y1, double x2, double y2, double x3, double y3)
6222
185
{
6223
185
    if (justMoved || (n > 0 && subpaths[n - 1]->isClosed())) {
6224
127
        if (n >= size) {
6225
0
            size *= 2;
6226
0
            subpaths = static_cast<GfxSubpath **>(greallocn(static_cast<void *>(subpaths), size, sizeof(GfxSubpath *)));
6227
0
        }
6228
127
        if (justMoved) {
6229
4
            subpaths[n] = new GfxSubpath(firstX, firstY);
6230
123
        } else {
6231
123
            subpaths[n] = new GfxSubpath(subpaths[n - 1]->getLastX(), subpaths[n - 1]->getLastY());
6232
123
        }
6233
127
        ++n;
6234
127
        justMoved = false;
6235
127
    }
6236
185
    subpaths[n - 1]->curveTo(x1, y1, x2, y2, x3, y3);
6237
185
}
6238
6239
void GfxPath::close()
6240
14.2k
{
6241
    // this is necessary to handle the pathological case of
6242
    // moveto/closepath/clip, which defines an empty clipping region
6243
14.2k
    if (justMoved) {
6244
20
        if (n >= size) {
6245
0
            size *= 2;
6246
0
            subpaths = static_cast<GfxSubpath **>(greallocn(static_cast<void *>(subpaths), size, sizeof(GfxSubpath *)));
6247
0
        }
6248
20
        subpaths[n] = new GfxSubpath(firstX, firstY);
6249
20
        ++n;
6250
20
        justMoved = false;
6251
20
    }
6252
14.2k
    subpaths[n - 1]->close();
6253
14.2k
}
6254
6255
void GfxPath::append(GfxPath *path)
6256
0
{
6257
0
    int i;
6258
6259
0
    if (n + path->n > size) {
6260
0
        size = n + path->n;
6261
0
        subpaths = static_cast<GfxSubpath **>(greallocn(static_cast<void *>(subpaths), size, sizeof(GfxSubpath *)));
6262
0
    }
6263
0
    for (i = 0; i < path->n; ++i) {
6264
0
        subpaths[n++] = path->subpaths[i]->copy();
6265
0
    }
6266
0
    justMoved = false;
6267
0
}
6268
6269
void GfxPath::offset(double dx, double dy)
6270
0
{
6271
0
    int i;
6272
6273
0
    for (i = 0; i < n; ++i) {
6274
0
        subpaths[i]->offset(dx, dy);
6275
0
    }
6276
0
}
6277
6278
//------------------------------------------------------------------------
6279
//
6280
//------------------------------------------------------------------------
6281
6282
#if USE_CMS
6283
6284
GfxLCMSProfilePtr GfxXYZ2DisplayTransforms::XYZProfile = nullptr;
6285
6286
GfxXYZ2DisplayTransforms::GfxXYZ2DisplayTransforms(const GfxLCMSProfilePtr &displayProfileA)
6287
{
6288
    if (!XYZProfile) {
6289
        // This is probably the one of the first invocations of lcms2, so we set the error handler
6290
        setCMSErrorHandler();
6291
6292
        XYZProfile = make_GfxLCMSProfilePtr(cmsCreateXYZProfile());
6293
    }
6294
6295
    displayProfile = displayProfileA;
6296
    if (displayProfile) {
6297
        cmsHTRANSFORM transform;
6298
        unsigned int nChannels;
6299
        unsigned int localDisplayPixelType;
6300
6301
        localDisplayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile.get()));
6302
        nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile.get()));
6303
        // create transform from XYZ
6304
        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, displayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_RELATIVE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) {
6305
            error(errSyntaxWarning, -1, "Can't create Lab transform");
6306
        } else {
6307
            XYZ2DisplayTransformRelCol = std::make_shared<GfxColorTransform>(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6308
        }
6309
6310
        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, displayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_ABSOLUTE_COLORIMETRIC, LCMS_FLAGS)) == nullptr) {
6311
            error(errSyntaxWarning, -1, "Can't create Lab transform");
6312
        } else {
6313
            XYZ2DisplayTransformAbsCol = std::make_shared<GfxColorTransform>(transform, INTENT_ABSOLUTE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6314
        }
6315
6316
        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, displayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_SATURATION, LCMS_FLAGS)) == nullptr) {
6317
            error(errSyntaxWarning, -1, "Can't create Lab transform");
6318
        } else {
6319
            XYZ2DisplayTransformSat = std::make_shared<GfxColorTransform>(transform, INTENT_SATURATION, PT_XYZ, localDisplayPixelType);
6320
        }
6321
6322
        if ((transform = cmsCreateTransform(XYZProfile.get(), TYPE_XYZ_DBL, displayProfile.get(), COLORSPACE_SH(localDisplayPixelType) | CHANNELS_SH(nChannels) | BYTES_SH(1), INTENT_PERCEPTUAL, LCMS_FLAGS)) == nullptr) {
6323
            error(errSyntaxWarning, -1, "Can't create Lab transform");
6324
        } else {
6325
            XYZ2DisplayTransformPerc = std::make_shared<GfxColorTransform>(transform, INTENT_PERCEPTUAL, PT_XYZ, localDisplayPixelType);
6326
        }
6327
    } else {
6328
        XYZ2DisplayTransformRelCol = nullptr;
6329
        XYZ2DisplayTransformAbsCol = nullptr;
6330
        XYZ2DisplayTransformSat = nullptr;
6331
        XYZ2DisplayTransformPerc = nullptr;
6332
    }
6333
}
6334
6335
#endif
6336
6337
//------------------------------------------------------------------------
6338
//
6339
//------------------------------------------------------------------------
6340
0
GfxState::ReusablePathIterator::ReusablePathIterator(GfxPath *pathA) : path(pathA)
6341
0
{
6342
0
    if (path->getNumSubpaths()) {
6343
0
        curSubPath = path->getSubpath(subPathOff);
6344
0
        numCoords = curSubPath->getNumPoints();
6345
0
    }
6346
0
}
6347
6348
bool GfxState::ReusablePathIterator::isEnd() const
6349
0
{
6350
0
    return coordOff >= numCoords;
6351
0
}
6352
6353
void GfxState::ReusablePathIterator::next()
6354
0
{
6355
0
    ++coordOff;
6356
0
    if (coordOff == numCoords) {
6357
0
        ++subPathOff;
6358
0
        if (subPathOff < path->getNumSubpaths()) {
6359
0
            coordOff = 0;
6360
0
            curSubPath = path->getSubpath(subPathOff);
6361
0
            numCoords = curSubPath->getNumPoints();
6362
0
        }
6363
0
    }
6364
0
}
6365
6366
void GfxState::ReusablePathIterator::setCoord(double x, double y)
6367
0
{
6368
0
    curSubPath->setX(coordOff, x);
6369
0
    curSubPath->setY(coordOff, y);
6370
0
}
6371
6372
void GfxState::ReusablePathIterator::reset()
6373
0
{
6374
0
    coordOff = 0;
6375
0
    subPathOff = 0;
6376
0
    curSubPath = path->getSubpath(0);
6377
0
    numCoords = curSubPath->getNumPoints();
6378
0
}
6379
6380
GfxState::GfxState(double hDPIA, double vDPIA, const PDFRectangle &pageBox, int rotateA, bool upsideDown)
6381
4.25k
{
6382
4.25k
    double kx, ky;
6383
6384
4.25k
    hDPI = hDPIA;
6385
4.25k
    vDPI = vDPIA;
6386
4.25k
    rotate = rotateA;
6387
4.25k
    px1 = pageBox.x1;
6388
4.25k
    py1 = pageBox.y1;
6389
4.25k
    px2 = pageBox.x2;
6390
4.25k
    py2 = pageBox.y2;
6391
4.25k
    kx = hDPI / 72.0;
6392
4.25k
    ky = vDPI / 72.0;
6393
4.25k
    if (rotate == 90) {
6394
0
        ctm[0] = 0;
6395
0
        ctm[1] = upsideDown ? ky : -ky;
6396
0
        ctm[2] = kx;
6397
0
        ctm[3] = 0;
6398
0
        ctm[4] = -kx * py1;
6399
0
        ctm[5] = ky * (upsideDown ? -px1 : px2);
6400
0
        pageWidth = kx * (py2 - py1);
6401
0
        pageHeight = ky * (px2 - px1);
6402
4.25k
    } else if (rotate == 180) {
6403
0
        ctm[0] = -kx;
6404
0
        ctm[1] = 0;
6405
0
        ctm[2] = 0;
6406
0
        ctm[3] = upsideDown ? ky : -ky;
6407
0
        ctm[4] = kx * px2;
6408
0
        ctm[5] = ky * (upsideDown ? -py1 : py2);
6409
0
        pageWidth = kx * (px2 - px1);
6410
0
        pageHeight = ky * (py2 - py1);
6411
4.25k
    } else if (rotate == 270) {
6412
0
        ctm[0] = 0;
6413
0
        ctm[1] = upsideDown ? -ky : ky;
6414
0
        ctm[2] = -kx;
6415
0
        ctm[3] = 0;
6416
0
        ctm[4] = kx * py2;
6417
0
        ctm[5] = ky * (upsideDown ? px2 : -px1);
6418
0
        pageWidth = kx * (py2 - py1);
6419
0
        pageHeight = ky * (px2 - px1);
6420
4.25k
    } else {
6421
4.25k
        ctm[0] = kx;
6422
4.25k
        ctm[1] = 0;
6423
4.25k
        ctm[2] = 0;
6424
4.25k
        ctm[3] = upsideDown ? -ky : ky;
6425
4.25k
        ctm[4] = -kx * px1;
6426
4.25k
        ctm[5] = ky * (upsideDown ? py2 : -py1);
6427
4.25k
        pageWidth = kx * (px2 - px1);
6428
4.25k
        pageHeight = ky * (py2 - py1);
6429
4.25k
    }
6430
6431
4.25k
    fillColorSpace = std::make_unique<GfxDeviceGrayColorSpace>();
6432
4.25k
    strokeColorSpace = std::make_unique<GfxDeviceGrayColorSpace>();
6433
4.25k
    fillColor.c[0] = 0;
6434
4.25k
    strokeColor.c[0] = 0;
6435
4.25k
    fillPattern = nullptr;
6436
4.25k
    strokePattern = nullptr;
6437
4.25k
    blendMode = gfxBlendNormal;
6438
4.25k
    fillOpacity = 1;
6439
4.25k
    strokeOpacity = 1;
6440
4.25k
    fillOverprint = false;
6441
4.25k
    strokeOverprint = false;
6442
4.25k
    overprintMode = 0;
6443
6444
4.25k
    lineWidth = 1;
6445
4.25k
    lineDashStart = 0;
6446
4.25k
    flatness = 1;
6447
4.25k
    lineJoin = GfxState::LineJoinMitre;
6448
4.25k
    lineCap = GfxState::LineCapButt;
6449
4.25k
    miterLimit = 10;
6450
4.25k
    strokeAdjust = false;
6451
4.25k
    alphaIsShape = false;
6452
4.25k
    textKnockout = false;
6453
6454
4.25k
    font = nullptr;
6455
4.25k
    fontSize = 0;
6456
4.25k
    textMat[0] = 1;
6457
4.25k
    textMat[1] = 0;
6458
4.25k
    textMat[2] = 0;
6459
4.25k
    textMat[3] = 1;
6460
4.25k
    textMat[4] = 0;
6461
4.25k
    textMat[5] = 0;
6462
4.25k
    charSpace = 0;
6463
4.25k
    wordSpace = 0;
6464
4.25k
    horizScaling = 1;
6465
4.25k
    leading = 0;
6466
4.25k
    rise = 0;
6467
4.25k
    render = 0;
6468
6469
4.25k
    path = new GfxPath();
6470
4.25k
    curX = curY = 0;
6471
4.25k
    curTextX = curTextY = 0;
6472
4.25k
    lineX = lineY = 0;
6473
6474
4.25k
    clipXMin = 0;
6475
4.25k
    clipYMin = 0;
6476
4.25k
    clipXMax = pageWidth;
6477
4.25k
    clipYMax = pageHeight;
6478
6479
4.25k
    renderingIntent[0] = 0;
6480
6481
4.25k
    saved = nullptr;
6482
6483
4.25k
    defaultGrayColorSpace = nullptr;
6484
4.25k
    defaultRGBColorSpace = nullptr;
6485
4.25k
    defaultCMYKColorSpace = nullptr;
6486
#if USE_CMS
6487
    localDisplayProfile = nullptr;
6488
    XYZ2DisplayTransforms = std::make_shared<GfxXYZ2DisplayTransforms>(nullptr);
6489
6490
    if (!sRGBProfile) {
6491
        // This is probably the one of the first invocations of lcms2, so we set the error handler
6492
        setCMSErrorHandler();
6493
6494
        sRGBProfile = make_GfxLCMSProfilePtr(cmsCreate_sRGBProfile());
6495
    }
6496
#endif
6497
4.25k
}
6498
6499
GfxState::~GfxState()
6500
51.7k
{
6501
51.7k
    delete path;
6502
51.7k
}
6503
6504
// Used for copy();
6505
GfxState::GfxState(const GfxState *state, bool copyPath)
6506
47.5k
{
6507
47.5k
    hDPI = state->hDPI;
6508
47.5k
    vDPI = state->vDPI;
6509
47.5k
    ctm = state->ctm;
6510
47.5k
    px1 = state->px1;
6511
47.5k
    py1 = state->py1;
6512
47.5k
    px2 = state->px2;
6513
47.5k
    py2 = state->py2;
6514
47.5k
    pageWidth = state->pageWidth;
6515
47.5k
    pageHeight = state->pageHeight;
6516
47.5k
    rotate = state->rotate;
6517
6518
47.5k
    if (state->fillColorSpace) {
6519
47.5k
        fillColorSpace = state->fillColorSpace->copy();
6520
47.5k
    }
6521
47.5k
    if (state->strokeColorSpace) {
6522
47.5k
        strokeColorSpace = state->strokeColorSpace->copy();
6523
47.5k
    }
6524
47.5k
    fillColor = state->fillColor;
6525
47.5k
    strokeColor = state->strokeColor;
6526
6527
47.5k
    if (state->fillPattern) {
6528
0
        fillPattern = state->fillPattern->copy();
6529
0
    }
6530
47.5k
    if (state->strokePattern) {
6531
0
        strokePattern = state->strokePattern->copy();
6532
0
    }
6533
47.5k
    blendMode = state->blendMode;
6534
47.5k
    fillOpacity = state->fillOpacity;
6535
47.5k
    strokeOpacity = state->strokeOpacity;
6536
47.5k
    fillOverprint = state->fillOverprint;
6537
47.5k
    strokeOverprint = state->strokeOverprint;
6538
47.5k
    overprintMode = state->overprintMode;
6539
47.5k
    transfer.reserve(state->transfer.size());
6540
47.5k
    for (const auto &element : state->transfer) {
6541
0
        transfer.push_back(element->copy());
6542
0
    }
6543
47.5k
    lineWidth = state->lineWidth;
6544
47.5k
    lineDash = state->lineDash;
6545
47.5k
    lineDashStart = state->lineDashStart;
6546
47.5k
    flatness = state->flatness;
6547
47.5k
    lineJoin = state->lineJoin;
6548
47.5k
    lineCap = state->lineCap;
6549
47.5k
    miterLimit = state->miterLimit;
6550
47.5k
    strokeAdjust = state->strokeAdjust;
6551
47.5k
    alphaIsShape = state->alphaIsShape;
6552
47.5k
    textKnockout = state->textKnockout;
6553
6554
47.5k
    font = state->font;
6555
47.5k
    fontSize = state->fontSize;
6556
47.5k
    textMat = state->textMat;
6557
47.5k
    charSpace = state->charSpace;
6558
47.5k
    wordSpace = state->wordSpace;
6559
47.5k
    horizScaling = state->horizScaling;
6560
47.5k
    leading = state->leading;
6561
47.5k
    rise = state->rise;
6562
47.5k
    render = state->render;
6563
6564
47.5k
    path = state->path;
6565
47.5k
    if (copyPath) {
6566
776
        path = state->path->copy();
6567
776
    }
6568
47.5k
    curX = state->curX;
6569
47.5k
    curY = state->curY;
6570
47.5k
    curTextX = state->curTextX;
6571
47.5k
    curTextY = state->curTextY;
6572
47.5k
    lineX = state->lineX;
6573
47.5k
    lineY = state->lineY;
6574
6575
47.5k
    clipXMin = state->clipXMin;
6576
47.5k
    clipYMin = state->clipYMin;
6577
47.5k
    clipXMax = state->clipXMax;
6578
47.5k
    clipYMax = state->clipYMax;
6579
47.5k
    memcpy(renderingIntent, state->renderingIntent, sizeof(renderingIntent));
6580
6581
47.5k
    saved = nullptr;
6582
#if USE_CMS
6583
    localDisplayProfile = state->localDisplayProfile;
6584
    XYZ2DisplayTransforms = state->XYZ2DisplayTransforms;
6585
#endif
6586
6587
47.5k
    if (state->defaultGrayColorSpace) {
6588
0
        defaultGrayColorSpace = state->defaultGrayColorSpace->copy();
6589
47.5k
    } else {
6590
47.5k
        defaultGrayColorSpace = nullptr;
6591
47.5k
    }
6592
47.5k
    if (state->defaultRGBColorSpace) {
6593
0
        defaultRGBColorSpace = state->defaultRGBColorSpace->copy();
6594
47.5k
    } else {
6595
47.5k
        defaultRGBColorSpace = nullptr;
6596
47.5k
    }
6597
47.5k
    if (state->defaultCMYKColorSpace) {
6598
0
        defaultCMYKColorSpace = state->defaultCMYKColorSpace->copy();
6599
47.5k
    } else {
6600
47.5k
        defaultCMYKColorSpace = nullptr;
6601
47.5k
    }
6602
47.5k
}
6603
6604
#if USE_CMS
6605
6606
GfxLCMSProfilePtr GfxState::sRGBProfile = nullptr;
6607
6608
void GfxState::setDisplayProfile(const GfxLCMSProfilePtr &localDisplayProfileA)
6609
{
6610
    localDisplayProfile = localDisplayProfileA;
6611
    XYZ2DisplayTransforms = std::make_shared<GfxXYZ2DisplayTransforms>(localDisplayProfile);
6612
}
6613
6614
void GfxState::setXYZ2DisplayTransforms(std::shared_ptr<GfxXYZ2DisplayTransforms> transforms)
6615
{
6616
    XYZ2DisplayTransforms = std::move(transforms);
6617
    localDisplayProfile = XYZ2DisplayTransforms->getDisplayProfile();
6618
}
6619
6620
std::shared_ptr<GfxColorTransform> GfxState::getXYZ2DisplayTransform()
6621
{
6622
    auto transform = XYZ2DisplayTransforms->getRelCol();
6623
    if (strcmp(renderingIntent, "AbsoluteColorimetric") == 0) {
6624
        transform = XYZ2DisplayTransforms->getAbsCol();
6625
    } else if (strcmp(renderingIntent, "Saturation") == 0) {
6626
        transform = XYZ2DisplayTransforms->getSat();
6627
    } else if (strcmp(renderingIntent, "Perceptual") == 0) {
6628
        transform = XYZ2DisplayTransforms->getPerc();
6629
    }
6630
    return transform;
6631
}
6632
6633
int GfxState::getCmsRenderingIntent() const
6634
{
6635
    const char *intent = getRenderingIntent();
6636
    int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
6637
    if (intent) {
6638
        if (strcmp(intent, "AbsoluteColorimetric") == 0) {
6639
            cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC;
6640
        } else if (strcmp(intent, "Saturation") == 0) {
6641
            cmsIntent = INTENT_SATURATION;
6642
        } else if (strcmp(intent, "Perceptual") == 0) {
6643
            cmsIntent = INTENT_PERCEPTUAL;
6644
        }
6645
    }
6646
    return cmsIntent;
6647
}
6648
6649
#endif
6650
6651
void GfxState::getUserClipBBox(double *xMin, double *yMin, double *xMax, double *yMax) const
6652
0
{
6653
0
    double ictm[6];
6654
0
    double xMin1, yMin1, xMax1, yMax1, tx, ty;
6655
6656
    // invert the CTM
6657
0
    const double det_denominator = (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
6658
0
    if (unlikely(det_denominator == 0)) {
6659
0
        *xMin = 0;
6660
0
        *yMin = 0;
6661
0
        *xMax = 0;
6662
0
        *yMax = 0;
6663
0
        return;
6664
0
    }
6665
0
    const double det = 1 / det_denominator;
6666
0
    ictm[0] = ctm[3] * det;
6667
0
    ictm[1] = -ctm[1] * det;
6668
0
    ictm[2] = -ctm[2] * det;
6669
0
    ictm[3] = ctm[0] * det;
6670
0
    ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
6671
0
    ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
6672
6673
    // transform all four corners of the clip bbox; find the min and max
6674
    // x and y values
6675
0
    xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
6676
0
    yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
6677
0
    tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
6678
0
    ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
6679
0
    if (tx < xMin1) {
6680
0
        xMin1 = tx;
6681
0
    } else if (tx > xMax1) {
6682
0
        xMax1 = tx;
6683
0
    }
6684
0
    if (ty < yMin1) {
6685
0
        yMin1 = ty;
6686
0
    } else if (ty > yMax1) {
6687
0
        yMax1 = ty;
6688
0
    }
6689
0
    tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
6690
0
    ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
6691
0
    if (tx < xMin1) {
6692
0
        xMin1 = tx;
6693
0
    } else if (tx > xMax1) {
6694
0
        xMax1 = tx;
6695
0
    }
6696
0
    if (ty < yMin1) {
6697
0
        yMin1 = ty;
6698
0
    } else if (ty > yMax1) {
6699
0
        yMax1 = ty;
6700
0
    }
6701
0
    tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
6702
0
    ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
6703
0
    if (tx < xMin1) {
6704
0
        xMin1 = tx;
6705
0
    } else if (tx > xMax1) {
6706
0
        xMax1 = tx;
6707
0
    }
6708
0
    if (ty < yMin1) {
6709
0
        yMin1 = ty;
6710
0
    } else if (ty > yMax1) {
6711
0
        yMax1 = ty;
6712
0
    }
6713
6714
0
    *xMin = xMin1;
6715
0
    *yMin = yMin1;
6716
0
    *xMax = xMax1;
6717
0
    *yMax = yMax1;
6718
0
}
6719
6720
double GfxState::transformWidth(double w) const
6721
0
{
6722
0
    double x, y;
6723
6724
0
    x = ctm[0] + ctm[2];
6725
0
    y = ctm[1] + ctm[3];
6726
0
    return w * sqrt(0.5 * (x * x + y * y));
6727
0
}
6728
6729
double GfxState::getTransformedFontSize() const
6730
73.9k
{
6731
73.9k
    double x1, y1, x2, y2;
6732
6733
73.9k
    x1 = textMat[2] * fontSize;
6734
73.9k
    y1 = textMat[3] * fontSize;
6735
73.9k
    x2 = ctm[0] * x1 + ctm[2] * y1;
6736
73.9k
    y2 = ctm[1] * x1 + ctm[3] * y1;
6737
73.9k
    return sqrt(x2 * x2 + y2 * y2);
6738
73.9k
}
6739
6740
void GfxState::getFontTransMat(double *m11, double *m12, double *m21, double *m22) const
6741
2.84M
{
6742
2.84M
    *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
6743
2.84M
    *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
6744
2.84M
    *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
6745
2.84M
    *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
6746
2.84M
}
6747
6748
void GfxState::setCTM(double a, double b, double c, double d, double e, double f)
6749
0
{
6750
0
    ctm[0] = a;
6751
0
    ctm[1] = b;
6752
0
    ctm[2] = c;
6753
0
    ctm[3] = d;
6754
0
    ctm[4] = e;
6755
0
    ctm[5] = f;
6756
0
}
6757
6758
void GfxState::concatCTM(double a, double b, double c, double d, double e, double f)
6759
776
{
6760
776
    double a1 = ctm[0];
6761
776
    double b1 = ctm[1];
6762
776
    double c1 = ctm[2];
6763
776
    double d1 = ctm[3];
6764
6765
776
    ctm[0] = a * a1 + b * c1;
6766
776
    ctm[1] = a * b1 + b * d1;
6767
776
    ctm[2] = c * a1 + d * c1;
6768
776
    ctm[3] = c * b1 + d * d1;
6769
776
    ctm[4] = e * a1 + f * c1 + ctm[4];
6770
776
    ctm[5] = e * b1 + f * d1 + ctm[5];
6771
776
}
6772
6773
void GfxState::shiftCTMAndClip(double tx, double ty)
6774
0
{
6775
0
    ctm[4] += tx;
6776
0
    ctm[5] += ty;
6777
0
    clipXMin += tx;
6778
0
    clipYMin += ty;
6779
0
    clipXMax += tx;
6780
0
    clipYMax += ty;
6781
0
}
6782
6783
void GfxState::setFillColorSpace(std::unique_ptr<GfxColorSpace> &&colorSpace)
6784
31.9k
{
6785
31.9k
    fillColorSpace = std::move(colorSpace);
6786
31.9k
}
6787
6788
void GfxState::setStrokeColorSpace(std::unique_ptr<GfxColorSpace> &&colorSpace)
6789
71
{
6790
71
    strokeColorSpace = std::move(colorSpace);
6791
71
}
6792
6793
void GfxState::setFillPattern(std::unique_ptr<GfxPattern> &&pattern)
6794
31.9k
{
6795
31.9k
    fillPattern = std::move(pattern);
6796
31.9k
}
6797
6798
void GfxState::setStrokePattern(std::unique_ptr<GfxPattern> &&pattern)
6799
102
{
6800
102
    strokePattern = std::move(pattern);
6801
102
}
6802
6803
void GfxState::setFont(std::shared_ptr<GfxFont> fontA, double fontSizeA)
6804
27.2k
{
6805
27.2k
    font = std::move(fontA);
6806
27.2k
    fontSize = fontSizeA;
6807
27.2k
}
6808
6809
void GfxState::setTransfer(std::vector<std::unique_ptr<Function>> funcs)
6810
0
{
6811
0
    transfer = std::move(funcs);
6812
0
}
6813
6814
void GfxState::setLineDash(std::vector<double> &&dash, double start)
6815
36
{
6816
36
    lineDash = dash;
6817
36
    lineDashStart = start;
6818
36
}
6819
6820
void GfxState::clearPath()
6821
16.3k
{
6822
16.3k
    delete path;
6823
16.3k
    path = new GfxPath();
6824
16.3k
}
6825
6826
void GfxState::clip()
6827
9.60k
{
6828
9.60k
    double xMin, yMin, xMax, yMax, x, y;
6829
9.60k
    GfxSubpath *subpath;
6830
9.60k
    int i, j;
6831
6832
9.60k
    xMin = xMax = yMin = yMax = 0; // make gcc happy
6833
19.8k
    for (i = 0; i < path->getNumSubpaths(); ++i) {
6834
10.2k
        subpath = path->getSubpath(i);
6835
61.4k
        for (j = 0; j < subpath->getNumPoints(); ++j) {
6836
51.2k
            transform(subpath->getX(j), subpath->getY(j), &x, &y);
6837
51.2k
            if (i == 0 && j == 0) {
6838
9.60k
                xMin = xMax = x;
6839
9.60k
                yMin = yMax = y;
6840
41.6k
            } else {
6841
41.6k
                if (x < xMin) {
6842
137
                    xMin = x;
6843
41.4k
                } else if (x > xMax) {
6844
9.69k
                    xMax = x;
6845
9.69k
                }
6846
41.6k
                if (y < yMin) {
6847
9.61k
                    yMin = y;
6848
32.0k
                } else if (y > yMax) {
6849
145
                    yMax = y;
6850
145
                }
6851
41.6k
            }
6852
51.2k
        }
6853
10.2k
    }
6854
9.60k
    if (xMin > clipXMin) {
6855
145
        clipXMin = xMin;
6856
145
    }
6857
9.60k
    if (yMin > clipYMin) {
6858
3.50k
        clipYMin = yMin;
6859
3.50k
    }
6860
9.60k
    if (xMax < clipXMax) {
6861
2.88k
        clipXMax = xMax;
6862
2.88k
    }
6863
9.60k
    if (yMax < clipYMax) {
6864
3.57k
        clipYMax = yMax;
6865
3.57k
    }
6866
9.60k
}
6867
6868
void GfxState::clipToStrokePath()
6869
0
{
6870
0
    double xMin, yMin, xMax, yMax, x, y, t0, t1;
6871
0
    GfxSubpath *subpath;
6872
0
    int i, j;
6873
6874
0
    xMin = xMax = yMin = yMax = 0; // make gcc happy
6875
0
    for (i = 0; i < path->getNumSubpaths(); ++i) {
6876
0
        subpath = path->getSubpath(i);
6877
0
        for (j = 0; j < subpath->getNumPoints(); ++j) {
6878
0
            transform(subpath->getX(j), subpath->getY(j), &x, &y);
6879
0
            if (i == 0 && j == 0) {
6880
0
                xMin = xMax = x;
6881
0
                yMin = yMax = y;
6882
0
            } else {
6883
0
                if (x < xMin) {
6884
0
                    xMin = x;
6885
0
                } else if (x > xMax) {
6886
0
                    xMax = x;
6887
0
                }
6888
0
                if (y < yMin) {
6889
0
                    yMin = y;
6890
0
                } else if (y > yMax) {
6891
0
                    yMax = y;
6892
0
                }
6893
0
            }
6894
0
        }
6895
0
    }
6896
6897
    // allow for the line width
6898
    //~ miter joins can extend farther than this
6899
0
    t0 = fabs(ctm[0]);
6900
0
    t1 = fabs(ctm[2]);
6901
0
    if (t0 > t1) {
6902
0
        xMin -= 0.5 * lineWidth * t0;
6903
0
        xMax += 0.5 * lineWidth * t0;
6904
0
    } else {
6905
0
        xMin -= 0.5 * lineWidth * t1;
6906
0
        xMax += 0.5 * lineWidth * t1;
6907
0
    }
6908
0
    t0 = fabs(ctm[0]);
6909
0
    t1 = fabs(ctm[3]);
6910
0
    if (t0 > t1) {
6911
0
        yMin -= 0.5 * lineWidth * t0;
6912
0
        yMax += 0.5 * lineWidth * t0;
6913
0
    } else {
6914
0
        yMin -= 0.5 * lineWidth * t1;
6915
0
        yMax += 0.5 * lineWidth * t1;
6916
0
    }
6917
6918
0
    if (xMin > clipXMin) {
6919
0
        clipXMin = xMin;
6920
0
    }
6921
0
    if (yMin > clipYMin) {
6922
0
        clipYMin = yMin;
6923
0
    }
6924
0
    if (xMax < clipXMax) {
6925
0
        clipXMax = xMax;
6926
0
    }
6927
0
    if (yMax < clipYMax) {
6928
0
        clipYMax = yMax;
6929
0
    }
6930
0
}
6931
6932
void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax)
6933
0
{
6934
0
    double x, y, xMin1, yMin1, xMax1, yMax1;
6935
6936
0
    transform(xMin, yMin, &x, &y);
6937
0
    xMin1 = xMax1 = x;
6938
0
    yMin1 = yMax1 = y;
6939
0
    transform(xMax, yMin, &x, &y);
6940
0
    if (x < xMin1) {
6941
0
        xMin1 = x;
6942
0
    } else if (x > xMax1) {
6943
0
        xMax1 = x;
6944
0
    }
6945
0
    if (y < yMin1) {
6946
0
        yMin1 = y;
6947
0
    } else if (y > yMax1) {
6948
0
        yMax1 = y;
6949
0
    }
6950
0
    transform(xMax, yMax, &x, &y);
6951
0
    if (x < xMin1) {
6952
0
        xMin1 = x;
6953
0
    } else if (x > xMax1) {
6954
0
        xMax1 = x;
6955
0
    }
6956
0
    if (y < yMin1) {
6957
0
        yMin1 = y;
6958
0
    } else if (y > yMax1) {
6959
0
        yMax1 = y;
6960
0
    }
6961
0
    transform(xMin, yMax, &x, &y);
6962
0
    if (x < xMin1) {
6963
0
        xMin1 = x;
6964
0
    } else if (x > xMax1) {
6965
0
        xMax1 = x;
6966
0
    }
6967
0
    if (y < yMin1) {
6968
0
        yMin1 = y;
6969
0
    } else if (y > yMax1) {
6970
0
        yMax1 = y;
6971
0
    }
6972
6973
0
    if (xMin1 > clipXMin) {
6974
0
        clipXMin = xMin1;
6975
0
    }
6976
0
    if (yMin1 > clipYMin) {
6977
0
        clipYMin = yMin1;
6978
0
    }
6979
0
    if (xMax1 < clipXMax) {
6980
0
        clipXMax = xMax1;
6981
0
    }
6982
0
    if (yMax1 < clipYMax) {
6983
0
        clipYMax = yMax1;
6984
0
    }
6985
0
}
6986
6987
void GfxState::textShift(double tx, double ty)
6988
117k
{
6989
117k
    double dx, dy;
6990
6991
117k
    textTransformDelta(tx, ty, &dx, &dy);
6992
117k
    curTextX += dx;
6993
117k
    curTextY += dy;
6994
117k
}
6995
6996
void GfxState::textShiftWithUserCoords(double dx, double dy)
6997
2.41M
{
6998
2.41M
    curTextX += dx;
6999
2.41M
    curTextY += dy;
7000
2.41M
}
7001
7002
GfxState *GfxState::save()
7003
46.7k
{
7004
46.7k
    GfxState *newState;
7005
7006
46.7k
    newState = copy();
7007
46.7k
    newState->saved = this;
7008
46.7k
    return newState;
7009
46.7k
}
7010
7011
GfxState *GfxState::restore()
7012
46.7k
{
7013
46.7k
    GfxState *oldState;
7014
7015
46.7k
    if (saved) {
7016
46.7k
        oldState = saved;
7017
7018
        // these attributes aren't saved/restored by the q/Q operators
7019
46.7k
        oldState->path = path;
7020
46.7k
        oldState->curX = curX;
7021
46.7k
        oldState->curY = curY;
7022
46.7k
        oldState->curTextX = curTextX;
7023
46.7k
        oldState->curTextY = curTextY;
7024
46.7k
        oldState->lineX = lineX;
7025
46.7k
        oldState->lineY = lineY;
7026
7027
46.7k
        path = nullptr;
7028
46.7k
        saved = nullptr;
7029
46.7k
        delete this;
7030
7031
46.7k
    } else {
7032
0
        oldState = this;
7033
0
    }
7034
7035
46.7k
    return oldState;
7036
46.7k
}
7037
7038
bool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode)
7039
0
{
7040
0
    int i, j;
7041
7042
0
    if (obj->isName()) {
7043
0
        for (i = 0; i < nGfxBlendModeNames; ++i) {
7044
0
            if (obj->getNameString() == gfxBlendModeNames[i].name) {
7045
0
                *mode = gfxBlendModeNames[i].mode;
7046
0
                return true;
7047
0
            }
7048
0
        }
7049
0
        return false;
7050
0
    }
7051
0
    if (obj->isArray()) {
7052
0
        for (i = 0; i < obj->arrayGetLength(); ++i) {
7053
0
            Object obj2 = obj->arrayGet(i);
7054
0
            if (!obj2.isName()) {
7055
0
                return false;
7056
0
            }
7057
0
            for (j = 0; j < nGfxBlendModeNames; ++j) {
7058
0
                if (obj2.getNameString() == gfxBlendModeNames[j].name) {
7059
0
                    *mode = gfxBlendModeNames[j].mode;
7060
0
                    return true;
7061
0
                }
7062
0
            }
7063
0
        }
7064
0
        *mode = gfxBlendNormal;
7065
0
        return true;
7066
0
    }
7067
0
    return false;
7068
0
}