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