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