w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

GfxState.cc
Go to the documentation of this file.
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 
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 
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 
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 
110 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
122 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;
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 
171 static void lcmsprofiledeleter(void *profile)
172 {
174 }
175 
177 {
178  if (profile == nullptr) {
179  return GfxLCMSProfilePtr();
180  }
182 }
183 
184 void GfxColorTransform::doTransform(void *in, void *out, unsigned int size)
185 {
187 }
188 
189 // transformA should be a cmsHTRANSFORM
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 
199 {
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 
214 {
215  overprintMask = 0x0f;
216  mapping = nullptr;
217 }
218 
220 
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")) {
308  } else if (obj1.isName("CalRGB")) {
310  } else if (obj1.isName("Lab")) {
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 
369 void GfxColorSpace::createMapping(std::vector<GfxSeparationColorSpace *> *separationList, int maxSepComps)
370 {
371  return;
372 }
373 
374 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 
385 {
386  return nGfxColorSpaceModes;
387 }
388 
390 {
391  return gfxColorSpaceModeNames[idx];
392 }
393 
394 #ifdef USE_CMS
395 
396 static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text)
397 {
398  error(errSyntaxWarning, -1, "{0:s}", text);
399 }
400 
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 
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 
526 
528 
530 {
531  return new GfxDeviceGrayColorSpace();
532 }
533 
535 {
536  *gray = clip01(color->c[0]);
537 }
538 
539 void GfxDeviceGrayColorSpace::getGrayLine(unsigned char *in, unsigned char *out, int length)
540 {
541  memcpy(out, in, length);
542 }
543 
545 {
546  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
547 }
548 
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 
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 
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 
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 
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 
597 {
598  cmyk->c = cmyk->m = cmyk->y = 0;
599  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
600 }
601 
603 {
604  clearGfxColor(deviceN);
605  deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]);
606 }
607 
609 {
610  color->c[0] = 0;
611 }
612 
613 //------------------------------------------------------------------------
614 // GfxCalGrayColorSpace
615 //------------------------------------------------------------------------
616 
618 {
619  whiteX = whiteY = whiteZ = 1;
620  blackX = blackY = blackZ = 0;
621  gamma = 1;
622 }
623 
625 
627 {
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.
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 
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 
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 
717 {
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)
750 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 
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);
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 
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 
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
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;
813 }
814 
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);
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 
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 
868 {
869  color->c[0] = 0;
870 }
871 
872 //------------------------------------------------------------------------
873 // GfxDeviceRGBColorSpace
874 //------------------------------------------------------------------------
875 
877 
879 
881 {
882  return new GfxDeviceRGBColorSpace();
883 }
884 
886 {
887  *gray = clip01((GfxColorComp)(0.3 * color->c[0] + 0.59 * color->c[1] + 0.11 * color->c[2] + 0.5));
888 }
889 
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 
900 {
901  rgb->r = clip01(color->c[0]);
902  rgb->g = clip01(color->c[1]);
903  rgb->b = clip01(color->c[2]);
904 }
905 
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 
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 
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 
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 
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 
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 
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 
1013 {
1014  color->c[0] = 0;
1015  color->c[1] = 0;
1016  color->c[2] = 0;
1017 }
1018 
1019 //------------------------------------------------------------------------
1020 // GfxCalRGBColorSpace
1021 //------------------------------------------------------------------------
1022 
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 
1040 
1042 {
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 
1066 {
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
1111 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 
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);
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 
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 
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
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;
1178 }
1179 
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);
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 
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 
1233 {
1234  color->c[0] = 0;
1235  color->c[1] = 0;
1236  color->c[2] = 0;
1237 }
1238 
1239 //------------------------------------------------------------------------
1240 // GfxDeviceCMYKColorSpace
1241 //------------------------------------------------------------------------
1242 
1244 
1246 
1248 {
1249  return new GfxDeviceCMYKColorSpace();
1250 }
1251 
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 
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 
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 
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++) {
1295  *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b));
1296  }
1297 }
1298 
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++) {
1305  *out++ = dblToByte(clip01(r));
1306  *out++ = dblToByte(clip01(g));
1307  *out++ = dblToByte(clip01(b));
1308  }
1309 }
1310 
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++) {
1317  *out++ = dblToByte(clip01(r));
1318  *out++ = dblToByte(clip01(g));
1319  *out++ = dblToByte(clip01(b));
1320  *out++ = 255;
1321  }
1322 }
1323 
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 
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 
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 
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 
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 
1377 {
1378  whiteX = whiteY = whiteZ = 1;
1379  blackX = blackY = blackZ = 0;
1380  aMin = bMin = -100;
1381  aMax = bMax = 100;
1382 }
1383 
1385 
1387 {
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 
1408 {
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 
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]);
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)
1476 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 
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 
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 
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
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;
1559 }
1560 
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]);
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 
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 
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 
1628 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 
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 
1657 {
1658  delete alt;
1659 #ifdef USE_CMS
1660  if (psCSA)
1661  gfree(psCSA);
1662 #endif
1663 }
1664 
1666 {
1668  int i;
1669 
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 
1684 {
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);
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
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 
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 
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 
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 
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 
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 
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 
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 {
2129  }
2130 #else
2132 #endif
2133 }
2134 
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 
2207 {
2208 #ifdef USE_CMS
2209  return lineTransform != nullptr || alt->useGetRGBLine();
2210 #else
2211  return alt->useGetRGBLine();
2212 #endif
2213 }
2214 
2216 {
2217 #ifdef USE_CMS
2218  return lineTransform != nullptr || alt->useGetCMYKLine();
2219 #else
2220  return alt->useGetCMYKLine();
2221 #endif
2222 }
2223 
2225 {
2226 #ifdef USE_CMS
2227  return lineTransform != nullptr || alt->useGetDeviceNLine();
2228 #else
2229  return alt->useGetDeviceNLine();
2230 #endif
2231 }
2232 
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 
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 
2259 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
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 
2330 {
2331  base = baseA;
2332  indexHigh = indexHighA;
2333  lookup = (unsigned char *)gmallocn((indexHigh + 1) * base->getNComps(), sizeof(unsigned char));
2335 }
2336 
2338 {
2339  delete base;
2340  gfree(lookup);
2341 }
2342 
2344 {
2346 
2348  memcpy(cs->lookup, lookup, (indexHigh + 1) * base->getNComps() * sizeof(unsigned char));
2349  return cs;
2350 }
2351 
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 
2424 {
2425  unsigned char *p;
2427  int n, i;
2428 
2429  n = base->getNComps();
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 
2446 {
2447  GfxColor color2;
2448 
2449  base->getGray(mapColorToBase(color, &color2), gray);
2450 }
2451 
2453 {
2454  GfxColor color2;
2455 
2456  base->getRGB(mapColorToBase(color, &color2), rgb);
2457 }
2458 
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 
2471 
2472  gfree(line);
2473 }
2474 
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 
2487 
2488  gfree(line);
2489 }
2490 
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 
2503 
2504  gfree(line);
2505 }
2506 
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 
2519 
2520  gfree(line);
2521 }
2522 
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 
2535 
2536  gfree(line);
2537 }
2538 
2540 {
2541  GfxColor color2;
2542 
2543  base->getCMYK(mapColorToBase(color, &color2), cmyk);
2544 }
2545 
2547 {
2548  GfxColor color2;
2549 
2550  base->getDeviceN(mapColorToBase(color, &color2), deviceN);
2551 }
2552 
2554 {
2555  color->c[0] = 0;
2556 }
2557 
2558 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 
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 
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 
2598 {
2599  delete name;
2600  delete alt;
2601  delete func;
2602  if (mapping != nullptr)
2603  gfree(mapping);
2604 }
2605 
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
2618 {
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 
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 
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 
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 
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 
2761 {
2762  color->c[0] = gfxColorComp1;
2763 }
2764 
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 
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 
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 
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 
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
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()) {
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 
2969 {
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 
2985 {
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 
3001 {
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 
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 
3035 {
3036  int i;
3037 
3038  for (i = 0; i < nComps; ++i) {
3039  color->c[i] = gfxColorComp1;
3040  }
3041 }
3042 
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 
3134 {
3135  under = underA;
3136 }
3137 
3139 {
3140  if (under) {
3141  delete under;
3142  }
3143 }
3144 
3146 {
3147  return new GfxPatternColorSpace(under ? under->copy() : nullptr);
3148 }
3149 
3151 {
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 
3173 {
3174  *gray = 0;
3175 }
3176 
3178 {
3179  rgb->r = rgb->g = rgb->b = 0;
3180 }
3181 
3183 {
3184  cmyk->c = cmyk->m = cmyk->y = 0;
3185  cmyk->k = 1;
3186 }
3187 
3189 {
3190  clearGfxColor(deviceN);
3191  deviceN->c[3] = 1;
3192 }
3193 
3195 {
3196  color->c[0] = 0;
3197 }
3198 
3199 //------------------------------------------------------------------------
3200 // Pattern
3201 //------------------------------------------------------------------------
3202 
3203 GfxPattern::GfxPattern(int typeA, int patternRefNumA) : type(typeA), patternRefNum(patternRefNumA) { }
3204 
3206 
3208 {
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) {
3222  } else if (obj1.isInt() && obj1.getInt() == 2) {
3224  }
3225  return pattern;
3226 }
3227 
3228 //------------------------------------------------------------------------
3229 // GfxTilingPattern
3230 //------------------------------------------------------------------------
3231 
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 
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 
3331 
3333 {
3335 }
3336 
3337 //------------------------------------------------------------------------
3338 // GfxShadingPattern
3339 //------------------------------------------------------------------------
3340 
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 
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 
3390 {
3391  delete shading;
3392 }
3393 
3395 {
3397 }
3398 
3399 //------------------------------------------------------------------------
3400 // GfxShading
3401 //------------------------------------------------------------------------
3402 
3403 GfxShading::GfxShading(int typeA)
3404 {
3405  type = typeA;
3406  colorSpace = nullptr;
3407 }
3408 
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 
3427 {
3428  if (colorSpace) {
3429  delete colorSpace;
3430  }
3431 }
3432 
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 
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());
3530  }
3531  } else {
3532  error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
3533  }
3534  }
3535 
3537  hasBBox = false;
3538  obj1 = dict->lookup("BBox");
3539  if (obj1.isArray()) {
3540  if (obj1.arrayGetLength() == 4) {
3541  hasBBox = true;
3542  bbox_xMin = obj1.arrayGet(0).getNum(&hasBBox);
3543  bbox_yMin = obj1.arrayGet(1).getNum(&hasBBox);
3544  bbox_xMax = obj1.arrayGet(2).getNum(&hasBBox);
3545  bbox_yMax = obj1.arrayGet(3).getNum(&hasBBox);
3546  if (!hasBBox) {
3547  error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)");
3548  }
3549  } else {
3550  error(errSyntaxWarning, -1, "Bad BBox in shading dictionary");
3551  }
3552  }
3553 
3554  return true;
3555 }
3556 
3557 //------------------------------------------------------------------------
3558 // GfxFunctionShading
3559 //------------------------------------------------------------------------
3560 
3561 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))
3562 {
3563  x0 = x0A;
3564  y0 = y0A;
3565  x1 = x1A;
3566  y1 = y1A;
3567  for (int i = 0; i < 6; ++i) {
3568  matrix[i] = matrixA[i];
3569  }
3570 }
3571 
3573 {
3574  x0 = shading->x0;
3575  y0 = shading->y0;
3576  x1 = shading->x1;
3577  y1 = shading->y1;
3578  for (int i = 0; i < 6; ++i) {
3579  matrix[i] = shading->matrix[i];
3580  }
3581  for (const auto &f : shading->funcs) {
3582  funcs.emplace_back(f->copy());
3583  }
3584 }
3585 
3587 
3589 {
3590  GfxFunctionShading *shading;
3591  double x0A, y0A, x1A, y1A;
3592  double matrixA[6];
3593  std::vector<std::unique_ptr<Function>> funcsA;
3594  Object obj1;
3595  int i;
3596 
3597  x0A = y0A = 0;
3598  x1A = y1A = 1;
3599  obj1 = dict->lookup("Domain");
3600  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3601  bool decodeOk = true;
3602  x0A = obj1.arrayGet(0).getNum(&decodeOk);
3603  x1A = obj1.arrayGet(1).getNum(&decodeOk);
3604  y0A = obj1.arrayGet(2).getNum(&decodeOk);
3605  y1A = obj1.arrayGet(3).getNum(&decodeOk);
3606 
3607  if (!decodeOk) {
3608  error(errSyntaxWarning, -1, "Invalid Domain array in function shading dictionary");
3609  return nullptr;
3610  }
3611  }
3612 
3613  matrixA[0] = 1;
3614  matrixA[1] = 0;
3615  matrixA[2] = 0;
3616  matrixA[3] = 1;
3617  matrixA[4] = 0;
3618  matrixA[5] = 0;
3619  obj1 = dict->lookup("Matrix");
3620  if (obj1.isArray() && obj1.arrayGetLength() == 6) {
3621  bool decodeOk = true;
3622  matrixA[0] = obj1.arrayGet(0).getNum(&decodeOk);
3623  matrixA[1] = obj1.arrayGet(1).getNum(&decodeOk);
3624  matrixA[2] = obj1.arrayGet(2).getNum(&decodeOk);
3625  matrixA[3] = obj1.arrayGet(3).getNum(&decodeOk);
3626  matrixA[4] = obj1.arrayGet(4).getNum(&decodeOk);
3627  matrixA[5] = obj1.arrayGet(5).getNum(&decodeOk);
3628 
3629  if (!decodeOk) {
3630  error(errSyntaxWarning, -1, "Invalid Matrix array in function shading dictionary");
3631  return nullptr;
3632  }
3633  }
3634 
3635  obj1 = dict->lookup("Function");
3636  if (obj1.isArray()) {
3637  const int nFuncsA = obj1.arrayGetLength();
3638  if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) {
3639  error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3640  return nullptr;
3641  }
3642  for (i = 0; i < nFuncsA; ++i) {
3643  Object obj2 = obj1.arrayGet(i);
3644  Function *f = Function::parse(&obj2);
3645  if (!f) {
3646  return nullptr;
3647  }
3648  funcsA.emplace_back(f);
3649  }
3650  } else {
3651  Function *f = Function::parse(&obj1);
3652  if (!f) {
3653  return nullptr;
3654  }
3655  funcsA.emplace_back(f);
3656  }
3657 
3658  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA, std::move(funcsA));
3659  if (!shading->init(res, dict, out, state)) {
3660  delete shading;
3661  return nullptr;
3662  }
3663  return shading;
3664 }
3665 
3667 {
3668  const bool parentInit = GfxShading::init(res, dict, out, state);
3669  if (!parentInit) {
3670  return false;
3671  }
3672 
3673  // funcs needs to be one of the two:
3674  // * One function 2-in -> nComps-out
3675  // * nComps functions 2-in -> 1-out
3676  const int nComps = colorSpace->getNComps();
3677  const int nFuncs = funcs.size();
3678  if (nFuncs == 1) {
3679  if (funcs[0]->getInputSize() != 2) {
3680  error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2");
3681  return false;
3682  }
3683  if (funcs[0]->getOutputSize() != nComps) {
3684  error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size");
3685  return false;
3686  }
3687  } else if (nFuncs == nComps) {
3688  for (const std::unique_ptr<Function> &f : funcs) {
3689  if (f->getInputSize() != 2) {
3690  error(errSyntaxWarning, -1, "GfxFunctionShading: function with input size != 2");
3691  return false;
3692  }
3693  if (f->getOutputSize() != 1) {
3694  error(errSyntaxWarning, -1, "GfxFunctionShading: function with wrong output size");
3695  return false;
3696  }
3697  }
3698  } else {
3699  return false;
3700  }
3701 
3702  return true;
3703 }
3704 
3706 {
3707  return new GfxFunctionShading(this);
3708 }
3709 
3710 void GfxFunctionShading::getColor(double x, double y, GfxColor *color) const
3711 {
3712  double in[2], out[gfxColorMaxComps];
3713 
3714  // NB: there can be one function with n outputs or n functions with
3715  // one output each (where n = number of color components)
3716  for (double &i : out) {
3717  i = 0;
3718  }
3719  in[0] = x;
3720  in[1] = y;
3721  for (int i = 0; i < getNFuncs(); ++i) {
3722  funcs[i]->transform(in, &out[i]);
3723  }
3724  for (int i = 0; i < gfxColorMaxComps; ++i) {
3725  color->c[i] = dblToCol(out[i]);
3726  }
3727 }
3728 
3729 //------------------------------------------------------------------------
3730 // GfxUnivariateShading
3731 //------------------------------------------------------------------------
3732 
3733 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))
3734 {
3735  t0 = t0A;
3736  t1 = t1A;
3737  extend0 = extend0A;
3738  extend1 = extend1A;
3739 
3740  cacheSize = 0;
3741  lastMatch = 0;
3742  cacheBounds = nullptr;
3743  cacheCoeff = nullptr;
3744  cacheValues = nullptr;
3745 }
3746 
3748 {
3749  t0 = shading->t0;
3750  t1 = shading->t1;
3751  for (const auto &f : shading->funcs) {
3752  funcs.emplace_back(f->copy());
3753  }
3754  extend0 = shading->extend0;
3755  extend1 = shading->extend1;
3756 
3757  cacheSize = 0;
3758  lastMatch = 0;
3759  cacheBounds = nullptr;
3760  cacheCoeff = nullptr;
3761  cacheValues = nullptr;
3762 }
3763 
3765 {
3766  gfree(cacheBounds);
3767 }
3768 
3770 {
3771  double out[gfxColorMaxComps];
3772 
3773  // NB: there can be one function with n outputs or n functions with
3774  // one output each (where n = number of color components)
3775  const int nComps = getNFuncs() * funcs[0]->getOutputSize();
3776 
3777  if (cacheSize > 0) {
3778  double x, ix, *l, *u, *upper;
3779 
3780  if (cacheBounds[lastMatch - 1] >= t) {
3783  lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3784  } else if (cacheBounds[lastMatch] < t) {
3787  lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3788  }
3789 
3790  x = (t - cacheBounds[lastMatch - 1]) * cacheCoeff[lastMatch];
3791  ix = 1.0 - x;
3792  u = cacheValues + lastMatch * nComps;
3793  l = u - nComps;
3794 
3795  for (int i = 0; i < nComps; ++i) {
3796  out[i] = ix * l[i] + x * u[i];
3797  }
3798  } else {
3799  for (int i = 0; i < nComps; ++i) {
3800  out[i] = 0;
3801  }
3802  for (int i = 0; i < getNFuncs(); ++i) {
3803  funcs[i]->transform(&t, &out[i]);
3804  }
3805  }
3806 
3807  for (int i = 0; i < nComps; ++i) {
3808  color->c[i] = dblToCol(out[i]);
3809  }
3810  return nComps;
3811 }
3812 
3813 void GfxUnivariateShading::setupCache(const Matrix *ctm, double xMin, double yMin, double xMax, double yMax)
3814 {
3815  double sMin, sMax, tMin, tMax, upperBound;
3816  int i, j, nComps, maxSize;
3817 
3818  gfree(cacheBounds);
3819  cacheBounds = nullptr;
3820  cacheSize = 0;
3821 
3822  if (unlikely(getNFuncs() < 1))
3823  return;
3824 
3825  // NB: there can be one function with n outputs or n functions with
3826  // one output each (where n = number of color components)
3827  nComps = getNFuncs() * funcs[0]->getOutputSize();
3828 
3829  getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
3830  upperBound = ctm->norm() * getDistance(sMin, sMax);
3831  maxSize = ceil(upperBound);
3832  maxSize = std::max<int>(maxSize, 2);
3833 
3834  {
3835  double x[4], y[4];
3836 
3837  ctm->transform(xMin, yMin, &x[0], &y[0]);
3838  ctm->transform(xMax, yMin, &x[1], &y[1]);
3839  ctm->transform(xMin, yMax, &x[2], &y[2]);
3840  ctm->transform(xMax, yMax, &x[3], &y[3]);
3841 
3842  xMin = xMax = x[0];
3843  yMin = yMax = y[0];
3844  for (i = 1; i < 4; i++) {
3845  xMin = std::min<double>(xMin, x[i]);
3846  yMin = std::min<double>(yMin, y[i]);
3847  xMax = std::max<double>(xMax, x[i]);
3848  yMax = std::max<double>(yMax, y[i]);
3849  }
3850  }
3851 
3852  if (maxSize > (xMax - xMin) * (yMax - yMin)) {
3853  return;
3854  }
3855 
3856  if (t0 < t1) {
3857  tMin = t0 + sMin * (t1 - t0);
3858  tMax = t0 + sMax * (t1 - t0);
3859  } else {
3860  tMin = t0 + sMax * (t1 - t0);
3861  tMax = t0 + sMin * (t1 - t0);
3862  }
3863 
3864  cacheBounds = (double *)gmallocn_checkoverflow(maxSize, sizeof(double) * (nComps + 2));
3865  if (unlikely(!cacheBounds))
3866  return;
3869 
3870  if (cacheSize != 0) {
3871  for (j = 0; j < cacheSize; ++j) {
3872  cacheCoeff[j] = 1 / (cacheBounds[j + 1] - cacheBounds[j]);
3873  }
3874  } else if (tMax != tMin) {
3875  double step = (tMax - tMin) / (maxSize - 1);
3876  double coeff = (maxSize - 1) / (tMax - tMin);
3877 
3878  cacheSize = maxSize;
3879 
3880  for (j = 0; j < cacheSize; ++j) {
3881  cacheBounds[j] = tMin + j * step;
3882  cacheCoeff[j] = coeff;
3883 
3884  for (i = 0; i < nComps; ++i) {
3885  cacheValues[j * nComps + i] = 0;
3886  }
3887  for (i = 0; i < getNFuncs(); ++i) {
3888  funcs[i]->transform(&cacheBounds[j], &cacheValues[j * nComps + i]);
3889  }
3890  }
3891  }
3892 
3893  lastMatch = 1;
3894 }
3895 
3897 {
3898  const bool parentInit = GfxShading::init(res, dict, out, state);
3899  if (!parentInit) {
3900  return false;
3901  }
3902 
3903  // funcs needs to be one of the two:
3904  // * One function 1-in -> nComps-out
3905  // * nComps functions 1-in -> 1-out
3906  const int nComps = colorSpace->getNComps();
3907  const int nFuncs = funcs.size();
3908  if (nFuncs == 1) {
3909  if (funcs[0]->getInputSize() != 1) {
3910  error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2");
3911  return false;
3912  }
3913  if (funcs[0]->getOutputSize() != nComps) {
3914  error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size");
3915  return false;
3916  }
3917  } else if (nFuncs == nComps) {
3918  for (const std::unique_ptr<Function> &f : funcs) {
3919  if (f->getInputSize() != 1) {
3920  error(errSyntaxWarning, -1, "GfxUnivariateShading: function with input size != 2");
3921  return false;
3922  }
3923  if (f->getOutputSize() != 1) {
3924  error(errSyntaxWarning, -1, "GfxUnivariateShading: function with wrong output size");
3925  return false;
3926  }
3927  }
3928  } else {
3929  return false;
3930  }
3931 
3932  return true;
3933 }
3934 
3935 //------------------------------------------------------------------------
3936 // GfxAxialShading
3937 //------------------------------------------------------------------------
3938 
3939 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)
3940  : GfxUnivariateShading(2, t0A, t1A, std::move(funcsA), extend0A, extend1A)
3941 {
3942  x0 = x0A;
3943  y0 = y0A;
3944  x1 = x1A;
3945  y1 = y1A;
3946 }
3947 
3949 {
3950  x0 = shading->x0;
3951  y0 = shading->y0;
3952  x1 = shading->x1;
3953  y1 = shading->y1;
3954 }
3955 
3957 
3959 {
3960  GfxAxialShading *shading;
3961  double x0A, y0A, x1A, y1A;
3962  double t0A, t1A;
3963  std::vector<std::unique_ptr<Function>> funcsA;
3964  bool extend0A, extend1A;
3965  Object obj1;
3966 
3967  x0A = y0A = x1A = y1A = 0;
3968  obj1 = dict->lookup("Coords");
3969  if (obj1.isArray() && obj1.arrayGetLength() == 4) {
3970  x0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
3971  y0A = obj1.arrayGet(1).getNumWithDefaultValue(0);
3972  x1A = obj1.arrayGet(2).getNumWithDefaultValue(0);
3973  y1A = obj1.arrayGet(3).getNumWithDefaultValue(0);
3974  } else {
3975  error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
3976  return nullptr;
3977  }
3978 
3979  t0A = 0;
3980  t1A = 1;
3981  obj1 = dict->lookup("Domain");
3982  if (obj1.isArray() && obj1.arrayGetLength() == 2) {
3983  t0A = obj1.arrayGet(0).getNumWithDefaultValue(0);
3984  t1A = obj1.arrayGet(1).getNumWithDefaultValue(1);
3985  }
3986 
3987  obj1 = dict->lookup("Function");
3988  if (obj1.isArray()) {
3989  const int nFuncsA = obj1.arrayGetLength();
3990  if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) {
3991  error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3992  return nullptr;
3993  }
3994  for (int i = 0; i < nFuncsA; ++i) {