root/middleware-offline/trunk/_src/eidmw/pteid-poppler/poppler/GfxState.cc @ 226

Revision 226, 153.5 KB (checked in by vsilva, 8 years ago)

Improve PDF native Signature

Line 
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-2012 Albert Astals Cid <aacid@kde.org>
20// Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp>
21// Copyright (C) 2009, 2011 Thomas Freitag <Thomas.Freitag@alfa.de>
22// Copyright (C) 2009 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 William Bader <williambader@hotmail.com>
27//
28// To see a description of the changes please see the Changelog file that
29// came with your tarball or type make ChangeLog if you are building from git
30//
31//========================================================================
32
33#include <config.h>
34
35#ifdef USE_GCC_PRAGMAS
36#pragma implementation
37#endif
38
39#include <algorithm>
40#include <stddef.h>
41#include <math.h>
42#include <string.h>
43#include "goo/gmem.h"
44#include "Error.h"
45#include "Object.h"
46#include "Array.h"
47#include "Page.h"
48#include "Gfx.h"
49#include "GfxState.h"
50#include "GfxState_helpers.h"
51#include "GfxFont.h"
52#include "GlobalParams.h"
53#include "PopplerCache.h"
54
55//------------------------------------------------------------------------
56
57// Max depth of nested color spaces.  This is used to catch infinite
58// loops in the color space object structure.
59#define colorSpaceRecursionLimit 8
60
61//------------------------------------------------------------------------
62
63GBool Matrix::invertTo(Matrix *other) const
64{
65  double det;
66
67  det = 1 / determinant();
68  other->m[0] = m[3] * det;
69  other->m[1] = -m[1] * det;
70  other->m[2] = -m[2] * det;
71  other->m[3] = m[0] * det;
72  other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det;
73  other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det;
74
75  return gTrue;
76}
77
78void Matrix::transform(double x, double y, double *tx, double *ty) const
79{
80  double temp_x, temp_y;
81
82  temp_x = m[0] * x + m[2] * y + m[4];
83  temp_y = m[1] * x + m[3] * y + m[5];
84
85  *tx = temp_x;
86  *ty = temp_y;
87}
88
89// Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
90double Matrix::norm() const
91{
92  double f, g, h, i, j;
93
94  i = m[0]*m[0] + m[1]*m[1];
95  j = m[2]*m[2] + m[3]*m[3];
96
97  f = 0.5 * (i + j);
98  g = 0.5 * (i - j);
99  h = m[0]*m[2] + m[1]*m[3];
100
101  return sqrt (f + hypot (g, h));
102}
103
104//------------------------------------------------------------------------
105
106struct GfxBlendModeInfo {
107  const char *name;
108  GfxBlendMode mode;
109};
110
111static const GfxBlendModeInfo gfxBlendModeNames[] = {
112  { "Normal",     gfxBlendNormal },
113  { "Compatible", gfxBlendNormal },
114  { "Multiply",   gfxBlendMultiply },
115  { "Screen",     gfxBlendScreen },
116  { "Overlay",    gfxBlendOverlay },
117  { "Darken",     gfxBlendDarken },
118  { "Lighten",    gfxBlendLighten },
119  { "ColorDodge", gfxBlendColorDodge },
120  { "ColorBurn",  gfxBlendColorBurn },
121  { "HardLight",  gfxBlendHardLight },
122  { "SoftLight",  gfxBlendSoftLight },
123  { "Difference", gfxBlendDifference },
124  { "Exclusion",  gfxBlendExclusion },
125  { "Hue",        gfxBlendHue },
126  { "Saturation", gfxBlendSaturation },
127  { "Color",      gfxBlendColor },
128  { "Luminosity", gfxBlendLuminosity }
129};
130
131#define nGfxBlendModeNames \
132          ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
133         
134//------------------------------------------------------------------------
135//
136// NB: This must match the GfxColorSpaceMode enum defined in
137// GfxState.h
138static const char *gfxColorSpaceModeNames[] = {
139  "DeviceGray",
140  "CalGray",
141  "DeviceRGB",
142  "CalRGB",
143  "DeviceCMYK",
144  "Lab",
145  "ICCBased",
146  "Indexed",
147  "Separation",
148  "DeviceN",
149  "Pattern"
150};
151
152#define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
153
154#ifdef USE_CMS
155
156#ifdef USE_LCMS1
157#include <lcms.h>
158#define cmsColorSpaceSignature icColorSpaceSignature
159#define cmsSetLogErrorHandler cmsSetErrorHandler
160#define cmsSigXYZData icSigXYZData
161#define cmsSigLuvData icSigLuvData
162#define cmsSigLabData icSigLabData
163#define cmsSigYCbCrData icSigYCbCrData
164#define cmsSigYxyData icSigYxyData
165#define cmsSigRgbData icSigRgbData
166#define cmsSigHsvData icSigHsvData
167#define cmsSigHlsData icSigHlsData
168#define cmsSigCmyData icSigCmyData
169#define cmsSig3colorData icSig3colorData
170#define cmsSigGrayData icSigGrayData
171#define cmsSigCmykData icSigCmykData
172#define cmsSig4colorData icSig4colorData
173#define cmsSig2colorData icSig2colorData
174#define cmsSig5colorData icSig5colorData
175#define cmsSig6colorData icSig6colorData
176#define cmsSig7colorData icSig7colorData
177#define cmsSig8colorData icSig8colorData
178#define cmsSig9colorData icSig9colorData
179#define cmsSig10colorData icSig10colorData
180#define cmsSig11colorData icSig11colorData
181#define cmsSig12colorData icSig12colorData
182#define cmsSig13colorData icSig13colorData
183#define cmsSig14colorData icSig14colorData
184#define cmsSig15colorData icSig15colorData
185#define LCMS_FLAGS 0
186#else
187#include <lcms2.h>
188#define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE
189#endif
190
191#define COLOR_PROFILE_DIR "/ColorProfiles/"
192#define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR
193
194void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) {
195  cmsDoTransform(transform, in, out, size);
196}
197
198// transformA should be a cmsHTRANSFORM
199GfxColorTransform::GfxColorTransform(void *transformA) {
200  transform = transformA;
201  refCount = 1;
202}
203
204GfxColorTransform::~GfxColorTransform() {
205  cmsDeleteTransform(transform);
206}
207
208void GfxColorTransform::ref() {
209  refCount++;
210}
211
212unsigned int GfxColorTransform::unref() {
213  return --refCount;
214}
215
216static cmsHPROFILE RGBProfile = NULL;
217static GooString *displayProfileName = NULL; // display profile file Name
218static cmsHPROFILE displayProfile = NULL; // display profile
219static unsigned int displayPixelType = 0;
220static GfxColorTransform *XYZ2DisplayTransform = NULL;
221
222// convert color space signature to cmsColor type
223static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs);
224static unsigned int getCMSNChannels(cmsColorSpaceSignature cs);
225static cmsHPROFILE loadColorProfile(const char *fileName);
226
227void GfxColorSpace::setDisplayProfile(void *displayProfileA) {
228  displayProfile = displayProfileA;
229}
230
231void GfxColorSpace::setDisplayProfileName(GooString *name) {
232  displayProfileName = name->copy();
233}
234
235cmsHPROFILE GfxColorSpace::getRGBProfile() {
236  return RGBProfile;
237}
238
239cmsHPROFILE GfxColorSpace::getDisplayProfile() {
240  return displayProfile;
241}
242
243#endif
244
245//------------------------------------------------------------------------
246// GfxColorSpace
247//------------------------------------------------------------------------
248
249GfxColorSpace::GfxColorSpace() {
250  overprintMask = 0x0f;
251}
252
253GfxColorSpace::~GfxColorSpace() {
254}
255
256GfxColorSpace *GfxColorSpace::parse(Object *csObj, Gfx *gfx, int recursion) {
257  GfxColorSpace *cs;
258  Object obj1;
259
260  if (recursion > colorSpaceRecursionLimit) {
261    error(errSyntaxError, -1, "Loop detected in color space objects");
262    return NULL;
263  }
264
265  cs = NULL;
266  if (csObj->isName()) {
267    if (csObj->isName("DeviceGray") || csObj->isName("G")) {
268      cs = new GfxDeviceGrayColorSpace();
269    } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
270      cs = new GfxDeviceRGBColorSpace();
271    } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
272      cs = new GfxDeviceCMYKColorSpace();
273    } else if (csObj->isName("Pattern")) {
274      cs = new GfxPatternColorSpace(NULL);
275    } else {
276      error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName());
277    }
278  } else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
279    csObj->arrayGet(0, &obj1);
280    if (obj1.isName("DeviceGray") || obj1.isName("G")) {
281      cs = new GfxDeviceGrayColorSpace();
282    } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
283      cs = new GfxDeviceRGBColorSpace();
284    } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
285      cs = new GfxDeviceCMYKColorSpace();
286    } else if (obj1.isName("CalGray")) {
287      cs = GfxCalGrayColorSpace::parse(csObj->getArray());
288    } else if (obj1.isName("CalRGB")) {
289      cs = GfxCalRGBColorSpace::parse(csObj->getArray());
290    } else if (obj1.isName("Lab")) {
291      cs = GfxLabColorSpace::parse(csObj->getArray());
292    } else if (obj1.isName("ICCBased")) {
293      cs = GfxICCBasedColorSpace::parse(csObj->getArray(), gfx, recursion);
294    } else if (obj1.isName("Indexed") || obj1.isName("I")) {
295      cs = GfxIndexedColorSpace::parse(csObj->getArray(), gfx, recursion);
296    } else if (obj1.isName("Separation")) {
297      cs = GfxSeparationColorSpace::parse(csObj->getArray(), gfx, recursion);
298    } else if (obj1.isName("DeviceN")) {
299      cs = GfxDeviceNColorSpace::parse(csObj->getArray(), gfx, recursion);
300    } else if (obj1.isName("Pattern")) {
301      cs = GfxPatternColorSpace::parse(csObj->getArray(), gfx, recursion);
302    } else {
303      error(errSyntaxWarning, -1, "Bad color space");
304    }
305    obj1.free();
306  } else if (csObj->isDict()) {
307    csObj->dictLookup("ColorSpace", &obj1);
308    if (obj1.isName("DeviceGray")) {
309      cs = new GfxDeviceGrayColorSpace();
310    } else if (obj1.isName("DeviceRGB")) {
311      cs = new GfxDeviceRGBColorSpace();
312    } else if (obj1.isName("DeviceCMYK")) {
313      cs = new GfxDeviceCMYKColorSpace();
314    } else {
315      error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName());
316    }
317    obj1.free();
318  } else {
319    error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict");
320  }
321  return cs;
322}
323
324void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
325                                     int maxImgPixel) {
326  int i;
327
328  for (i = 0; i < getNComps(); ++i) {
329    decodeLow[i] = 0;
330    decodeRange[i] = 1;
331  }
332}
333
334int GfxColorSpace::getNumColorSpaceModes() {
335  return nGfxColorSpaceModes;
336}
337
338const char *GfxColorSpace::getColorSpaceModeName(int idx) {
339  return gfxColorSpaceModeNames[idx];
340}
341
342#ifdef USE_CMS
343cmsHPROFILE loadColorProfile(const char *fileName)
344{
345  cmsHPROFILE hp = NULL;
346  FILE *fp;
347
348  if (fileName[0] == '/') {
349    // full path
350    // check if open the file
351    if ((fp = fopen(fileName,"r")) != NULL) {
352      fclose(fp);
353      hp = cmsOpenProfileFromFile(fileName,"r");
354    }
355    return hp;
356  }
357  // try to load from user directory
358  GooString *path = globalParams->getBaseDir();
359  path->append(COLOR_PROFILE_DIR);
360  path->append(fileName);
361  // check if open the file
362  if ((fp = fopen(path->getCString(),"r")) != NULL) {
363    fclose(fp);
364    hp = cmsOpenProfileFromFile(path->getCString(),"r");
365  }
366  delete path;
367  if (hp == NULL) {
368    // load from global directory
369    path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
370    path->append(fileName);
371    // check if open the file
372    if ((fp = fopen(path->getCString(),"r")) != NULL) {
373      fclose(fp);
374      hp = cmsOpenProfileFromFile(path->getCString(),"r");
375    }
376    delete path;
377  }
378  return hp;
379}
380
381#ifdef USE_LCMS1
382static int CMSError(int ecode, const char *msg)
383{
384    error(errSyntaxWarning, -1, "{0:s}", msg);
385    return 1;
386}
387#else
388static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text)
389{
390    error(errSyntaxWarning, -1, "{0:s}", text);
391}
392#endif
393
394int GfxColorSpace::setupColorProfiles()
395{
396  static GBool initialized = gFalse;
397  cmsHTRANSFORM transform;
398  unsigned int nChannels;
399
400  // do only once
401  if (initialized) return 0;
402  initialized = gTrue;
403
404  // set error handlor
405  cmsSetLogErrorHandler(CMSError);
406
407  if (displayProfile == NULL) {
408    // load display profile if it was not already loaded.
409    if (displayProfileName == NULL) {
410      displayProfile = loadColorProfile("display.icc");
411    } else if (displayProfileName->getLength() > 0) {
412      displayProfile = loadColorProfile(displayProfileName->getCString());
413    }
414  }
415  // load RGB profile
416  RGBProfile = loadColorProfile("RGB.icc");
417  if (RGBProfile == NULL) {
418    /* use built in sRGB profile */
419    RGBProfile = cmsCreate_sRGBProfile();
420  }
421  // create transforms
422  if (displayProfile != NULL) {
423    displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
424    nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
425    // create transform from XYZ
426    cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
427    if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
428           displayProfile, 
429           COLORSPACE_SH(displayPixelType) |
430             CHANNELS_SH(nChannels) | BYTES_SH(1),
431          INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
432      error(errSyntaxWarning, -1, "Can't create Lab transform");
433    } else {
434      XYZ2DisplayTransform = new GfxColorTransform(transform);
435    }
436    cmsCloseProfile(XYZProfile);
437  }
438  return 0;
439}
440
441unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs)
442{
443    switch (cs) {
444    case cmsSigXYZData:
445      return PT_XYZ;
446      break;
447    case cmsSigLabData:
448      return PT_Lab;
449      break;
450    case cmsSigLuvData:
451      return PT_YUV;
452      break;
453    case cmsSigYCbCrData:
454      return PT_YCbCr;
455      break;
456    case cmsSigYxyData:
457      return PT_Yxy;
458      break;
459    case cmsSigRgbData:
460      return PT_RGB;
461      break;
462    case cmsSigGrayData:
463      return PT_GRAY;
464      break;
465    case cmsSigHsvData:
466      return PT_HSV;
467      break;
468    case cmsSigHlsData:
469      return PT_HLS;
470      break;
471    case cmsSigCmykData:
472      return PT_CMYK;
473      break;
474    case cmsSigCmyData:
475      return PT_CMY;
476      break;
477    case cmsSig2colorData:
478    case cmsSig3colorData:
479    case cmsSig4colorData:
480    case cmsSig5colorData:
481    case cmsSig6colorData:
482    case cmsSig7colorData:
483    case cmsSig8colorData:
484    case cmsSig9colorData:
485    case cmsSig10colorData:
486    case cmsSig11colorData:
487    case cmsSig12colorData:
488    case cmsSig13colorData:
489    case cmsSig14colorData:
490    case cmsSig15colorData:
491    default:
492      break;
493    }
494    return PT_RGB;
495}
496
497unsigned int getCMSNChannels(cmsColorSpaceSignature cs)
498{
499    switch (cs) {
500    case cmsSigXYZData:
501    case cmsSigLuvData:
502    case cmsSigLabData:
503    case cmsSigYCbCrData:
504    case cmsSigYxyData:
505    case cmsSigRgbData:
506    case cmsSigHsvData:
507    case cmsSigHlsData:
508    case cmsSigCmyData:
509    case cmsSig3colorData:
510      return 3;
511      break;
512    case cmsSigGrayData:
513      return 1;
514      break;
515    case cmsSigCmykData:
516    case cmsSig4colorData:
517      return 4;
518      break;
519    case cmsSig2colorData:
520      return 2;
521      break;
522    case cmsSig5colorData:
523      return 5;
524      break;
525    case cmsSig6colorData:
526      return 6;
527      break;
528    case cmsSig7colorData:
529      return 7;
530      break;
531    case cmsSig8colorData:
532      return 8;
533      break;
534    case cmsSig9colorData:
535      return 9;
536      break;
537    case cmsSig10colorData:
538      return 10;
539      break;
540    case cmsSig11colorData:
541      return 11;
542      break;
543    case cmsSig12colorData:
544      return 12;
545      break;
546    case cmsSig13colorData:
547      return 13;
548      break;
549    case cmsSig14colorData:
550      return 14;
551      break;
552    case cmsSig15colorData:
553      return 15;
554    default:
555      break;
556    }
557    return 3;
558}
559#endif
560
561//------------------------------------------------------------------------
562// GfxDeviceGrayColorSpace
563//------------------------------------------------------------------------
564
565GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
566}
567
568GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
569}
570
571GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
572  return new GfxDeviceGrayColorSpace();
573}
574
575void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
576  *gray = clip01(color->c[0]);
577}
578
579void GfxDeviceGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
580  memcpy (out, in, length);
581}
582
583void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
584  rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
585}
586
587void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, unsigned int *out,
588                                         int length) {
589  int i;
590
591  for (i = 0; i < length; i++)
592    out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0);
593}
594
595void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
596  for (int i = 0; i < length; i++) {
597    *out++ = in[i];
598    *out++ = in[i];
599    *out++ = in[i];
600  }
601}
602
603void GfxDeviceGrayColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
604  for (int i = 0; i < length; i++) {
605    *out++ = in[i];
606    *out++ = in[i];
607    *out++ = in[i];
608    *out++ = 255;
609  }
610}
611
612void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
613  cmyk->c = cmyk->m = cmyk->y = 0;
614  cmyk->k = clip01(gfxColorComp1 - color->c[0]);
615}
616
617void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
618  color->c[0] = 0;
619}
620
621//------------------------------------------------------------------------
622// GfxCalGrayColorSpace
623//------------------------------------------------------------------------
624
625GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
626  whiteX = whiteY = whiteZ = 1;
627  blackX = blackY = blackZ = 0;
628  gamma = 1;
629}
630
631GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
632}
633
634GfxColorSpace *GfxCalGrayColorSpace::copy() {
635  GfxCalGrayColorSpace *cs;
636
637  cs = new GfxCalGrayColorSpace();
638  cs->whiteX = whiteX;
639  cs->whiteY = whiteY;
640  cs->whiteZ = whiteZ;
641  cs->blackX = blackX;
642  cs->blackY = blackY;
643  cs->blackZ = blackZ;
644  cs->gamma = gamma;
645  return cs;
646}
647
648// This is the inverse of MatrixLMN in Example 4.10 from the PostScript
649// Language Reference, Third Edition.
650static const double xyzrgb[3][3] = {
651  {  3.240449, -1.537136, -0.498531 },
652  { -0.969265,  1.876011,  0.041556 },
653  {  0.055643, -0.204026,  1.057229 }
654};
655
656GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) {
657  GfxCalGrayColorSpace *cs;
658  Object obj1, obj2, obj3;
659
660  arr->get(1, &obj1);
661  if (!obj1.isDict()) {
662    error(errSyntaxWarning, -1, "Bad CalGray color space");
663    obj1.free();
664    return NULL;
665  }
666  cs = new GfxCalGrayColorSpace();
667  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
668      obj2.arrayGetLength() == 3) {
669    obj2.arrayGet(0, &obj3);
670    if (likely(obj3.isNum()))
671      cs->whiteX = obj3.getNum();
672    obj3.free();
673    obj2.arrayGet(1, &obj3);
674    if (likely(obj3.isNum()))
675      cs->whiteY = obj3.getNum();
676    obj3.free();
677    obj2.arrayGet(2, &obj3);
678    if (likely(obj3.isNum()))
679      cs->whiteZ = obj3.getNum();
680    obj3.free();
681  }
682  obj2.free();
683  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
684      obj2.arrayGetLength() == 3) {
685    obj2.arrayGet(0, &obj3);
686    if (likely(obj3.isNum()))
687      cs->blackX = obj3.getNum();
688    obj3.free();
689    obj2.arrayGet(1, &obj3);
690    if (likely(obj3.isNum()))
691      cs->blackY = obj3.getNum();
692    obj3.free();
693    obj2.arrayGet(2, &obj3);
694    if (likely(obj3.isNum()))
695      cs->blackZ = obj3.getNum();
696    obj3.free();
697  }
698  obj2.free();
699  if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
700    cs->gamma = obj2.getNum();
701  }
702  obj2.free();
703  obj1.free();
704
705  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
706                xyzrgb[0][1] * cs->whiteY +
707                xyzrgb[0][2] * cs->whiteZ);
708  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
709                xyzrgb[1][1] * cs->whiteY +
710                xyzrgb[1][2] * cs->whiteZ);
711  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
712                xyzrgb[2][1] * cs->whiteY +
713                xyzrgb[2][2] * cs->whiteZ);
714
715  return cs;
716}
717
718// convert CalGray to media XYZ color space
719// (not multiply by the white point)
720void GfxCalGrayColorSpace::getXYZ(GfxColor *color, 
721  double *pX, double *pY, double *pZ) {
722  const double A = colToDbl(color->c[0]);
723  const double xyzColor = pow(A,gamma);
724  *pX = xyzColor;
725  *pY = xyzColor;
726  *pZ = xyzColor;
727}
728
729void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
730  GfxRGB rgb;
731
732#ifdef USE_CMS
733  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
734    Guchar out[gfxColorMaxComps];
735    double in[gfxColorMaxComps];
736    double X, Y, Z;
737   
738    getXYZ(color,&X,&Y,&Z);
739    in[0] = clip01(X);
740    in[1] = clip01(Y);
741    in[2] = clip01(Z);
742    XYZ2DisplayTransform->doTransform(in,out,1);
743    *gray = byteToCol(out[0]);
744    return;
745  }
746#endif
747  getRGB(color, &rgb);
748  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
749                                0.587 * rgb.g +
750                                0.114 * rgb.b + 0.5));
751}
752
753void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
754  double X, Y, Z;
755  double r, g, b;
756
757  getXYZ(color,&X,&Y,&Z);
758#ifdef USE_CMS
759  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
760    Guchar out[gfxColorMaxComps];
761    double in[gfxColorMaxComps];
762   
763    in[0] = clip01(X);
764    in[1] = clip01(Y);
765    in[2] = clip01(Z);
766    XYZ2DisplayTransform->doTransform(in,out,1);
767    rgb->r = byteToCol(out[0]);
768    rgb->g = byteToCol(out[1]);
769    rgb->b = byteToCol(out[2]);
770    return;
771  }
772#endif
773  X *= whiteX;
774  Y *= whiteY;
775  Z *= whiteZ;
776  // convert XYZ to RGB, including gamut mapping and gamma correction
777  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
778  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
779  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
780  rgb->r = dblToCol(sqrt(clip01(r * kr)));
781  rgb->g = dblToCol(sqrt(clip01(g * kg)));
782  rgb->b = dblToCol(sqrt(clip01(b * kb)));
783}
784
785void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
786  GfxRGB rgb;
787  GfxColorComp c, m, y, k;
788
789#ifdef USE_CMS
790  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
791    double in[gfxColorMaxComps];
792    Guchar out[gfxColorMaxComps];
793    double X, Y, Z;
794   
795    getXYZ(color,&X,&Y,&Z);
796    in[0] = clip01(X);
797    in[1] = clip01(Y);
798    in[2] = clip01(Z);
799   
800    XYZ2DisplayTransform->doTransform(in,out,1);
801    cmyk->c = byteToCol(out[0]);
802    cmyk->m = byteToCol(out[1]);
803    cmyk->y = byteToCol(out[2]);
804    cmyk->k = byteToCol(out[3]);
805    return;
806  }
807#endif
808  getRGB(color, &rgb);
809  c = clip01(gfxColorComp1 - rgb.r);
810  m = clip01(gfxColorComp1 - rgb.g);
811  y = clip01(gfxColorComp1 - rgb.b);
812  k = c;
813  if (m < k) {
814    k = m;
815  }
816  if (y < k) {
817    k = y;
818  }
819  cmyk->c = c - k;
820  cmyk->m = m - k;
821  cmyk->y = y - k;
822  cmyk->k = k;
823}
824
825void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
826  color->c[0] = 0;
827}
828
829//------------------------------------------------------------------------
830// GfxDeviceRGBColorSpace
831//------------------------------------------------------------------------
832
833GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
834}
835
836GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
837}
838
839GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
840  return new GfxDeviceRGBColorSpace();
841}
842
843void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
844  *gray = clip01((GfxColorComp)(0.3 * color->c[0] +
845                 0.59 * color->c[1] +
846                 0.11 * color->c[2] + 0.5));
847}
848
849void GfxDeviceRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
850  int i;
851
852  for (i = 0; i < length; i++) {
853    out[i] = 
854      (in[i * 3 + 0] * 19595 + 
855       in[i * 3 + 1] * 38469 + 
856       in[i * 3 + 2] * 7472) / 65536;
857  }
858}
859
860void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
861  rgb->r = clip01(color->c[0]);
862  rgb->g = clip01(color->c[1]);
863  rgb->b = clip01(color->c[2]);
864}
865
866void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out,
867                                        int length) {
868  Guchar *p;
869  int i;
870
871  for (i = 0, p = in; i < length; i++, p += 3)
872    out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0);
873}
874
875void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
876  for (int i = 0; i < length; i++) {
877    *out++ = *in++;
878    *out++ = *in++;
879    *out++ = *in++;
880  }
881}
882
883void GfxDeviceRGBColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
884  for (int i = 0; i < length; i++) {
885    *out++ = *in++;
886    *out++ = *in++;
887    *out++ = *in++;
888    *out++ = 255;
889  }
890}
891void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
892  GfxColorComp c, m, y, k;
893
894  c = clip01(gfxColorComp1 - color->c[0]);
895  m = clip01(gfxColorComp1 - color->c[1]);
896  y = clip01(gfxColorComp1 - color->c[2]);
897  k = c;
898  if (m < k) {
899    k = m;
900  }
901  if (y < k) {
902    k = y;
903  }
904  cmyk->c = c - k;
905  cmyk->m = m - k;
906  cmyk->y = y - k;
907  cmyk->k = k;
908}
909
910void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
911  color->c[0] = 0;
912  color->c[1] = 0;
913  color->c[2] = 0;
914}
915
916//------------------------------------------------------------------------
917// GfxCalRGBColorSpace
918//------------------------------------------------------------------------
919
920GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
921  whiteX = whiteY = whiteZ = 1;
922  blackX = blackY = blackZ = 0;
923  gammaR = gammaG = gammaB = 1;
924  mat[0] = 1; mat[1] = 0; mat[2] = 0;
925  mat[3] = 0; mat[4] = 1; mat[5] = 0;
926  mat[6] = 0; mat[7] = 0; mat[8] = 1;
927}
928
929GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
930}
931
932GfxColorSpace *GfxCalRGBColorSpace::copy() {
933  GfxCalRGBColorSpace *cs;
934  int i;
935
936  cs = new GfxCalRGBColorSpace();
937  cs->whiteX = whiteX;
938  cs->whiteY = whiteY;
939  cs->whiteZ = whiteZ;
940  cs->blackX = blackX;
941  cs->blackY = blackY;
942  cs->blackZ = blackZ;
943  cs->gammaR = gammaR;
944  cs->gammaG = gammaG;
945  cs->gammaB = gammaB;
946  for (i = 0; i < 9; ++i) {
947    cs->mat[i] = mat[i];
948  }
949  return cs;
950}
951
952GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) {
953  GfxCalRGBColorSpace *cs;
954  Object obj1, obj2, obj3;
955  int i;
956
957  arr->get(1, &obj1);
958  if (!obj1.isDict()) {
959    error(errSyntaxWarning, -1, "Bad CalRGB color space");
960    obj1.free();
961    return NULL;
962  }
963  cs = new GfxCalRGBColorSpace();
964  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
965      obj2.arrayGetLength() == 3) {
966    obj2.arrayGet(0, &obj3);
967    if (likely(obj3.isNum()))
968      cs->whiteX = obj3.getNum();
969    obj3.free();
970    obj2.arrayGet(1, &obj3);
971    if (likely(obj3.isNum()))
972      cs->whiteY = obj3.getNum();
973    obj3.free();
974    obj2.arrayGet(2, &obj3);
975    if (likely(obj3.isNum()))
976      cs->whiteZ = obj3.getNum();
977    obj3.free();
978  }
979  obj2.free();
980  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
981      obj2.arrayGetLength() == 3) {
982    obj2.arrayGet(0, &obj3);
983    if (likely(obj3.isNum()))
984      cs->blackX = obj3.getNum();
985    obj3.free();
986    obj2.arrayGet(1, &obj3);
987    if (likely(obj3.isNum()))
988      cs->blackY = obj3.getNum();
989    obj3.free();
990    obj2.arrayGet(2, &obj3);
991    if (likely(obj3.isNum()))
992      cs->blackZ = obj3.getNum();
993    obj3.free();
994  }
995  obj2.free();
996  if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
997      obj2.arrayGetLength() == 3) {
998    obj2.arrayGet(0, &obj3);
999    if (likely(obj3.isNum()))
1000      cs->gammaR = obj3.getNum();
1001    obj3.free();
1002    obj2.arrayGet(1, &obj3);
1003    if (likely(obj3.isNum()))
1004      cs->gammaG = obj3.getNum();
1005    obj3.free();
1006    obj2.arrayGet(2, &obj3);
1007    if (likely(obj3.isNum()))
1008      cs->gammaB = obj3.getNum();
1009    obj3.free();
1010  }
1011  obj2.free();
1012  if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
1013      obj2.arrayGetLength() == 9) {
1014    for (i = 0; i < 9; ++i) {
1015      obj2.arrayGet(i, &obj3);
1016      if (likely(obj3.isNum()))
1017        cs->mat[i] = obj3.getNum();
1018      obj3.free();
1019    }
1020  }
1021  obj2.free();
1022  obj1.free();
1023
1024  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1025                xyzrgb[0][1] * cs->whiteY +
1026                xyzrgb[0][2] * cs->whiteZ);
1027  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1028                xyzrgb[1][1] * cs->whiteY +
1029                xyzrgb[1][2] * cs->whiteZ);
1030  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1031                xyzrgb[2][1] * cs->whiteY +
1032                xyzrgb[2][2] * cs->whiteZ);
1033
1034  return cs;
1035}
1036
1037// convert CalRGB to XYZ color space
1038void GfxCalRGBColorSpace::getXYZ(GfxColor *color, 
1039  double *pX, double *pY, double *pZ) {
1040  double A, B, C;
1041
1042  A = pow(colToDbl(color->c[0]), gammaR);
1043  B = pow(colToDbl(color->c[1]), gammaG);
1044  C = pow(colToDbl(color->c[2]), gammaB);
1045  *pX = mat[0] * A + mat[3] * B + mat[6] * C;
1046  *pY = mat[1] * A + mat[4] * B + mat[7] * C;
1047  *pZ = mat[2] * A + mat[5] * B + mat[8] * C;
1048}
1049
1050void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1051  GfxRGB rgb;
1052
1053#ifdef USE_CMS
1054  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
1055    Guchar out[gfxColorMaxComps];
1056    double in[gfxColorMaxComps];
1057    double X, Y, Z;
1058   
1059    getXYZ(color,&X,&Y,&Z);
1060    in[0] = clip01(X);
1061    in[1] = clip01(Y);
1062    in[2] = clip01(Z);
1063    XYZ2DisplayTransform->doTransform(in,out,1);
1064    *gray = byteToCol(out[0]);
1065    return;
1066  }
1067#endif
1068  getRGB(color, &rgb);
1069  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1070                                0.587 * rgb.g +
1071                                0.114 * rgb.b + 0.5));
1072}
1073
1074void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1075  double X, Y, Z;
1076  double r, g, b;
1077
1078  getXYZ(color,&X,&Y,&Z);
1079#ifdef USE_CMS
1080  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
1081    Guchar out[gfxColorMaxComps];
1082    double in[gfxColorMaxComps];
1083   
1084    in[0] = clip01(X/whiteX);
1085    in[1] = clip01(Y/whiteY);
1086    in[2] = clip01(Z/whiteZ);
1087    XYZ2DisplayTransform->doTransform(in,out,1);
1088    rgb->r = byteToCol(out[0]);
1089    rgb->g = byteToCol(out[1]);
1090    rgb->b = byteToCol(out[2]);
1091    return;
1092  }
1093#endif
1094  // convert XYZ to RGB, including gamut mapping and gamma correction
1095  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1096  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1097  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1098  rgb->r = dblToCol(sqrt(clip01(r)));
1099  rgb->g = dblToCol(sqrt(clip01(g)));
1100  rgb->b = dblToCol(sqrt(clip01(b)));
1101}
1102
1103void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1104  GfxRGB rgb;
1105  GfxColorComp c, m, y, k;
1106
1107#ifdef USE_CMS
1108  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
1109    double in[gfxColorMaxComps];
1110    Guchar out[gfxColorMaxComps];
1111    double X, Y, Z;
1112   
1113    getXYZ(color,&X,&Y,&Z);
1114    in[0] = clip01(X);
1115    in[1] = clip01(Y);
1116    in[2] = clip01(Z);
1117    XYZ2DisplayTransform->doTransform(in,out,1);
1118    cmyk->c = byteToCol(out[0]);
1119    cmyk->m = byteToCol(out[1]);
1120    cmyk->y = byteToCol(out[2]);
1121    cmyk->k = byteToCol(out[3]);
1122    return;
1123  }
1124#endif
1125  getRGB(color, &rgb);
1126  c = clip01(gfxColorComp1 - rgb.r);
1127  m = clip01(gfxColorComp1 - rgb.g);
1128  y = clip01(gfxColorComp1 - rgb.b);
1129  k = c;
1130  if (m < k) {
1131    k = m;
1132  }
1133  if (y < k) {
1134    k = y;
1135  }
1136  cmyk->c = c - k;
1137  cmyk->m = m - k;
1138  cmyk->y = y - k;
1139  cmyk->k = k;
1140}
1141
1142void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
1143  color->c[0] = 0;
1144  color->c[1] = 0;
1145  color->c[2] = 0;
1146}
1147
1148//------------------------------------------------------------------------
1149// GfxDeviceCMYKColorSpace
1150//------------------------------------------------------------------------
1151
1152GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
1153}
1154
1155GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
1156}
1157
1158GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
1159  return new GfxDeviceCMYKColorSpace();
1160}
1161
1162void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1163  *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
1164                                - 0.3  * color->c[0]
1165                                - 0.59 * color->c[1]
1166                                - 0.11 * color->c[2] + 0.5));
1167}
1168
1169void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1170  double c, m, y, k, c1, m1, y1, k1, r, g, b;
1171   
1172  c = colToDbl(color->c[0]);
1173  m = colToDbl(color->c[1]);
1174  y = colToDbl(color->c[2]);
1175  k = colToDbl(color->c[3]);
1176  c1 = 1 - c;
1177  m1 = 1 - m;
1178  y1 = 1 - y;
1179  k1 = 1 - k;
1180  cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1181  rgb->r = clip01(dblToCol(r));
1182  rgb->g = clip01(dblToCol(g));
1183  rgb->b = clip01(dblToCol(b));
1184}
1185
1186static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(Guchar *&in, double &r, double &g, double &b)
1187{
1188  double c, m, y, k, c1, m1, y1, k1;
1189 
1190  c = byteToDbl(*in++);
1191  m = byteToDbl(*in++);
1192  y = byteToDbl(*in++);
1193  k = byteToDbl(*in++);
1194  c1 = 1 - c;
1195  m1 = 1 - m;
1196  y1 = 1 - y;
1197  k1 = 1 - k;
1198  cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1199}
1200
1201void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length)
1202{
1203  double r, g, b;
1204  for (int i = 0; i < length; i++) {
1205    GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1206    *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b));
1207  }
1208}
1209
1210void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
1211{
1212  double r, g, b;
1213 
1214  for (int i = 0; i < length; i++) {
1215    GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1216    *out++ = dblToByte(clip01(r));
1217    *out++ = dblToByte(clip01(g));
1218    *out++ = dblToByte(clip01(b));
1219  }
1220}
1221
1222void GfxDeviceCMYKColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
1223{
1224  double r, g, b;
1225 
1226  for (int i = 0; i < length; i++) {
1227    GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1228    *out++ = dblToByte(clip01(r));
1229    *out++ = dblToByte(clip01(g));
1230    *out++ = dblToByte(clip01(b));
1231    *out++ = 255;
1232  }
1233}
1234
1235void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1236  cmyk->c = clip01(color->c[0]);
1237  cmyk->m = clip01(color->c[1]);
1238  cmyk->y = clip01(color->c[2]);
1239  cmyk->k = clip01(color->c[3]);
1240}
1241
1242void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
1243  color->c[0] = 0;
1244  color->c[1] = 0;
1245  color->c[2] = 0;
1246  color->c[3] = gfxColorComp1;
1247}
1248
1249//------------------------------------------------------------------------
1250// GfxLabColorSpace
1251//------------------------------------------------------------------------
1252
1253GfxLabColorSpace::GfxLabColorSpace() {
1254  whiteX = whiteY = whiteZ = 1;
1255  blackX = blackY = blackZ = 0;
1256  aMin = bMin = -100;
1257  aMax = bMax = 100;
1258}
1259
1260GfxLabColorSpace::~GfxLabColorSpace() {
1261}
1262
1263GfxColorSpace *GfxLabColorSpace::copy() {
1264  GfxLabColorSpace *cs;
1265
1266  cs = new GfxLabColorSpace();
1267  cs->whiteX = whiteX;
1268  cs->whiteY = whiteY;
1269  cs->whiteZ = whiteZ;
1270  cs->blackX = blackX;
1271  cs->blackY = blackY;
1272  cs->blackZ = blackZ;
1273  cs->aMin = aMin;
1274  cs->aMax = aMax;
1275  cs->bMin = bMin;
1276  cs->bMax = bMax;
1277  cs->kr = kr;
1278  cs->kg = kg;
1279  cs->kb = kb;
1280  return cs;
1281}
1282
1283GfxColorSpace *GfxLabColorSpace::parse(Array *arr) {
1284  GfxLabColorSpace *cs;
1285  Object obj1, obj2, obj3;
1286
1287  arr->get(1, &obj1);
1288  if (!obj1.isDict()) {
1289    error(errSyntaxWarning, -1, "Bad Lab color space");
1290    obj1.free();
1291    return NULL;
1292  }
1293  cs = new GfxLabColorSpace();
1294  if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
1295      obj2.arrayGetLength() == 3) {
1296    obj2.arrayGet(0, &obj3);
1297    cs->whiteX = obj3.getNum();
1298    obj3.free();
1299    obj2.arrayGet(1, &obj3);
1300    cs->whiteY = obj3.getNum();
1301    obj3.free();
1302    obj2.arrayGet(2, &obj3);
1303    cs->whiteZ = obj3.getNum();
1304    obj3.free();
1305  }
1306  obj2.free();
1307  if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
1308      obj2.arrayGetLength() == 3) {
1309    obj2.arrayGet(0, &obj3);
1310    cs->blackX = obj3.getNum();
1311    obj3.free();
1312    obj2.arrayGet(1, &obj3);
1313    cs->blackY = obj3.getNum();
1314    obj3.free();
1315    obj2.arrayGet(2, &obj3);
1316    cs->blackZ = obj3.getNum();
1317    obj3.free();
1318  }
1319  obj2.free();
1320  if (obj1.dictLookup("Range", &obj2)->isArray() &&
1321      obj2.arrayGetLength() == 4) {
1322    obj2.arrayGet(0, &obj3);
1323    cs->aMin = obj3.getNum();
1324    obj3.free();
1325    obj2.arrayGet(1, &obj3);
1326    cs->aMax = obj3.getNum();
1327    obj3.free();
1328    obj2.arrayGet(2, &obj3);
1329    cs->bMin = obj3.getNum();
1330    obj3.free();
1331    obj2.arrayGet(3, &obj3);
1332    cs->bMax = obj3.getNum();
1333    obj3.free();
1334  }
1335  obj2.free();
1336  obj1.free();
1337
1338  cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1339                xyzrgb[0][1] * cs->whiteY +
1340                xyzrgb[0][2] * cs->whiteZ);
1341  cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1342                xyzrgb[1][1] * cs->whiteY +
1343                xyzrgb[1][2] * cs->whiteZ);
1344  cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1345                xyzrgb[2][1] * cs->whiteY +
1346                xyzrgb[2][2] * cs->whiteZ);
1347
1348  return cs;
1349}
1350
1351void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1352  GfxRGB rgb;
1353
1354#ifdef USE_CMS
1355  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) {
1356    Guchar out[gfxColorMaxComps];
1357    double in[gfxColorMaxComps];
1358   
1359    getXYZ(color, &in[0], &in[1], &in[2]);
1360    XYZ2DisplayTransform->doTransform(in,out,1);
1361    *gray = byteToCol(out[0]);
1362    return;
1363  }
1364#endif
1365  getRGB(color, &rgb);
1366  *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1367                                0.587 * rgb.g +
1368                                0.114 * rgb.b + 0.5));
1369}
1370
1371// convert L*a*b* to media XYZ color space
1372// (not multiply by the white point)
1373void GfxLabColorSpace::getXYZ(GfxColor *color, 
1374  double *pX, double *pY, double *pZ) {
1375  double X, Y, Z;
1376  double t1, t2;
1377
1378  t1 = (colToDbl(color->c[0]) + 16) / 116;
1379  t2 = t1 + colToDbl(color->c[1]) / 500;
1380  if (t2 >= (6.0 / 29.0)) {
1381    X = t2 * t2 * t2;
1382  } else {
1383    X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1384  }
1385  if (t1 >= (6.0 / 29.0)) {
1386    Y = t1 * t1 * t1;
1387  } else {
1388    Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
1389  }
1390  t2 = t1 - colToDbl(color->c[2]) / 200;
1391  if (t2 >= (6.0 / 29.0)) {
1392    Z = t2 * t2 * t2;
1393  } else {
1394    Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1395  }
1396  *pX = X;
1397  *pY = Y;
1398  *pZ = Z;
1399}
1400
1401void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1402  double X, Y, Z;
1403  double r, g, b;
1404
1405  getXYZ(color, &X, &Y, &Z);
1406#ifdef USE_CMS
1407  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) {
1408    Guchar out[gfxColorMaxComps];
1409    double in[gfxColorMaxComps];
1410   
1411    in[0] = clip01(X);
1412    in[1] = clip01(Y);
1413    in[2] = clip01(Z);
1414    XYZ2DisplayTransform->doTransform(in,out,1);
1415    rgb->r = byteToCol(out[0]);
1416    rgb->g = byteToCol(out[1]);
1417    rgb->b = byteToCol(out[2]);
1418    return;
1419  }
1420#endif
1421  X *= whiteX;
1422  Y *= whiteY;
1423  Z *= whiteZ;
1424  // convert XYZ to RGB, including gamut mapping and gamma correction
1425  r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1426  g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1427  b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1428  rgb->r = dblToCol(sqrt(clip01(r * kr)));
1429  rgb->g = dblToCol(sqrt(clip01(g * kg)));
1430  rgb->b = dblToCol(sqrt(clip01(b * kb)));
1431}
1432
1433void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1434  GfxRGB rgb;
1435  GfxColorComp c, m, y, k;
1436
1437#ifdef USE_CMS
1438  if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) {
1439    double in[gfxColorMaxComps];
1440    Guchar out[gfxColorMaxComps];
1441   
1442    getXYZ(color, &in[0], &in[1], &in[2]);
1443    XYZ2DisplayTransform->doTransform(in,out,1);
1444    cmyk->c = byteToCol(out[0]);
1445    cmyk->m = byteToCol(out[1]);
1446    cmyk->y = byteToCol(out[2]);
1447    cmyk->k = byteToCol(out[3]);
1448    return;
1449  }
1450#endif
1451  getRGB(color, &rgb);
1452  c = clip01(gfxColorComp1 - rgb.r);
1453  m = clip01(gfxColorComp1 - rgb.g);
1454  y = clip01(gfxColorComp1 - rgb.b);
1455  k = c;
1456  if (m < k) {
1457    k = m;
1458  }
1459  if (y < k) {
1460    k = y;
1461  }
1462  cmyk->c = c - k;
1463  cmyk->m = m - k;
1464  cmyk->y = y - k;
1465  cmyk->k = k;
1466}
1467
1468void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
1469  color->c[0] = 0;
1470  if (aMin > 0) {
1471    color->c[1] = dblToCol(aMin);
1472  } else if (aMax < 0) {
1473    color->c[1] = dblToCol(aMax);
1474  } else {
1475    color->c[1] = 0;
1476  }
1477  if (bMin > 0) {
1478    color->c[2] = dblToCol(bMin);
1479  } else if (bMax < 0) {
1480    color->c[2] = dblToCol(bMax);
1481  } else {
1482    color->c[2] = 0;
1483  }
1484}
1485
1486void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
1487                                        int maxImgPixel) {
1488  decodeLow[0] = 0;
1489  decodeRange[0] = 100;
1490  decodeLow[1] = aMin;
1491  decodeRange[1] = aMax - aMin;
1492  decodeLow[2] = bMin;
1493  decodeRange[2] = bMax - bMin;
1494}
1495
1496//------------------------------------------------------------------------
1497// GfxICCBasedColorSpace
1498//------------------------------------------------------------------------
1499
1500class GfxICCBasedColorSpaceKey : public PopplerCacheKey
1501{
1502  public:
1503    GfxICCBasedColorSpaceKey(int numA, int genA) : num(numA), gen(genA)
1504    {
1505    }
1506   
1507    bool operator==(const PopplerCacheKey &key) const
1508    {
1509      const GfxICCBasedColorSpaceKey *k = static_cast<const GfxICCBasedColorSpaceKey*>(&key);
1510      return k->num == num && k->gen == gen;
1511    }
1512   
1513    int num, gen;
1514};
1515
1516class GfxICCBasedColorSpaceItem : public PopplerCacheItem
1517{
1518  public:
1519    GfxICCBasedColorSpaceItem(GfxICCBasedColorSpace *csA)
1520    {
1521      cs = static_cast<GfxICCBasedColorSpace*>(csA->copy());
1522    }
1523   
1524    ~GfxICCBasedColorSpaceItem()
1525    {
1526      delete cs;
1527    }
1528   
1529    GfxICCBasedColorSpace *cs;
1530};
1531
1532GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
1533                                             Ref *iccProfileStreamA) {
1534  nComps = nCompsA;
1535  alt = altA;
1536  iccProfileStream = *iccProfileStreamA;
1537  rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
1538  rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
1539#ifdef USE_CMS
1540  transform = NULL;
1541  lineTransform = NULL;
1542#endif
1543}
1544
1545GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
1546  delete alt;
1547#ifdef USE_CMS
1548  if (transform != NULL) {
1549    if (transform->unref() == 0) delete transform;
1550  }
1551  if (lineTransform != NULL) {
1552    if (lineTransform->unref() == 0) delete lineTransform;
1553  }
1554#endif
1555}
1556
1557GfxColorSpace *GfxICCBasedColorSpace::copy() {
1558  GfxICCBasedColorSpace *cs;
1559  int i;
1560
1561  cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
1562  for (i = 0; i < 4; ++i) {
1563    cs->rangeMin[i] = rangeMin[i];
1564    cs->rangeMax[i] = rangeMax[i];
1565  }
1566#ifdef USE_CMS
1567  cs->transform = transform;
1568  if (transform != NULL) transform->ref();
1569  cs->lineTransform = lineTransform;
1570  if (lineTransform != NULL) lineTransform->ref();
1571#endif
1572  return cs;
1573}
1574
1575GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, Gfx *gfx, int recursion) {
1576  GfxICCBasedColorSpace *cs;
1577  Ref iccProfileStreamA;
1578  int nCompsA;
1579  GfxColorSpace *altA;
1580  Dict *dict;
1581  Object obj1, obj2, obj3;
1582  int i;
1583
1584  if (arr->getLength() < 2) {
1585    error(errSyntaxError, -1, "Bad ICCBased color space");
1586    return NULL;
1587  }
1588  arr->getNF(1, &obj1);
1589  if (obj1.isRef()) {
1590    iccProfileStreamA = obj1.getRef();
1591  } else {
1592    iccProfileStreamA.num = 0;
1593    iccProfileStreamA.gen = 0;
1594  }
1595  obj1.free();
1596#ifdef USE_CMS
1597  // check cache
1598  if (gfx && iccProfileStreamA.num > 0) {
1599    GfxICCBasedColorSpaceKey k(iccProfileStreamA.num, iccProfileStreamA.gen);
1600    GfxICCBasedColorSpaceItem *item = static_cast<GfxICCBasedColorSpaceItem *>(gfx->getIccColorSpaceCache()->lookup(k));
1601    if (item != NULL)
1602    {
1603      cs = static_cast<GfxICCBasedColorSpace*>(item->cs->copy());
1604      return cs;
1605    }
1606  }
1607#endif
1608  arr->get(1, &obj1);
1609  if (!obj1.isStream()) {
1610    error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)");
1611    obj1.free();
1612    return NULL;
1613  }
1614  dict = obj1.streamGetDict();
1615  if (!dict->lookup("N", &obj2)->isInt()) {
1616    error(errSyntaxWarning, -1, "Bad ICCBased color space (N)");
1617    obj2.free();
1618    obj1.free();
1619    return NULL;
1620  }
1621  nCompsA = obj2.getInt();
1622  obj2.free();
1623  if (nCompsA > 4) {
1624    error(errSyntaxError, -1,
1625          "ICCBased color space with too many ({0:d} > 4) components",
1626          nCompsA);
1627    nCompsA = 4;
1628  }
1629  if (dict->lookup("Alternate", &obj2)->isNull() ||
1630      !(altA = GfxColorSpace::parse(&obj2, gfx, recursion + 1))) {
1631    switch (nCompsA) {
1632    case 1:
1633      altA = new GfxDeviceGrayColorSpace();
1634      break;
1635    case 3:
1636      altA = new GfxDeviceRGBColorSpace();
1637      break;
1638    case 4:
1639      altA = new GfxDeviceCMYKColorSpace();
1640      break;
1641    default:
1642      error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N");
1643      obj2.free();
1644      obj1.free();
1645      return NULL;
1646    }
1647  }
1648  obj2.free();
1649  cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
1650  if (dict->lookup("Range", &obj2)->isArray() &&
1651      obj2.arrayGetLength() == 2 * nCompsA) {
1652    Object obj4;
1653    for (i = 0; i < nCompsA; ++i) {
1654      obj2.arrayGet(2*i, &obj3);
1655      obj2.arrayGet(2*i+1, &obj4);
1656      if (obj3.isNum() && obj4.isNum()) {
1657        cs->rangeMin[i] = obj3.getNum();
1658        cs->rangeMax[i] = obj4.getNum();
1659      }
1660      obj3.free();
1661      obj4.free();
1662    }
1663  }
1664  obj2.free();
1665  obj1.free();
1666
1667#ifdef USE_CMS
1668  arr->get(1, &obj1);
1669  dict = obj1.streamGetDict();
1670  Guchar *profBuf;
1671  Stream *iccStream = obj1.getStream();
1672  int length = 0;
1673
1674  profBuf = iccStream->toUnsignedChars(&length, 65536, 65536);
1675  cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,length);
1676  gfree(profBuf);
1677  if (hp == 0) {
1678    error(errSyntaxWarning, -1, "read ICCBased color space profile error");
1679  } else {
1680    cmsHPROFILE dhp = displayProfile;
1681    if (dhp == NULL) dhp = RGBProfile;
1682    unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(hp));
1683    unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp));
1684    unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp));
1685    cmsHTRANSFORM transform;
1686    if ((transform = cmsCreateTransform(hp,
1687           COLORSPACE_SH(cst) |CHANNELS_SH(nCompsA) | BYTES_SH(1),
1688           dhp,
1689           COLORSPACE_SH(dcst) |
1690             CHANNELS_SH(dNChannels) | BYTES_SH(1),
1691          INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
1692      error(errSyntaxWarning, -1, "Can't create transform");
1693      cs->transform = NULL;
1694    } else {
1695      cs->transform = new GfxColorTransform(transform);
1696    }
1697    if (dcst == PT_RGB) {
1698       // create line transform only when the display is RGB type color space
1699      if ((transform = cmsCreateTransform(hp,
1700            CHANNELS_SH(nCompsA) | BYTES_SH(1),dhp,
1701            TYPE_RGB_8,INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
1702        error(errSyntaxWarning, -1, "Can't create transform");
1703        cs->lineTransform = NULL;
1704      } else {
1705        cs->lineTransform = new GfxColorTransform(transform);
1706      }
1707    }
1708    cmsCloseProfile(hp);
1709  }
1710  obj1.free();
1711  // put this colorSpace into cache
1712  if (gfx && iccProfileStreamA.num > 0) {
1713    GfxICCBasedColorSpaceKey *k = new GfxICCBasedColorSpaceKey(iccProfileStreamA.num, iccProfileStreamA.gen);
1714    GfxICCBasedColorSpaceItem *item = new GfxICCBasedColorSpaceItem(cs);
1715    gfx->getIccColorSpaceCache()->put(k, item);
1716  }
1717#endif
1718  return cs;
1719}
1720
1721void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1722#ifdef USE_CMS
1723  if (transform != 0 && displayPixelType == PT_GRAY) {
1724    Guchar in[gfxColorMaxComps];
1725    Guchar out[gfxColorMaxComps];
1726   
1727    for (int i = 0;i < nComps;i++) {
1728        in[i] = colToByte(color->c[i]);
1729    }
1730    transform->doTransform(in,out,1);
1731    *gray = byteToCol(out[0]);
1732  } else {
1733    GfxRGB rgb;
1734    getRGB(color,&rgb);
1735    *gray = clip01((GfxColorComp)(0.3 * rgb.r +
1736                   0.59 * rgb.g +
1737                   0.11 * rgb.b + 0.5));
1738  }
1739#else
1740  alt->getGray(color, gray);
1741#endif
1742}
1743
1744void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1745#ifdef USE_CMS
1746  if (transform != 0
1747       && (displayProfile == NULL || displayPixelType == PT_RGB)) {
1748    Guchar in[gfxColorMaxComps];
1749    Guchar out[gfxColorMaxComps];
1750   
1751    for (int i = 0;i < nComps;i++) {
1752        in[i] = colToByte(color->c[i]);
1753    }
1754    transform->doTransform(in,out,1);
1755    rgb->r = byteToCol(out[0]);
1756    rgb->g = byteToCol(out[1]);
1757    rgb->b = byteToCol(out[2]);
1758  } else {
1759    alt->getRGB(color, rgb);
1760  }
1761#else
1762  alt->getRGB(color, rgb);
1763#endif
1764}
1765
1766void GfxICCBasedColorSpace::getRGBLine(Guchar *in, unsigned int *out,
1767                                       int length) {
1768#ifdef USE_CMS
1769  if (lineTransform != 0) {
1770    Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
1771    lineTransform->doTransform(in, tmp, length);
1772    for (int i = 0; i < length; ++i) {
1773        Guchar *current = tmp + (i * 3);
1774        out[i] = (current[0] << 16) | (current[1] << 8) | current[2];
1775    }
1776    gfree(tmp);
1777  } else {
1778    alt->getRGBLine(in, out, length);
1779  }
1780#else
1781  alt->getRGBLine(in, out, length);
1782#endif
1783}
1784
1785void GfxICCBasedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
1786#ifdef USE_CMS
1787  if (lineTransform != 0) {
1788    Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
1789    lineTransform->doTransform(in, tmp, length);
1790    Guchar *current = tmp;
1791    for (int i = 0; i < length; ++i) {
1792        *out++ = *current++;
1793        *out++ = *current++;
1794        *out++ = *current++;
1795    }
1796    gfree(tmp);
1797  } else {
1798    alt->getRGBLine(in, out, length);
1799  }
1800#else
1801  alt->getRGBLine(in, out, length);
1802#endif
1803}
1804
1805void GfxICCBasedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
1806#ifdef USE_CMS
1807  if (lineTransform != 0) {
1808    Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
1809    lineTransform->doTransform(in, tmp, length);
1810    Guchar *current = tmp;
1811    for (int i = 0; i < length; ++i) {
1812        *out++ = *current++;
1813        *out++ = *current++;
1814        *out++ = *current++;
1815        *out++ = 255;
1816    }
1817    gfree(tmp);
1818  } else {
1819    alt->getRGBXLine(in, out, length);
1820  }
1821#else
1822  alt->getRGBXLine(in, out, length);
1823#endif
1824}
1825
1826void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1827#ifdef USE_CMS
1828  if (transform != NULL && displayPixelType == PT_CMYK) {
1829    Guchar in[gfxColorMaxComps];
1830    Guchar out[gfxColorMaxComps];
1831   
1832    for (int i = 0;i < nComps;i++) {
1833        in[i] = colToByte(color->c[i]);
1834    }
1835    transform->doTransform(in,out,1);
1836    cmyk->c = byteToCol(out[0]);
1837    cmyk->m = byteToCol(out[1]);
1838    cmyk->y = byteToCol(out[2]);
1839    cmyk->k = byteToCol(out[3]);
1840  } else {
1841    GfxRGB rgb;
1842    GfxColorComp c, m, y, k;
1843
1844    getRGB(color,&rgb);
1845    c = clip01(gfxColorComp1 - rgb.r);
1846    m = clip01(gfxColorComp1 - rgb.g);
1847    y = clip01(gfxColorComp1 - rgb.b);
1848    k = c;
1849    if (m < k) {
1850      k = m;
1851    }
1852    if (y < k) {
1853      k = y;
1854    }
1855    cmyk->c = c - k;
1856    cmyk->m = m - k;
1857    cmyk->y = y - k;
1858    cmyk->k = k;
1859  }
1860#else
1861  alt->getCMYK(color, cmyk);
1862#endif
1863}
1864
1865GBool GfxICCBasedColorSpace::useGetRGBLine() {
1866#ifdef USE_CMS
1867  return lineTransform != NULL || alt->useGetRGBLine();
1868#else
1869  return alt->useGetRGBLine();
1870#endif
1871}
1872
1873void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
1874  int i;
1875
1876  for (i = 0; i < nComps; ++i) {
1877    if (rangeMin[i] > 0) {
1878      color->c[i] = dblToCol(rangeMin[i]);
1879    } else if (rangeMax[i] < 0) {
1880      color->c[i] = dblToCol(rangeMax[i]);
1881    } else {
1882      color->c[i] = 0;
1883    }
1884  }
1885}
1886
1887void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
1888                                             double *decodeRange,
1889                                             int maxImgPixel) {
1890  alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
1891
1892#if 0
1893  // this is nominally correct, but some PDF files don't set the
1894  // correct ranges in the ICCBased dict
1895  int i;
1896
1897  for (i = 0; i < nComps; ++i) {
1898    decodeLow[i] = rangeMin[i];
1899    decodeRange[i] = rangeMax[i] - rangeMin[i];
1900  }
1901#endif
1902}
1903
1904//------------------------------------------------------------------------
1905// GfxIndexedColorSpace
1906//------------------------------------------------------------------------
1907
1908GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
1909                                           int indexHighA) {
1910  base = baseA;
1911  indexHigh = indexHighA;
1912  lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
1913                              sizeof(Guchar));
1914  overprintMask = base->getOverprintMask();
1915}
1916
1917GfxIndexedColorSpace::~GfxIndexedColorSpace() {
1918  delete base;
1919  gfree(lookup);
1920}
1921
1922GfxColorSpace *GfxIndexedColorSpace::copy() {
1923  GfxIndexedColorSpace *cs;
1924
1925  cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
1926  memcpy(cs->lookup, lookup,
1927         (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
1928  return cs;
1929}
1930
1931GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, Gfx *gfx, int recursion) {
1932  GfxIndexedColorSpace *cs;
1933  GfxColorSpace *baseA;
1934  int indexHighA;
1935  Object obj1;
1936  char *s;
1937  int n, i, j;
1938
1939  if (arr->getLength() != 4) {
1940    error(errSyntaxWarning, -1, "Bad Indexed color space");
1941    goto err1;
1942  }
1943  arr->get(1, &obj1);
1944  if (!(baseA = GfxColorSpace::parse(&obj1, gfx, recursion + 1))) {
1945    error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)");
1946    goto err2;
1947  }
1948  obj1.free();
1949  if (!arr->get(2, &obj1)->isInt()) {
1950    error(errSyntaxWarning, -1, "Bad Indexed color space (hival)");
1951    delete baseA;
1952    goto err2;
1953  }
1954  indexHighA = obj1.getInt();
1955  if (indexHighA < 0 || indexHighA > 255) {
1956    // the PDF spec requires indexHigh to be in [0,255] -- allowing
1957    // values larger than 255 creates a security hole: if nComps *
1958    // indexHigh is greater than 2^31, the loop below may overwrite
1959    // past the end of the array
1960    int previousValue = indexHighA;
1961    if (indexHighA < 0) indexHighA = 0;
1962    else indexHighA = 255;
1963    error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA);
1964  }
1965  obj1.free();
1966  cs = new GfxIndexedColorSpace(baseA, indexHighA);
1967  arr->get(3, &obj1);
1968  n = baseA->getNComps();
1969  if (obj1.isStream()) {
1970    obj1.streamReset();
1971    for (i = 0; i <= indexHighA; ++i) {
1972      const int readChars = obj1.streamGetChars(n, &cs->lookup[i*n]);
1973      for (j = readChars; j < n; ++j) {
1974        error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes");
1975        cs->lookup[i*n + j] = 0;
1976      }
1977    }
1978    obj1.streamClose();
1979  } else if (obj1.isString()) {
1980    if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
1981      error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)");
1982      goto err3;
1983    }
1984    s = obj1.getString()->getCString();
1985    for (i = 0; i <= indexHighA; ++i) {
1986      for (j = 0; j < n; ++j) {
1987        cs->lookup[i*n + j] = (Guchar)*s++;
1988      }
1989    }
1990  } else {
1991    error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)");
1992    goto err3;
1993  }
1994  obj1.free();
1995  return cs;
1996
1997 err3:
1998  delete cs;
1999 err2:
2000  obj1.free();
2001 err1:
2002  return NULL;
2003}
2004
2005GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
2006                                               GfxColor *baseColor) {
2007  Guchar *p;
2008  double low[gfxColorMaxComps], range[gfxColorMaxComps];
2009  int n, i;
2010
2011  n = base->getNComps();
2012  base->getDefaultRanges(low, range, indexHigh);
2013  const int idx = (int)(colToDbl(color->c[0]) + 0.5) * n;
2014  if (likely(idx + n < (indexHigh + 1) * base->getNComps())) {
2015    p = &lookup[idx];
2016    for (i = 0; i < n; ++i) {
2017      baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
2018    }
2019  } else {
2020    for (i = 0; i < n; ++i) {
2021      baseColor->c[i] = 0;
2022    }
2023  }
2024  return baseColor;
2025}
2026
2027void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2028  GfxColor color2;
2029
2030  base->getGray(mapColorToBase(color, &color2), gray);
2031}
2032
2033void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2034  GfxColor color2;
2035
2036  base->getRGB(mapColorToBase(color, &color2), rgb);
2037}
2038
2039void GfxIndexedColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) {
2040  Guchar *line;
2041  int i, j, n;
2042
2043  n = base->getNComps();
2044  line = (Guchar *) gmallocn (length, n);
2045  for (i = 0; i < length; i++)
2046    for (j = 0; j < n; j++)
2047      line[i * n + j] = lookup[in[i] * n + j];
2048
2049  base->getRGBLine(line, out, length);
2050
2051  gfree (line);
2052}
2053
2054void GfxIndexedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
2055{
2056  Guchar *line;
2057  int i, j, n;
2058
2059  n = base->getNComps();
2060  line = (Guchar *) gmallocn (length, n);
2061  for (i = 0; i < length; i++)
2062    for (j = 0; j < n; j++)
2063      line[i * n + j] = lookup[in[i] * n + j];
2064
2065  base->getRGBLine(line, out, length);
2066
2067  gfree (line);
2068}
2069
2070void GfxIndexedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
2071{
2072  Guchar *line;
2073  int i, j, n;
2074
2075  n = base->getNComps();
2076  line = (Guchar *) gmallocn (length, n);
2077  for (i = 0; i < length; i++)
2078    for (j = 0; j < n; j++)
2079      line[i * n + j] = lookup[in[i] * n + j];
2080
2081  base->getRGBXLine(line, out, length);
2082
2083  gfree (line);
2084}
2085
2086void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2087  GfxColor color2;
2088
2089  base->getCMYK(mapColorToBase(color, &color2), cmyk);
2090}
2091
2092void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
2093  color->c[0] = 0;
2094}
2095
2096void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
2097                                            double *decodeRange,
2098                                            int maxImgPixel) {
2099  decodeLow[0] = 0;
2100  decodeRange[0] = maxImgPixel;
2101}
2102
2103//------------------------------------------------------------------------
2104// GfxSeparationColorSpace
2105//------------------------------------------------------------------------
2106
2107GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2108                                                 GfxColorSpace *altA,
2109                                                 Function *funcA) {
2110  name = nameA;
2111  alt = altA;
2112  func = funcA;
2113  nonMarking = !name->cmp("None");
2114  if (!name->cmp("Cyan")) {
2115    overprintMask = 0x01;
2116  } else if (!name->cmp("Magenta")) {
2117    overprintMask = 0x02;
2118  } else if (!name->cmp("Yellow")) {
2119    overprintMask = 0x04;
2120  } else if (!name->cmp("Black")) {
2121    overprintMask = 0x08;
2122  }
2123}
2124
2125GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2126                                                 GfxColorSpace *altA,
2127                                                 Function *funcA,
2128                                                 GBool nonMarkingA,
2129                                                 Guint overprintMaskA) {
2130  name = nameA;
2131  alt = altA;
2132  func = funcA;
2133  nonMarking = nonMarkingA;
2134  overprintMask = overprintMaskA;
2135}
2136
2137GfxSeparationColorSpace::~GfxSeparationColorSpace() {
2138  delete name;
2139  delete alt;
2140  delete func;
2141}
2142
2143GfxColorSpace *GfxSeparationColorSpace::copy() {
2144  return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
2145                                     nonMarking, overprintMask);
2146}
2147
2148//~ handle the 'All' and 'None' colorants
2149GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, Gfx *gfx, int recursion) {
2150  GfxSeparationColorSpace *cs;
2151  GooString *nameA;
2152  GfxColorSpace *altA;
2153  Function *funcA;
2154  Object obj1;
2155
2156  if (arr->getLength() != 4) {
2157    error(errSyntaxWarning, -1, "Bad Separation color space");
2158    goto err1;
2159  }
2160  if (!arr->get(1, &obj1)->isName()) {
2161    error(errSyntaxWarning, -1, "Bad Separation color space (name)");
2162    goto err2;
2163  }
2164  nameA = new GooString(obj1.getName());
2165  obj1.free();
2166  arr->get(2, &obj1);
2167  if (!(altA = GfxColorSpace::parse(&obj1, gfx, recursion + 1))) {
2168    error(errSyntaxWarning, -1, "Bad Separation color space (alternate color space)");
2169    goto err3;
2170  }
2171  obj1.free();
2172  arr->get(3, &obj1);
2173  if (!(funcA = Function::parse(&obj1))) {
2174    goto err4;
2175  }
2176  obj1.free();
2177  cs = new GfxSeparationColorSpace(nameA, altA, funcA);
2178  return cs;
2179
2180 err4:
2181  delete altA;
2182 err3:
2183  delete nameA;
2184 err2:
2185  obj1.free();
2186 err1:
2187  return NULL;
2188}
2189
2190void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2191  double x;
2192  double c[gfxColorMaxComps];
2193  GfxColor color2;
2194  int i;
2195
2196  x = colToDbl(color->c[0]);
2197  func->transform(&x, c);
2198  for (i = 0; i < alt->getNComps(); ++i) {
2199    color2.c[i] = dblToCol(c[i]);
2200  }
2201  alt->getGray(&color2, gray);
2202}
2203
2204void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2205  double x;
2206  double c[gfxColorMaxComps];
2207  GfxColor color2;
2208  int i;
2209
2210  x = colToDbl(color->c[0]);
2211  func->transform(&x, c);
2212  for (i = 0; i < alt->getNComps(); ++i) {
2213    color2.c[i] = dblToCol(c[i]);
2214  }
2215  alt->getRGB(&color2, rgb);
2216}
2217
2218void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2219  double x;
2220  double c[gfxColorMaxComps];
2221  GfxColor color2;
2222  int i;
2223
2224  x = colToDbl(color->c[0]);
2225  func->transform(&x, c);
2226  for (i = 0; i < alt->getNComps(); ++i) {
2227    color2.c[i] = dblToCol(c[i]);
2228  }
2229  alt->getCMYK(&color2, cmyk);
2230}
2231
2232void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
2233  color->c[0] = gfxColorComp1;
2234}
2235
2236//------------------------------------------------------------------------
2237// GfxDeviceNColorSpace
2238//------------------------------------------------------------------------
2239
2240GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
2241                                           GooString **namesA,
2242                                           GfxColorSpace *altA,
2243                                           Function *funcA) {
2244  int i;
2245
2246  nComps = nCompsA;
2247  alt = altA;
2248  func = funcA;
2249  nonMarking = gTrue;
2250  overprintMask = 0;
2251  for (i = 0; i < nComps; ++i) {
2252    names[i] = namesA[i];
2253    if (names[i]->cmp("None")) {
2254      nonMarking = gFalse;
2255    }
2256    if (!names[i]->cmp("Cyan")) {
2257      overprintMask |= 0x01;
2258    } else if (!names[i]->cmp("Magenta")) {
2259      overprintMask |= 0x02;
2260    } else if (!names[i]->cmp("Yellow")) {
2261      overprintMask |= 0x04;
2262    } else if (!names[i]->cmp("Black")) {
2263      overprintMask |= 0x08;
2264    } else {
2265      overprintMask = 0x0f;
2266    }
2267  }
2268}
2269
2270GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
2271                                           GooString **namesA,
2272                                           GfxColorSpace *altA,
2273                                           Function *funcA,
2274                                           GBool nonMarkingA,
2275                                           Guint overprintMaskA) {
2276  int i;
2277
2278  nComps = nCompsA;
2279  alt = altA;
2280  func = funcA;
2281  nonMarking = nonMarkingA;
2282  overprintMask = overprintMaskA;
2283  for (i = 0; i < nComps; ++i) {
2284    names[i] = namesA[i]->copy();
2285  }
2286}
2287
2288GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
2289  int i;
2290
2291  for (i = 0; i < nComps; ++i) {
2292    delete names[i];
2293  }
2294  delete alt;
2295  delete func;
2296}
2297
2298GfxColorSpace *GfxDeviceNColorSpace::copy() {
2299  return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
2300                                  nonMarking, overprintMask);
2301}
2302
2303//~ handle the 'None' colorant
2304GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, Gfx *gfx, int recursion) {
2305  GfxDeviceNColorSpace *cs;
2306  int nCompsA;
2307  GooString *namesA[gfxColorMaxComps];
2308  GfxColorSpace *altA;
2309  Function *funcA;
2310  Object obj1, obj2;
2311  int i;
2312
2313  if (arr->getLength() != 4 && arr->getLength() != 5) {
2314    error(errSyntaxWarning, -1, "Bad DeviceN color space");
2315    goto err1;
2316  }
2317  if (!arr->get(1, &obj1)->isArray()) {
2318    error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
2319    goto err2;
2320  }
2321  nCompsA = obj1.arrayGetLength();
2322  if (nCompsA > gfxColorMaxComps) {
2323    error(errSyntaxWarning, -1, "DeviceN color space with too many ({0:d} > {1:d}) components",
2324          nCompsA, gfxColorMaxComps);
2325    nCompsA = gfxColorMaxComps;
2326  }
2327  for (i = 0; i < nCompsA; ++i) {
2328    if (!obj1.arrayGet(i, &obj2)->isName()) {
2329      error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
2330      obj2.free();
2331      goto err2;
2332    }
2333    namesA[i] = new GooString(obj2.getName());
2334    obj2.free();
2335  }
2336  obj1.free();
2337  arr->get(2, &obj1);
2338  if (!(altA = GfxColorSpace::parse(&obj1, gfx, recursion + 1))) {
2339    error(errSyntaxWarning, -1, "Bad DeviceN color space (alternate color space)");
2340    goto err3;
2341  }
2342  obj1.free();
2343  arr->get(3, &obj1);
2344  if (!(funcA = Function::parse(&obj1))) {
2345    goto err4;
2346  }
2347  obj1.free();
2348  cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA);
2349  return cs;
2350
2351 err4:
2352  delete altA;
2353 err3:
2354  for (i = 0; i < nCompsA; ++i) {
2355    delete namesA[i];
2356  }
2357 err2:
2358  obj1.free();
2359 err1:
2360  return NULL;
2361}
2362
2363void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2364  double x[gfxColorMaxComps], c[gfxColorMaxComps];
2365  GfxColor color2;
2366  int i;
2367
2368  for (i = 0; i < nComps; ++i) {
2369    x[i] = colToDbl(color->c[i]);
2370  }
2371  func->transform(x, c);
2372  for (i = 0; i < alt->getNComps(); ++i) {
2373    color2.c[i] = dblToCol(c[i]);
2374  }
2375  alt->getGray(&color2, gray);
2376}
2377
2378void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2379  double x[gfxColorMaxComps], c[gfxColorMaxComps];
2380  GfxColor color2;
2381  int i;
2382
2383  for (i = 0; i < nComps; ++i) {
2384    x[i] = colToDbl(color->c[i]);
2385  }
2386  func->transform(x, c);
2387  for (i = 0; i < alt->getNComps(); ++i) {
2388    color2.c[i] = dblToCol(c[i]);
2389  }
2390  alt->getRGB(&color2, rgb);
2391}
2392
2393void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2394  double x[gfxColorMaxComps], c[gfxColorMaxComps];
2395  GfxColor color2;
2396  int i;
2397
2398  for (i = 0; i < nComps; ++i) {
2399    x[i] = colToDbl(color->c[i]);
2400  }
2401  func->transform(x, c);
2402  for (i = 0; i < alt->getNComps(); ++i) {
2403    color2.c[i] = dblToCol(c[i]);
2404  }
2405  alt->getCMYK(&color2, cmyk);
2406}
2407
2408void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
2409  int i;
2410
2411  for (i = 0; i < nComps; ++i) {
2412    color->c[i] = gfxColorComp1;
2413  }
2414}
2415
2416//------------------------------------------------------------------------
2417// GfxPatternColorSpace
2418//------------------------------------------------------------------------
2419
2420GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
2421  under = underA;
2422}
2423
2424GfxPatternColorSpace::~GfxPatternColorSpace() {
2425  if (under) {
2426    delete under;
2427  }
2428}
2429
2430GfxColorSpace *GfxPatternColorSpace::copy() {
2431  return new GfxPatternColorSpace(under ? under->copy() :
2432                                          (GfxColorSpace *)NULL);
2433}
2434
2435GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, Gfx *gfx, int recursion) {
2436  GfxPatternColorSpace *cs;
2437  GfxColorSpace *underA;
2438  Object obj1;
2439
2440  if (arr->getLength() != 1 && arr->getLength() != 2) {
2441    error(errSyntaxWarning, -1, "Bad Pattern color space");
2442    return NULL;
2443  }
2444  underA = NULL;
2445  if (arr->getLength() == 2) {
2446    arr->get(1, &obj1);
2447    if (!(underA = GfxColorSpace::parse(&obj1, gfx, recursion + 1))) {
2448      error(errSyntaxWarning, -1, "Bad Pattern color space (underlying color space)");
2449      obj1.free();
2450      return NULL;
2451    }
2452    obj1.free();
2453  }
2454  cs = new GfxPatternColorSpace(underA);
2455  return cs;
2456}
2457
2458void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2459  *gray = 0;
2460}
2461
2462void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2463  rgb->r = rgb->g = rgb->b = 0;
2464}
2465
2466void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2467  cmyk->c = cmyk->m = cmyk->y = 0;
2468  cmyk->k = 1;
2469}
2470
2471void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
2472  color->c[0]=0;
2473}
2474
2475//------------------------------------------------------------------------
2476// Pattern
2477//------------------------------------------------------------------------
2478
2479GfxPattern::GfxPattern(int typeA) {
2480  type = typeA;
2481}
2482
2483GfxPattern::~GfxPattern() {
2484}
2485
2486GfxPattern *GfxPattern::parse(Object *obj, Gfx *gfx) {
2487  GfxPattern *pattern;
2488  Object obj1;
2489
2490  if (obj->isDict()) {
2491    obj->dictLookup("PatternType", &obj1);
2492  } else if (obj->isStream()) {
2493    obj->streamGetDict()->lookup("PatternType", &obj1);
2494  } else {
2495    return NULL;
2496  }
2497  pattern = NULL;
2498  if (obj1.isInt() && obj1.getInt() == 1) {
2499    pattern = GfxTilingPattern::parse(obj);
2500  } else if (obj1.isInt() && obj1.getInt() == 2) {
2501    pattern = GfxShadingPattern::parse(obj, gfx);
2502  }
2503  obj1.free();
2504  return pattern;
2505}
2506
2507//------------------------------------------------------------------------
2508// GfxTilingPattern
2509//------------------------------------------------------------------------
2510
2511GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
2512  GfxTilingPattern *pat;
2513  Dict *dict;
2514  int paintTypeA, tilingTypeA;
2515  double bboxA[4], matrixA[6];
2516  double xStepA, yStepA;
2517  Object resDictA;
2518  Object obj1, obj2;
2519  int i;
2520
2521  if (!patObj->isStream()) {
2522    return NULL;
2523  }
2524  dict = patObj->streamGetDict();
2525
2526  if (dict->lookup("PaintType", &obj1)->isInt()) {
2527    paintTypeA = obj1.getInt();
2528  } else {
2529    paintTypeA = 1;
2530    error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
2531  }
2532  obj1.free();
2533  if (dict->lookup("TilingType", &obj1)->isInt()) {
2534    tilingTypeA = obj1.getInt();
2535  } else {
2536    tilingTypeA = 1;
2537    error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
2538  }
2539  obj1.free();
2540  bboxA[0] = bboxA[1] = 0;
2541  bboxA[2] = bboxA[3] = 1;
2542  if (dict->lookup("BBox", &obj1)->isArray() &&
2543      obj1.arrayGetLength() == 4) {
2544    for (i = 0; i < 4; ++i) {
2545      if (obj1.arrayGet(i, &obj2)->isNum()) {
2546        bboxA[i] = obj2.getNum();
2547      }
2548      obj2.free();
2549    }
2550  } else {
2551    error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern");
2552  }
2553  obj1.free();
2554  if (dict->lookup("XStep", &obj1)->isNum()) {
2555    xStepA = obj1.getNum();
2556  } else {
2557    xStepA = 1;
2558    error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern");
2559  }
2560  obj1.free();
2561  if (dict->lookup("YStep", &obj1)->isNum()) {
2562    yStepA = obj1.getNum();
2563  } else {
2564    yStepA = 1;
2565    error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern");
2566  }
2567  obj1.free();
2568  if (!dict->lookup("Resources", &resDictA)->isDict()) {
2569    resDictA.free();
2570    resDictA.initNull();
2571    error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern");
2572  }
2573  matrixA[0] = 1; matrixA[1] = 0;
2574  matrixA[2] = 0; matrixA[3] = 1;
2575  matrixA[4] = 0; matrixA[5] = 0;
2576  if (dict->lookup("Matrix", &obj1)->isArray() &&
2577      obj1.arrayGetLength() == 6) {
2578    for (i = 0; i < 6; ++i) {
2579      if (obj1.arrayGet(i, &obj2)->isNum()) {
2580        matrixA[i] = obj2.getNum();
2581      }
2582      obj2.free();
2583    }
2584  }
2585  obj1.free();
2586
2587  pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
2588                             &resDictA, matrixA, patObj);
2589  resDictA.free();
2590  return pat;
2591}
2592
2593GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
2594                                   double *bboxA, double xStepA, double yStepA,
2595                                   Object *resDictA, double *matrixA,
2596                                   Object *contentStreamA):
2597  GfxPattern(1)
2598{
2599  int i;
2600
2601  paintType = paintTypeA;
2602  tilingType = tilingTypeA;
2603  for (i = 0; i < 4; ++i) {
2604    bbox[i] = bboxA[i];
2605  }
2606  xStep = xStepA;
2607  yStep = yStepA;
2608  resDictA->copy(&resDict);
2609  for (i = 0; i < 6; ++i) {
2610    matrix[i] = matrixA[i];
2611  }
2612  contentStreamA->copy(&contentStream);
2613}
2614
2615GfxTilingPattern::~GfxTilingPattern() {
2616  resDict.free();
2617  contentStream.free();
2618}
2619
2620GfxPattern *GfxTilingPattern::copy() {
2621  return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
2622                              &resDict, matrix, &contentStream);
2623}
2624
2625//------------------------------------------------------------------------
2626// GfxShadingPattern
2627//------------------------------------------------------------------------
2628
2629GfxShadingPattern *GfxShadingPattern::parse(Object *patObj, Gfx *gfx) {
2630  Dict *dict;
2631  GfxShading *shadingA;
2632  double matrixA[6];
2633  Object obj1, obj2;
2634  int i;
2635
2636  if (!patObj->isDict()) {
2637    return NULL;
2638  }
2639  dict = patObj->getDict();
2640
2641  dict->lookup("Shading", &obj1);
2642  shadingA = GfxShading::parse(&obj1, gfx);
2643  obj1.free();
2644  if (!shadingA) {
2645    return NULL;
2646  }
2647
2648  matrixA[0] = 1; matrixA[1] = 0;
2649  matrixA[2] = 0; matrixA[3] = 1;
2650  matrixA[4] = 0; matrixA[5] = 0;
2651  if (dict->lookup("Matrix", &obj1)->isArray() &&
2652      obj1.arrayGetLength() == 6) {
2653    for (i = 0; i < 6; ++i) {
2654      if (obj1.arrayGet(i, &obj2)->isNum()) {
2655        matrixA[i] = obj2.getNum();
2656      }
2657      obj2.free();
2658    }
2659  }
2660  obj1.free();
2661
2662  return new GfxShadingPattern(shadingA, matrixA);
2663}
2664
2665GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
2666  GfxPattern(2)
2667{
2668  int i;
2669
2670  shading = shadingA;
2671  for (i = 0; i < 6; ++i) {
2672    matrix[i] = matrixA[i];
2673  }
2674}
2675
2676GfxShadingPattern::~GfxShadingPattern() {
2677  delete shading;
2678}
2679
2680GfxPattern *GfxShadingPattern::copy() {
2681  return new GfxShadingPattern(shading->copy(), matrix);
2682}
2683
2684//------------------------------------------------------------------------
2685// GfxShading
2686//------------------------------------------------------------------------
2687
2688GfxShading::GfxShading(int typeA) {
2689  type = typeA;
2690  colorSpace = NULL;
2691}
2692
2693GfxShading::GfxShading(GfxShading *shading) {
2694  int i;
2695
2696  type = shading->type;
2697  colorSpace = shading->colorSpace->copy();
2698  for (i = 0; i < gfxColorMaxComps; ++i) {
2699    background.c[i] = shading->background.c[i];
2700  }
2701  hasBackground = shading->hasBackground;
2702  xMin = shading->xMin;
2703  yMin = shading->yMin;
2704  xMax = shading->xMax;
2705  yMax = shading->yMax;
2706  hasBBox = shading->hasBBox;
2707}
2708
2709GfxShading::~GfxShading() {
2710  if (colorSpace) {
2711    delete colorSpace;
2712  }
2713}
2714
2715GfxShading *GfxShading::parse(Object *obj, Gfx *gfx) {
2716  GfxShading *shading;
2717  Dict *dict;
2718  int typeA;
2719  Object obj1;
2720
2721  if (obj->isDict()) {
2722    dict = obj->getDict();
2723  } else if (obj->isStream()) {
2724    dict = obj->streamGetDict();
2725  } else {
2726    return NULL;
2727  }
2728
2729  if (!dict->lookup("ShadingType", &obj1)->isInt()) {
2730    error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary");
2731    obj1.free();
2732    return NULL;
2733  }
2734  typeA = obj1.getInt();
2735  obj1.free();
2736
2737  switch (typeA) {
2738  case 1:
2739    shading = GfxFunctionShading::parse(dict, gfx);
2740    break;
2741  case 2:
2742    shading = GfxAxialShading::parse(dict, gfx);
2743    break;
2744  case 3:
2745    shading = GfxRadialShading::parse(dict, gfx);
2746    break;
2747  case 4:
2748    if (obj->isStream()) {
2749      shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream(), gfx);
2750    } else {
2751      error(errSyntaxWarning, -1, "Invalid Type 4 shading object");
2752      goto err1;
2753    }
2754    break;
2755  case 5:
2756    if (obj->isStream()) {
2757      shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream(), gfx);
2758    } else {
2759      error(errSyntaxWarning, -1, "Invalid Type 5 shading object");
2760      goto err1;
2761    }
2762    break;
2763  case 6:
2764    if (obj->isStream()) {
2765      shading = GfxPatchMeshShading::parse(6, dict, obj->getStream(), gfx);
2766    } else {
2767      error(errSyntaxWarning, -1, "Invalid Type 6 shading object");
2768      goto err1;
2769    }
2770    break;
2771  case 7:
2772    if (obj->isStream()) {
2773      shading = GfxPatchMeshShading::parse(7, dict, obj->getStream(), gfx);
2774    } else {
2775      error(errSyntaxWarning, -1, "Invalid Type 7 shading object");
2776      goto err1;
2777    }
2778    break;
2779  default:
2780    error(errSyntaxWarning, -1, "Unimplemented shading type {0:d}", typeA);
2781    goto err1;
2782  }
2783
2784  return shading;
2785
2786 err1:
2787  return NULL;
2788}
2789
2790GBool GfxShading::init(Dict *dict, Gfx *gfx) {
2791  Object obj1, obj2;
2792  int i;
2793
2794  dict->lookup("ColorSpace", &obj1);
2795  if (!(colorSpace = GfxColorSpace::parse(&obj1, gfx))) {
2796    error(errSyntaxWarning, -1, "Bad color space in shading dictionary");
2797    obj1.free();
2798    return gFalse;
2799  }
2800  obj1.free();
2801
2802  for (i = 0; i < gfxColorMaxComps; ++i) {
2803    background.c[i] = 0;
2804  }
2805  hasBackground = gFalse;
2806  if (dict->lookup("Background", &obj1)->isArray()) {
2807    if (obj1.arrayGetLength() == colorSpace->getNComps()) {
2808      hasBackground = gTrue;
2809      for (i = 0; i < colorSpace->getNComps(); ++i) {
2810        background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
2811        obj2.free();
2812      }
2813    } else {
2814      error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
2815    }
2816  }
2817  obj1.free();
2818
2819  xMin = yMin = xMax = yMax = 0;
2820  hasBBox = gFalse;
2821  if (dict->lookup("BBox", &obj1)->isArray()) {
2822    if (obj1.arrayGetLength() == 4) {
2823      Object obj3, obj4, obj5;
2824      obj1.arrayGet(0, &obj2);
2825      obj1.arrayGet(1, &obj3);
2826      obj1.arrayGet(2, &obj4);
2827      obj1.arrayGet(3, &obj5);
2828      if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum())
2829      {
2830        hasBBox = gTrue;
2831        xMin = obj2.getNum();
2832        yMin = obj3.getNum();
2833        xMax = obj4.getNum();
2834        yMax = obj5.getNum();
2835      } else {
2836        error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)");
2837      }
2838      obj2.free();
2839      obj3.free();
2840      obj4.free();
2841      obj5.free();
2842    } else {
2843      error(errSyntaxWarning, -1, "Bad BBox in shading dictionary");
2844    }
2845  }
2846  obj1.free();
2847
2848  return gTrue;
2849}
2850
2851//------------------------------------------------------------------------
2852// GfxFunctionShading
2853//------------------------------------------------------------------------
2854
2855GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
2856                                       double x1A, double y1A,
2857                                       double *matrixA,
2858                                       Function **funcsA, int nFuncsA):
2859  GfxShading(1)
2860{
2861  int i;
2862
2863  x0 = x0A;
2864  y0 = y0A;
2865  x1 = x1A;
2866  y1 = y1A;
2867  for (i = 0; i < 6; ++i) {
2868    matrix[i] = matrixA[i];
2869  }
2870  nFuncs = nFuncsA;
2871  for (i = 0; i < nFuncs; ++i) {
2872    funcs[i] = funcsA[i];
2873  }
2874}
2875
2876GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
2877  GfxShading(shading)
2878{
2879  int i;
2880
2881  x0 = shading->x0;
2882  y0 = shading->y0;
2883  x1 = shading->x1;
2884  y1 = shading->y1;
2885  for (i = 0; i < 6; ++i) {
2886    matrix[i] = shading->matrix[i];
2887  }
2888  nFuncs = shading->nFuncs;
2889  for (i = 0; i < nFuncs; ++i) {
2890    funcs[i] = shading->funcs[i]->copy();
2891  }
2892}
2893
2894GfxFunctionShading::~GfxFunctionShading() {
2895  int i;
2896
2897  for (i = 0; i < nFuncs; ++i) {
2898    delete funcs[i];
2899  }
2900}
2901
2902GfxFunctionShading *GfxFunctionShading::parse(Dict *dict, Gfx *gfx) {
2903  GfxFunctionShading *shading;
2904  double x0A, y0A, x1A, y1A;
2905  double matrixA[6];
2906  Function *funcsA[gfxColorMaxComps];
2907  int nFuncsA;
2908  Object obj1, obj2;
2909  int i;
2910
2911  x0A = y0A = 0;
2912  x1A = y1A = 1;
2913  if (dict->lookup("Domain", &obj1)->isArray() &&
2914      obj1.arrayGetLength() == 4) {
2915    x0A = obj1.arrayGet(0, &obj2)->getNum();
2916    obj2.free();
2917    x1A = obj1.arrayGet(1, &obj2)->getNum();
2918    obj2.free();
2919    y0A = obj1.arrayGet(2, &obj2)->getNum();
2920    obj2.free();
2921    y1A = obj1.arrayGet(3, &obj2)->getNum();
2922    obj2.free();
2923  }
2924  obj1.free();
2925
2926  matrixA[0] = 1; matrixA[1] = 0;
2927  matrixA[2] = 0; matrixA[3] = 1;
2928  matrixA[4] = 0; matrixA[5] = 0;
2929  if (dict->lookup("Matrix", &obj1)->isArray() &&
2930      obj1.arrayGetLength() == 6) {
2931    matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
2932    obj2.free();
2933    matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
2934    obj2.free();
2935    matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
2936    obj2.free();
2937    matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
2938    obj2.free();
2939    matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
2940    obj2.free();
2941    matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
2942    obj2.free();
2943  }
2944  obj1.free();
2945
2946  dict->lookup("Function", &obj1);
2947  if (obj1.isArray()) {
2948    nFuncsA = obj1.arrayGetLength();
2949    if (nFuncsA > gfxColorMaxComps) {
2950      error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
2951      goto err1;
2952    }
2953    for (i = 0; i < nFuncsA; ++i) {
2954      obj1.arrayGet(i, &obj2);
2955      if (!(funcsA[i] = Function::parse(&obj2))) {
2956        goto err2;
2957      }
2958      obj2.free();
2959    }
2960  } else {
2961    nFuncsA = 1;
2962    if (!(funcsA[0] = Function::parse(&obj1))) {
2963      goto err1;
2964    }
2965  }
2966  obj1.free();
2967
2968  shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
2969                                   funcsA, nFuncsA);
2970  if (!shading->init(dict, gfx)) {
2971    delete shading;
2972    return NULL;
2973  }
2974  return shading;
2975
2976 err2:
2977  obj2.free();
2978 err1:
2979  obj1.free();
2980  return NULL;
2981}
2982
2983GfxShading *GfxFunctionShading::copy() {
2984  return new GfxFunctionShading(this);
2985}
2986
2987void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
2988  double in[2], out[gfxColorMaxComps];
2989  int i;
2990
2991  // NB: there can be one function with n outputs or n functions with
2992  // one output each (where n = number of color components)
2993  for (i = 0; i < gfxColorMaxComps; ++i) {
2994    out[i] = 0;
2995  }
2996  in[0] = x;
2997  in[1] = y;
2998  for (i = 0; i < nFuncs; ++i) {
2999    funcs[i]->transform(in, &out[i]);
3000  }
3001  for (i = 0; i < gfxColorMaxComps; ++i) {
3002    color->c[i] = dblToCol(out[i]);
3003  }
3004}
3005
3006//------------------------------------------------------------------------
3007// GfxUnivariateShading
3008//------------------------------------------------------------------------
3009
3010GfxUnivariateShading::GfxUnivariateShading(int typeA,
3011                                           double t0A, double t1A,
3012                                           Function **funcsA, int nFuncsA,
3013                                           GBool extend0A, GBool extend1A):
3014  GfxShading(typeA)
3015{
3016  int i;
3017
3018  t0 = t0A;
3019  t1 = t1A;
3020  nFuncs = nFuncsA;
3021  for (i = 0; i < nFuncs; ++i) {
3022    funcs[i] = funcsA[i];
3023  }
3024  extend0 = extend0A;
3025  extend1 = extend1A;
3026
3027  cacheSize = 0;
3028  lastMatch = 0;
3029  cacheBounds = NULL;
3030  cacheCoeff = NULL;
3031  cacheValues = NULL;
3032}
3033
3034GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading *shading):
3035  GfxShading(shading)
3036{
3037  int i;
3038
3039  t0 = shading->t0;
3040  t1 = shading->t1;
3041  nFuncs = shading->nFuncs;
3042  for (i = 0; i < nFuncs; ++i) {
3043    funcs[i] = shading->funcs[i]->copy();
3044  }
3045  extend0 = shading->extend0;
3046  extend1 = shading->extend1;
3047
3048  cacheSize = 0;
3049  lastMatch = 0;
3050  cacheBounds = NULL;
3051  cacheCoeff = NULL;
3052  cacheValues = NULL;
3053}
3054
3055GfxUnivariateShading::~GfxUnivariateShading() {
3056  int i;
3057
3058  for (i = 0; i < nFuncs; ++i) {
3059    delete funcs[i];
3060  }
3061
3062  gfree (cacheBounds);
3063}
3064
3065void GfxUnivariateShading::getColor(double t, GfxColor *color) {
3066  double out[gfxColorMaxComps];
3067  int i, nComps;
3068
3069  // NB: there can be one function with n outputs or n functions with
3070  // one output each (where n = number of color components)
3071  nComps = nFuncs * funcs[0]->getOutputSize();
3072
3073  if (cacheSize > 0) {
3074    double x, ix, *l, *u, *upper;
3075
3076    if (cacheBounds[lastMatch - 1] >= t) {
3077      upper = std::lower_bound (cacheBounds, cacheBounds + lastMatch - 1, t);
3078      lastMatch = upper - cacheBounds;
3079      lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3080    } else if (cacheBounds[lastMatch] < t) {
3081      upper = std::lower_bound (cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t);
3082      lastMatch = upper - cacheBounds;
3083      lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
3084    }
3085
3086    x = (t - cacheBounds[lastMatch-1]) * cacheCoeff[lastMatch];
3087    ix = 1.0 - x;
3088    u = cacheValues + lastMatch * nComps;
3089    l = u - nComps;
3090
3091    for (i = 0; i < nComps; ++i) {
3092      out[i] = ix * l[i] + x * u[i];
3093    }
3094  } else {
3095    for (i = 0; i < nComps; ++i) {
3096      out[i] = 0;
3097    }
3098    for (i = 0; i < nFuncs; ++i) {
3099      funcs[i]->transform(&t, &out[i]);
3100    }
3101  }
3102
3103  for (i = 0; i < nComps; ++i) {
3104    color->c[i] = dblToCol(out[i]);
3105  }
3106}
3107
3108void GfxUnivariateShading::setupCache(const Matrix *ctm,
3109                                      double xMin, double yMin,
3110                                      double xMax, double yMax) {
3111  double sMin, sMax, tMin, tMax, upperBound;
3112  int i, j, nComps, maxSize;
3113
3114  gfree (cacheBounds);
3115  cacheBounds = NULL;
3116  cacheSize = 0;
3117
3118  // NB: there can be one function with n outputs or n functions with
3119  // one output each (where n = number of color components)
3120  nComps = nFuncs * funcs[0]->getOutputSize();
3121
3122  getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
3123  upperBound = ctm->norm() * getDistance(sMin, sMax);
3124  maxSize = ceil(upperBound);
3125  maxSize = std::max<int>(maxSize, 2);
3126
3127  {
3128    double x[4], y[4];
3129
3130    ctm->transform(xMin, yMin, &x[0], &y[0]);
3131    ctm->transform(xMax, yMin, &x[1], &y[1]);
3132    ctm->transform(xMin, yMax, &x[2], &y[2]);
3133    ctm->transform(xMax, yMax, &x[3], &y[3]);
3134
3135    xMin = xMax = x[0];
3136    yMin = yMax = y[0];
3137    for (i = 1; i < 4; i++) {
3138      xMin = std::min<double>(xMin, x[i]);
3139      yMin = std::min<double>(yMin, y[i]);
3140      xMax = std::max<double>(xMax, x[i]);
3141      yMax = std::max<double>(yMax, y[i]);
3142    }
3143  }
3144
3145  if (maxSize > (xMax-xMin) * (yMax-yMin)) {
3146    return;
3147  }
3148
3149  if (t0 < t1) {
3150    tMin = t0 + sMin * (t1 - t0);
3151    tMax = t0 + sMax * (t1 - t0);
3152  } else {
3153    tMin = t0 + sMax * (t1 - t0);
3154    tMax = t0 + sMin * (t1 - t0);
3155  }
3156
3157  cacheBounds = (double *)gmallocn(maxSize, sizeof(double) * (nComps + 2));
3158  cacheCoeff = cacheBounds + maxSize;
3159  cacheValues = cacheCoeff + maxSize;
3160
3161  if (cacheSize != 0) {
3162    for (j = 0; j < cacheSize; ++j) {
3163      cacheCoeff[j] = 1 / (cacheBounds[j+1] - cacheBounds[j]);
3164    }
3165  } else if (tMax != tMin) {
3166    double step = (tMax - tMin) / (maxSize - 1);
3167    double coeff = (maxSize - 1) / (tMax - tMin);
3168
3169    cacheSize = maxSize;
3170
3171    for (j = 0; j < cacheSize; ++j) {
3172      cacheBounds[j] = tMin + j * step;
3173      cacheCoeff[j] = coeff;
3174
3175      for (i = 0; i < nComps; ++i) {
3176        cacheValues[j*nComps + i] = 0;
3177      }
3178      for (i = 0; i < nFuncs; ++i) {
3179        funcs[i]->transform(&cacheBounds[j], &cacheValues[j*nComps + i]);
3180      }
3181    }
3182  }
3183
3184  lastMatch = 1;
3185}
3186
3187
3188//------------------------------------------------------------------------
3189// GfxAxialShading
3190//------------------------------------------------------------------------
3191
3192GfxAxialShading::GfxAxialShading(double x0A, double y0A,
3193                                 double x1A, double y1A,
3194                                 double t0A, double t1A,
3195                                 Function **funcsA, int nFuncsA,
3196                                 GBool extend0A, GBool extend1A):
3197  GfxUnivariateShading(2, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
3198{
3199  x0 = x0A;
3200  y0 = y0A;
3201  x1 = x1A;
3202  y1 = y1A;
3203}
3204
3205GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
3206  GfxUnivariateShading(shading)
3207{
3208  x0 = shading->x0;
3209  y0 = shading->y0;
3210  x1 = shading->x1;
3211  y1 = shading->y1;
3212}
3213
3214GfxAxialShading::~GfxAxialShading() {
3215}
3216
3217GfxAxialShading *GfxAxialShading::parse(Dict *dict, Gfx *gfx) {
3218  GfxAxialShading *shading;
3219  double x0A, y0A, x1A, y1A;
3220  double t0A, t1A;
3221  Function *funcsA[gfxColorMaxComps];
3222  int nFuncsA;
3223  GBool extend0A, extend1A;
3224  Object obj1, obj2;
3225  int i;
3226
3227  x0A = y0A = x1A = y1A = 0;
3228  if (dict->lookup("Coords", &obj1)->isArray() &&
3229      obj1.arrayGetLength() == 4) {
3230    Object obj3, obj4, obj5;
3231    obj1.arrayGet(0, &obj2);
3232    obj1.arrayGet(1, &obj3);
3233    obj1.arrayGet(2, &obj4);
3234    obj1.arrayGet(3, &obj5);
3235    if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum()) {
3236      x0A = obj2.getNum();
3237      y0A = obj3.getNum();
3238      x1A = obj4.getNum();
3239      y1A = obj5.getNum();
3240    }
3241    obj2.free();
3242    obj3.free();
3243    obj4.free();
3244    obj5.free();
3245  } else {
3246    error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
3247    goto err1;
3248  }
3249  obj1.free();
3250
3251  t0A = 0;
3252  t1A = 1;
3253  if (dict->lookup("Domain", &obj1)->isArray() &&
3254      obj1.arrayGetLength() == 2) {
3255    Object obj3;
3256    obj1.arrayGet(0, &obj2);
3257    obj1.arrayGet(1, &obj3);
3258    if (obj2.isNum() && obj3.isNum()) {
3259      t0A = obj2.getNum();
3260      t1A = obj3.getNum();
3261    }
3262    obj2.free();
3263    obj3.free();
3264  }
3265  obj1.free();
3266
3267  dict->lookup("Function", &obj1);
3268  if (obj1.isArray()) {
3269    nFuncsA = obj1.arrayGetLength();
3270    if (nFuncsA > gfxColorMaxComps) {
3271      error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3272      goto err1;
3273    }
3274    for (i = 0; i < nFuncsA; ++i) {
3275      obj1.arrayGet(i, &obj2);
3276      if (!(funcsA[i] = Function::parse(&obj2))) {
3277        obj1.free();
3278        obj2.free();
3279        goto err1;
3280      }
3281      obj2.free();
3282    }
3283  } else {
3284    nFuncsA = 1;
3285    if (!(funcsA[0] = Function::parse(&obj1))) {
3286      obj1.free();
3287      goto err1;
3288    }
3289  }
3290  obj1.free();
3291
3292  extend0A = extend1A = gFalse;
3293  if (dict->lookup("Extend", &obj1)->isArray() &&
3294      obj1.arrayGetLength() == 2) {
3295    extend0A = obj1.arrayGet(0, &obj2)->getBool();
3296    obj2.free();
3297    extend1A = obj1.arrayGet(1, &obj2)->getBool();
3298    obj2.free();
3299  }
3300  obj1.free();
3301
3302  shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
3303                                funcsA, nFuncsA, extend0A, extend1A);
3304  if (!shading->init(dict, gfx)) {
3305    delete shading;
3306    return NULL;
3307  }
3308  return shading;
3309
3310 err1:
3311  return NULL;
3312}
3313
3314GfxShading *GfxAxialShading::copy() {
3315  return new GfxAxialShading(this);
3316}
3317
3318double GfxAxialShading::getDistance(double sMin, double sMax) {
3319  double xMin, yMin, xMax, yMax;
3320
3321  xMin = x0 + sMin * (x1 - x0);
3322  yMin = y0 + sMin * (y1 - y0);
3323  xMax = x0 + sMax * (x1 - x0);
3324  yMax = y0 + sMax * (y1 - y0);
3325
3326  return hypot(xMax-xMin, yMax-yMin);
3327}
3328
3329void GfxAxialShading::getParameterRange(double *lower, double *upper,
3330                                        double xMin, double yMin,
3331                                        double xMax, double yMax) {
3332  double pdx, pdy, invsqnorm, tdx, tdy, t, range[2];
3333
3334  // Linear gradients are orthogonal to the line passing through their
3335  // extremes. Because of convexity, the parameter range can be
3336  // computed as the convex hull (one the real line) of the parameter
3337  // values of the 4 corners of the box.
3338  //
3339  // The parameter value t for a point (x,y) can be computed as:
3340  //
3341  //   t = (p2 - p1) . (x,y) / |p2 - p1|^2
3342  //
3343  // t0  is the t value for the top left corner
3344  // tdx is the difference between left and right corners
3345  // tdy is the difference between top and bottom corners
3346
3347  pdx = x1 - x0;
3348  pdy = y1 - y0;
3349  invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
3350  pdx *= invsqnorm;
3351  pdy *= invsqnorm;
3352
3353  t = (xMin - x0) * pdx + (yMin - y0) * pdy;
3354  tdx = (xMax - xMin) * pdx;
3355  tdy = (yMax - yMin) * pdy;
3356
3357  // Because of the linearity of the t value, tdx can simply be added
3358  // the t0 to move along the top edge. After this, *lower and *upper
3359  // represent the parameter range for the top edge, so extending it
3360  // to include the whole box simply requires adding tdy to the
3361  // correct extreme.
3362
3363  range[0] = range[1] = t;
3364  if (tdx < 0)
3365    range[0] += tdx;
3366  else
3367    range[1] += tdx;
3368
3369  if (tdy < 0)
3370    range[0] += tdy;
3371  else
3372    range[1] += tdy;
3373
3374  *lower = std::max<double>(0., std::min<double>(1., range[0]));
3375  *upper = std::max<double>(0., std::min<double>(1., range[1]));
3376}                                       
3377
3378//------------------------------------------------------------------------
3379// GfxRadialShading
3380//------------------------------------------------------------------------
3381
3382#ifndef RADIAL_EPSILON
3383#define RADIAL_EPSILON  (1. / 1024 / 1024)
3384#endif
3385
3386GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
3387                                   double x1A, double y1A, double r1A,
3388                                   double t0A, double t1A,
3389                                   Function **funcsA, int nFuncsA,
3390                                   GBool extend0A, GBool extend1A):
3391  GfxUnivariateShading(3, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
3392{
3393  x0 = x0A;
3394  y0 = y0A;
3395  r0 = r0A;
3396  x1 = x1A;
3397  y1 = y1A;
3398  r1 = r1A;
3399}
3400
3401GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
3402  GfxUnivariateShading(shading)
3403{
3404  x0 = shading->x0;
3405  y0 = shading->y0;
3406  r0 = shading->r0;
3407  x1 = shading->x1;
3408  y1 = shading->y1;
3409  r1 = shading->r1;
3410}
3411
3412GfxRadialShading::~GfxRadialShading() {
3413}
3414
3415GfxRadialShading *GfxRadialShading::parse(Dict *dict, Gfx *gfx) {
3416  GfxRadialShading *shading;
3417  double x0A, y0A, r0A, x1A, y1A, r1A;
3418  double t0A, t1A;
3419  Function *funcsA[gfxColorMaxComps];
3420  int nFuncsA;
3421  GBool extend0A, extend1A;
3422  Object obj1, obj2;
3423  int i;
3424
3425  x0A = y0A = r0A = x1A = y1A = r1A = 0;
3426  if (dict->lookup("Coords", &obj1)->isArray() &&
3427      obj1.arrayGetLength() == 6) {
3428    x0A = obj1.arrayGet(0, &obj2)->getNum();
3429    obj2.free();
3430    y0A = obj1.arrayGet(1, &obj2)->getNum();
3431    obj2.free();
3432    r0A = obj1.arrayGet(2, &obj2)->getNum();
3433    obj2.free();
3434    x1A = obj1.arrayGet(3, &obj2)->getNum();
3435    obj2.free();
3436    y1A = obj1.arrayGet(4, &obj2)->getNum();
3437    obj2.free();
3438    r1A = obj1.arrayGet(5, &obj2)->getNum();
3439    obj2.free();
3440  } else {
3441    error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
3442    goto err1;
3443  }
3444  obj1.free();
3445
3446  t0A = 0;
3447  t1A = 1;
3448  if (dict->lookup("Domain", &obj1)->isArray() &&
3449      obj1.arrayGetLength() == 2) {
3450    t0A = obj1.arrayGet(0, &obj2)->getNum();
3451    obj2.free();
3452    t1A = obj1.arrayGet(1, &obj2)->getNum();
3453    obj2.free();
3454  }
3455  obj1.free();
3456
3457  dict->lookup("Function", &obj1);
3458  if (obj1.isArray()) {
3459    nFuncsA = obj1.arrayGetLength();
3460    if (nFuncsA > gfxColorMaxComps) {
3461      error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3462      goto err1;
3463    }
3464    for (i = 0; i < nFuncsA; ++i) {
3465      obj1.arrayGet(i, &obj2);
3466      if (!(funcsA[i] = Function::parse(&obj2))) {
3467        obj1.free();
3468        obj2.free();
3469        goto err1;
3470      }
3471      obj2.free();
3472    }
3473  } else {
3474    nFuncsA = 1;
3475    if (!(funcsA[0] = Function::parse(&obj1))) {
3476      obj1.free();
3477      goto err1;
3478    }
3479  }
3480  obj1.free();
3481
3482  extend0A = extend1A = gFalse;
3483  if (dict->lookup("Extend", &obj1)->isArray() &&
3484      obj1.arrayGetLength() == 2) {
3485    extend0A = obj1.arrayGet(0, &obj2)->getBool();
3486    obj2.free();
3487    extend1A = obj1.arrayGet(1, &obj2)->getBool();
3488    obj2.free();
3489  }
3490  obj1.free();
3491
3492  shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
3493                                 funcsA, nFuncsA, extend0A, extend1A);
3494  if (!shading->init(dict, gfx)) {
3495    delete shading;
3496    return NULL;
3497  }
3498  return shading;
3499
3500 err1:
3501  return NULL;
3502}
3503
3504GfxShading *GfxRadialShading::copy() {
3505  return new GfxRadialShading(this);
3506}
3507
3508double GfxRadialShading::getDistance(double sMin, double sMax) {
3509  double xMin, yMin, rMin, xMax, yMax, rMax;
3510
3511  xMin = x0 + sMin * (x1 - x0);
3512  yMin = y0 + sMin * (y1 - y0);
3513  rMin = r0 + sMin * (r1 - r0);
3514
3515  xMax = x0 + sMax * (x1 - x0);
3516  yMax = y0 + sMax * (y1 - y0);
3517  rMax = r0 + sMax * (r1 - r0);
3518
3519  return hypot(xMax-xMin, yMax-yMin) + fabs(rMax-rMin);
3520}
3521
3522// extend range, adapted from cairo, radialExtendRange
3523static GBool
3524radialExtendRange (double range[2], double value, GBool valid)
3525{
3526  if (!valid)
3527    range[0] = range[1] = value;
3528  else if (value < range[0])
3529    range[0] = value;
3530  else if (value > range[1])
3531    range[1] = value;
3532
3533  return gTrue;
3534}
3535
3536inline void radialEdge(double num, double den, double delta, double lower, double upper,
3537                                           double dr, double mindr, GBool &valid, double *range)
3538{ 
3539  if (fabs (den) >= RADIAL_EPSILON) {                                   
3540    double t_edge, v;                                                                                                                           
3541    t_edge = (num) / (den);                                             
3542    v = t_edge * (delta);                                               
3543    if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))           
3544      valid = radialExtendRange (range, t_edge, valid);                 
3545  }
3546}
3547
3548inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr, 
3549                                           double dr, double mindr, GBool &valid, double *range)
3550{
3551    b = (x) * dx + (y) * dy + cr * dr;                         
3552    if (fabs (b) >= RADIAL_EPSILON) {                           
3553      double t_corner;                                         
3554      double x2 = (x) * (x);                                   
3555      double y2 = (y) * (y);                                   
3556      double cr2 = (cr) * (cr);                                 
3557      double c = x2 + y2 - cr2;                                 
3558                                                               
3559      t_corner = 0.5 * c / b;                                   
3560      if (t_corner * dr >= mindr)                               
3561        valid = radialExtendRange (range, t_corner, valid);     
3562    }
3563}
3564
3565inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr,
3566                                           double inva, double dr, double mindr, GBool &valid, double *range)
3567{
3568    b = (x) * dx + (y) * dy + cr * dr;                         
3569    c = (x) * (x) + (y) * (y) - cr * cr;                       
3570    d = b * b - a * c;                                         
3571    if (d >= 0) {                                               
3572      double t_corner;                                         
3573                                                               
3574      d = sqrt (d);                                             
3575      t_corner = (b + d) * inva;                               
3576      if (t_corner * dr >= mindr)                               
3577        valid = radialExtendRange (range, t_corner, valid);     
3578      t_corner = (b - d) * inva;                               
3579      if (t_corner * dr >= mindr)                               
3580        valid = radialExtendRange (range, t_corner, valid);     
3581    }
3582}
3583void GfxRadialShading::getParameterRange(double *lower, double *upper,
3584                                         double xMin, double yMin,
3585                                         double xMax, double yMax) {
3586  double cx, cy, cr, dx, dy, dr;
3587  double a, x_focus, y_focus;
3588  double mindr, minx, miny, maxx, maxy;
3589  double range[2];
3590  GBool valid;
3591
3592  // A radial pattern is considered degenerate if it can be
3593  // represented as a solid or clear pattern.  This corresponds to one
3594  // of the two cases:
3595  //
3596  // 1) The radii are both very small:
3597  //      |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
3598  //
3599  // 2) The two circles have about the same radius and are very
3600  //    close to each other (approximately a cylinder gradient that
3601  //    doesn't move with the parameter):
3602  //      |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
3603
3604  if (xMin >= xMax || yMin >=yMax || 
3605      (fabs (r0 - r1) < RADIAL_EPSILON &&
3606       (std::min<double>(r0, r1) < RADIAL_EPSILON ||
3607        std::max<double>(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) {
3608    *lower = *upper = 0;
3609    return;
3610  }
3611
3612  range[0] = range[1] = 0;
3613  valid = gFalse;
3614
3615  x_focus = y_focus = 0; // silence gcc
3616
3617  cx = x0;
3618  cy = y0;
3619  cr = r0;
3620  dx = x1 - cx;
3621  dy = y1 - cy;
3622  dr = r1 - cr;
3623
3624  // translate by -(cx, cy) to simplify computations
3625  xMin -= cx;
3626  yMin -= cy;
3627  xMax -= cx;
3628  yMax -= cy;
3629
3630  // enlarge boundaries slightly to avoid rounding problems in the
3631  // parameter range computation
3632  xMin -= RADIAL_EPSILON;
3633  yMin -= RADIAL_EPSILON;
3634  xMax += RADIAL_EPSILON;
3635  yMax += RADIAL_EPSILON;
3636
3637  // enlarge boundaries even more to avoid rounding problems when
3638  // testing if a point belongs to the box
3639  minx = xMin - RADIAL_EPSILON;
3640  miny = yMin - RADIAL_EPSILON;
3641  maxx = xMax + RADIAL_EPSILON;
3642  maxy = yMax + RADIAL_EPSILON;
3643
3644  // we dont' allow negative radiuses, so we will be checking that
3645  // t*dr >= mindr to consider t valid
3646  mindr = -(cr + RADIAL_EPSILON);
3647
3648  // After the previous transformations, the start circle is centered
3649  // in the origin and has radius cr. A 1-unit change in the t
3650  // parameter corresponds to dx,dy,dr changes in the x,y,r of the
3651  // circle (center coordinates, radius).
3652  //
3653  // To compute the minimum range needed to correctly draw the
3654  // pattern, we start with an empty range and extend it to include
3655  // the circles touching the bounding box or within it.
3656   
3657  // Focus, the point where the circle has radius == 0.
3658  //
3659  // r = cr + t * dr = 0
3660  // t = -cr / dr
3661  //
3662  // If the radius is constant (dr == 0) there is no focus (the
3663  // gradient represents a cylinder instead of a cone).
3664  if (fabs (dr) >= RADIAL_EPSILON) {
3665    double t_focus;
3666
3667    t_focus = -cr / dr;
3668    x_focus = t_focus * dx;
3669    y_focus = t_focus * dy;
3670    if (minx <= x_focus && x_focus <= maxx &&
3671        miny <= y_focus && y_focus <= maxy)
3672    {
3673      valid = radialExtendRange (range, t_focus, valid);
3674    }
3675  }
3676
3677  // Circles externally tangent to box edges.
3678  //
3679  // All circles have center in (dx, dy) * t
3680  //
3681  // If the circle is tangent to the line defined by the edge of the
3682  // box, then at least one of the following holds true:
3683  //
3684  //   (dx*t) + (cr + dr*t) == x0 (left   edge)
3685  //   (dx*t) - (cr + dr*t) == x1 (right  edge)
3686  //   (dy*t) + (cr + dr*t) == y0 (top    edge)
3687  //   (dy*t) - (cr + dr*t) == y1 (bottom edge)
3688  //
3689  // The solution is only valid if the tangent point is actually on
3690  // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
3691  // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
3692  //
3693  // For the first equation:
3694  //
3695  //   (dx + dr) * t = x0 - cr
3696  //   t = (x0 - cr) / (dx + dr)
3697  //   y = dy * t
3698  //
3699  // in the code this becomes:
3700  //
3701  //   t_edge = (num) / (den)
3702  //   v = (delta) * t_edge
3703  //
3704  // If the denominator in t is 0, the pattern is tangent to a line
3705  // parallel to the edge under examination. The corner-case where the
3706  // boundary line is the same as the edge is handled by the focus
3707  // point case and/or by the a==0 case.
3708 
3709  // circles tangent (externally) to left/right/top/bottom edge
3710  radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range);
3711  radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range);
3712  radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range);
3713  radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range);
3714
3715  // Circles passing through a corner.
3716  //
3717  // A circle passing through the point (x,y) satisfies:
3718  //
3719  // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
3720  //
3721  // If we set:
3722  //   a = dx^2 + dy^2 - dr^2
3723  //   b = x*dx + y*dy + cr*dr
3724  //   c = x^2 + y^2 - cr^2
3725  // we have:
3726  //   a*t^2 - 2*b*t + c == 0
3727   
3728  a = dx * dx + dy * dy - dr * dr;
3729  if (fabs (a) < RADIAL_EPSILON * RADIAL_EPSILON) {
3730    double b;
3731
3732    // Ensure that gradients with both a and dr small are
3733    // considered degenerate.
3734    // The floating point version of the degeneracy test implemented
3735    // in _radial_pattern_is_degenerate() is:
3736    //
3737    //  1) The circles are practically the same size:
3738    //     |dr| < RADIAL_EPSILON
3739    //  AND
3740    //  2a) The circles are both very small:
3741    //      min (r0, r1) < RADIAL_EPSILON
3742    //   OR
3743    //  2b) The circles are very close to each other:
3744    //      max (|dx|, |dy|) < 2 * RADIAL_EPSILON
3745    //
3746    // Assuming that the gradient is not degenerate, we want to
3747    // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
3748    //
3749    // If the gradient is not degenerate yet it has |dr| <
3750    // RADIAL_EPSILON, (2b) is false, thus:
3751    //
3752    //   max (|dx|, |dy|) >= 2*RADIAL_EPSILON
3753    // which implies:
3754    //   4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
3755    //
3756    // From the definition of a, we get:
3757    //   a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
3758    //   dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
3759    //   3*RADIAL_EPSILON^2 < dr^2
3760    //
3761    // which is inconsistent with the hypotheses, thus |dr| <
3762    // RADIAL_EPSILON is false or the gradient is degenerate.
3763       
3764    assert (fabs (dr) >= RADIAL_EPSILON);
3765
3766    // If a == 0, all the circles are tangent to a line in the
3767    // focus point. If this line is within the box extents, we
3768    // should add the circle with infinite radius, but this would
3769    // make the range unbounded. We will be limiting the range to
3770    // [0,1] anyway, so we simply add the biggest legitimate
3771    // circle (it happens for 0 or for 1).
3772    if (dr < 0) {
3773      valid = radialExtendRange (range, 0, valid);
3774    } else {
3775      valid = radialExtendRange (range, 1, valid);
3776    }
3777
3778    // Nondegenerate, nonlimit circles passing through the corners.
3779    //
3780    // a == 0 && a*t^2 - 2*b*t + c == 0
3781    //
3782    // t = c / (2*b)
3783    //
3784    // The b == 0 case has just been handled, so we only have to
3785    // compute this if b != 0.
3786
3787        // circles touching each corner
3788        radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range);
3789        radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range);
3790        radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range);
3791        radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range);
3792  } else {
3793    double inva, b, c, d;
3794
3795    inva = 1 / a;
3796
3797    // Nondegenerate, nonlimit circles passing through the corners.
3798    //
3799    // a != 0 && a*t^2 - 2*b*t + c == 0
3800    //
3801    // t = (b +- sqrt (b*b - a*c)) / a
3802    //
3803    // If the argument of sqrt() is negative, then no circle
3804    // passes through the corner.
3805
3806    // circles touching each corner
3807        radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
3808        radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
3809        radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
3810        radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
3811  }
3812
3813  *lower = std::max<double>(0., std::min<double>(1., range[0]));
3814  *upper = std::max<double>(0., std::min<double>(1., range[1]));
3815}
3816
3817//------------------------------------------------------------------------
3818// GfxShadingBitBuf
3819//------------------------------------------------------------------------
3820
3821class GfxShadingBitBuf {
3822public:
3823
3824  GfxShadingBitBuf(Stream *strA);
3825  ~GfxShadingBitBuf();
3826  GBool getBits(int n, Guint *val);
3827  void flushBits();
3828
3829private:
3830
3831  Stream *str;
3832  int bitBuf;
3833  int nBits;
3834};
3835
3836GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
3837  str = strA;
3838  str->reset();
3839  bitBuf = 0;
3840  nBits = 0;
3841}
3842
3843GfxShadingBitBuf::~GfxShadingBitBuf() {
3844  str->close();
3845}
3846
3847GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
3848  int x;
3849
3850  if (nBits >= n) {
3851    x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
3852    nBits -= n;
3853  } else {
3854    x = 0;
3855    if (nBits > 0) {
3856      x = bitBuf & ((1 << nBits) - 1);
3857      n -= nBits;
3858      nBits = 0;
3859    }
3860    while (n > 0) {
3861      if ((bitBuf = str->getChar()) == EOF) {
3862        nBits = 0;
3863        return gFalse;
3864      }
3865      if (n >= 8) {
3866        x = (x << 8) | bitBuf;
3867        n -= 8;
3868      } else {
3869        x = (x << n) | (bitBuf >> (8 - n));
3870        nBits = 8 - n;
3871        n = 0;
3872      }
3873    }
3874  }
3875  *val = x;
3876  return gTrue;
3877}
3878
3879void GfxShadingBitBuf::flushBits() {
3880  bitBuf = 0;
3881  nBits = 0;
3882}
3883
3884//------------------------------------------------------------------------
3885// GfxGouraudTriangleShading
3886//------------------------------------------------------------------------
3887
3888GfxGouraudTriangleShading::GfxGouraudTriangleShading(
3889                               int typeA,
3890                               GfxGouraudVertex *verticesA, int nVerticesA,
3891                               int (*trianglesA)[3], int nTrianglesA,
3892                               Function **funcsA, int nFuncsA):
3893  GfxShading(typeA)
3894{
3895  int i;
3896
3897  vertices = verticesA;
3898  nVertices = nVerticesA;
3899  triangles = trianglesA;
3900  nTriangles = nTrianglesA;
3901  nFuncs = nFuncsA;
3902  for (i = 0; i < nFuncs; ++i) {
3903    funcs[i] = funcsA[i];
3904  }
3905}
3906
3907GfxGouraudTriangleShading::GfxGouraudTriangleShading(
3908                               GfxGouraudTriangleShading *shading):
3909  GfxShading(shading)
3910{
3911  int i;
3912
3913  nVertices = shading->nVertices;
3914  vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
3915  memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
3916  nTriangles = shading->nTriangles;
3917  triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
3918  memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
3919  nFuncs = shading->nFuncs;
3920  for (i = 0; i < nFuncs; ++i) {
3921    funcs[i] = shading->funcs[i]->copy();
3922  }
3923}
3924
3925GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
3926  int i;
3927
3928  gfree(vertices);
3929  gfree(triangles);
3930  for (i = 0; i < nFuncs; ++i) {
3931    delete funcs[i];
3932  }
3933}
3934
3935GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(int typeA,
3936                                                            Dict *dict,
3937                                                            Stream *str,
3938                                                            Gfx *gfx) {
3939  GfxGouraudTriangleShading *shading;
3940  Function *funcsA[gfxColorMaxComps];
3941  int nFuncsA;
3942  int coordBits, compBits, flagBits, vertsPerRow, nRows;
3943  double xMin, xMax, yMin, yMax;
3944  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
3945  double xMul, yMul;
3946  double cMul[gfxColorMaxComps];
3947  GfxGouraudVertex *verticesA;
3948  int (*trianglesA)[3];
3949  int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
3950  Guint x, y, flag;
3951  Guint c[gfxColorMaxComps];
3952  GfxShadingBitBuf *bitBuf;
3953  Object obj1, obj2;
3954  int i, j, k, state;
3955
3956  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
3957    coordBits = obj1.getInt();
3958  } else {
3959    error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
3960    goto err2;
3961  }
3962  obj1.free();
3963  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
3964    compBits = obj1.getInt();
3965  } else {
3966    error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
3967    goto err2;
3968  }
3969  obj1.free();
3970  flagBits = vertsPerRow = 0; // make gcc happy
3971  if (typeA == 4) {
3972    if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
3973      flagBits = obj1.getInt();
3974    } else {
3975      error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
3976      goto err2;
3977    }
3978    obj1.free();
3979  } else {
3980    if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
3981      vertsPerRow = obj1.getInt();
3982    } else {
3983      error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary");
3984      goto err2;
3985    }
3986    obj1.free();
3987  }
3988  if (dict->lookup("Decode", &obj1)->isArray() &&
3989      obj1.arrayGetLength() >= 6) {
3990    xMin = obj1.arrayGet(0, &obj2)->getNum();
3991    obj2.free();
3992    xMax = obj1.arrayGet(1, &obj2)->getNum();
3993    obj2.free();
3994    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
3995    yMin = obj1.arrayGet(2, &obj2)->getNum();
3996    obj2.free();
3997    yMax = obj1.arrayGet(3, &obj2)->getNum();
3998    obj2.free();
3999    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4000    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4001      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
4002      obj2.free();
4003      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
4004      obj2.free();
4005      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
4006    }
4007    nComps = i;
4008  } else {
4009    error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4010    goto err2;
4011  }
4012  obj1.free();
4013
4014  if (!dict->lookup("Function", &obj1)->isNull()) {
4015    if (obj1.isArray()) {
4016      nFuncsA = obj1.arrayGetLength();
4017      if (nFuncsA > gfxColorMaxComps) {
4018        error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4019        goto err1;
4020      }
4021      for (i = 0; i < nFuncsA; ++i) {
4022        obj1.arrayGet(i, &obj2);
4023        if (!(funcsA[i] = Function::parse(&obj2))) {
4024          obj1.free();
4025          obj2.free();
4026          goto err1;
4027        }
4028        obj2.free();
4029      }
4030    } else {
4031      nFuncsA = 1;
4032      if (!(funcsA[0] = Function::parse(&obj1))) {
4033        obj1.free();
4034        goto err1;
4035      }
4036    }
4037  } else {
4038    nFuncsA = 0;
4039  }
4040  obj1.free();
4041
4042  nVerticesA = nTrianglesA = 0;
4043  verticesA = NULL;
4044  trianglesA = NULL;
4045  vertSize = triSize = 0;
4046  state = 0;
4047  flag = 0; // make gcc happy
4048  bitBuf = new GfxShadingBitBuf(str);
4049  while (1) {
4050    if (typeA == 4) {
4051      if (!bitBuf->getBits(flagBits, &flag)) {
4052        break;
4053      }
4054    }
4055    if (!bitBuf->getBits(coordBits, &x) ||
4056        !bitBuf->getBits(coordBits, &y)) {
4057      break;
4058    }
4059    for (i = 0; i < nComps; ++i) {
4060      if (!bitBuf->getBits(compBits, &c[i])) {
4061        break;
4062      }
4063    }
4064    if (i < nComps) {
4065      break;
4066    }
4067    if (nVerticesA == vertSize) {
4068      int oldVertSize = vertSize;
4069      vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
4070      verticesA = (GfxGouraudVertex *)
4071                      greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
4072      memset(verticesA + oldVertSize, 0, (vertSize - oldVertSize) * sizeof(GfxGouraudVertex));
4073    }
4074    verticesA[nVerticesA].x = xMin + xMul * (double)x;
4075    verticesA[nVerticesA].y = yMin + yMul * (double)y;
4076    for (i = 0; i < nComps; ++i) {
4077      verticesA[nVerticesA].color.c[i] =
4078          dblToCol(cMin[i] + cMul[i] * (double)c[i]);
4079    }
4080    ++nVerticesA;
4081    bitBuf->flushBits();
4082    if (typeA == 4) {
4083      if (state == 0 || state == 1) {
4084        ++state;
4085      } else if (state == 2 || flag > 0) {
4086        if (nTrianglesA == triSize) {
4087          triSize = (triSize == 0) ? 16 : 2 * triSize;
4088          trianglesA = (int (*)[3])
4089                           greallocn(trianglesA, triSize * 3, sizeof(int));
4090        }
4091        if (state == 2) {
4092          trianglesA[nTrianglesA][0] = nVerticesA - 3;
4093          trianglesA[nTrianglesA][1] = nVerticesA - 2;
4094          trianglesA[nTrianglesA][2] = nVerticesA - 1;
4095          ++state;
4096        } else if (flag == 1) {
4097          trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
4098          trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
4099          trianglesA[nTrianglesA][2] = nVerticesA - 1;
4100        } else { // flag == 2
4101          trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
4102          trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
4103          trianglesA[nTrianglesA][2] = nVerticesA - 1;
4104        }
4105        ++nTrianglesA;
4106      } else { // state == 3 && flag == 0
4107        state = 1;
4108      }
4109    }
4110  }
4111  delete bitBuf;
4112  if (typeA == 5) {
4113    nRows = nVerticesA / vertsPerRow;
4114    nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
4115    trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
4116    k = 0;
4117    for (i = 0; i < nRows - 1; ++i) {
4118      for (j = 0; j < vertsPerRow - 1; ++j) {
4119        trianglesA[k][0] = i * vertsPerRow + j;
4120        trianglesA[k][1] = i * vertsPerRow + j+1;
4121        trianglesA[k][2] = (i+1) * vertsPerRow + j;
4122        ++k;
4123        trianglesA[k][0] = i * vertsPerRow + j+1;
4124        trianglesA[k][1] = (i+1) * vertsPerRow + j;
4125        trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
4126        ++k;
4127      }
4128    }
4129  }
4130
4131  shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
4132                                          trianglesA, nTrianglesA,
4133                                          funcsA, nFuncsA);
4134  if (!shading->init(dict, gfx)) {
4135    delete shading;
4136    return NULL;
4137  }
4138  return shading;
4139
4140 err2:
4141  obj1.free();
4142 err1:
4143  return NULL;
4144}
4145
4146GfxShading *GfxGouraudTriangleShading::copy() {
4147  return new GfxGouraudTriangleShading(this);
4148}
4149
4150void GfxGouraudTriangleShading::getTriangle(
4151                                    int i,
4152                                    double *x0, double *y0, GfxColor *color0,
4153                                    double *x1, double *y1, GfxColor *color1,
4154                                    double *x2, double *y2, GfxColor *color2) {
4155  double in;
4156  double out[gfxColorMaxComps];
4157  int v, j;
4158
4159  assert(!isParameterized()); 
4160
4161  v = triangles[i][0];
4162  *x0 = vertices[v].x;
4163  *y0 = vertices[v].y;
4164  if (nFuncs > 0) {
4165    in = colToDbl(vertices[v].color.c[0]);
4166    for (j = 0; j < nFuncs; ++j) {
4167      funcs[j]->transform(&in, &out[j]);
4168    }
4169    for (j = 0; j < gfxColorMaxComps; ++j) {
4170      color0->c[j] = dblToCol(out[j]);
4171    }
4172  } else {
4173    *color0 = vertices[v].color;
4174  }
4175  v = triangles[i][1];
4176  *x1 = vertices[v].x;
4177  *y1 = vertices[v].y;
4178  if (nFuncs > 0) {
4179    in = colToDbl(vertices[v].color.c[0]);
4180    for (j = 0; j < nFuncs; ++j) {
4181      funcs[j]->transform(&in, &out[j]);
4182    }
4183    for (j = 0; j < gfxColorMaxComps; ++j) {
4184      color1->c[j] = dblToCol(out[j]);
4185    }
4186  } else {
4187    *color1 = vertices[v].color;
4188  }
4189  v = triangles[i][2];
4190  *x2 = vertices[v].x;
4191  *y2 = vertices[v].y;
4192  if (nFuncs > 0) {
4193    in = colToDbl(vertices[v].color.c[0]);
4194    for (j = 0; j < nFuncs; ++j) {
4195      funcs[j]->transform(&in, &out[j]);
4196    }
4197    for (j = 0; j < gfxColorMaxComps; ++j) {
4198      color2->c[j] = dblToCol(out[j]);
4199    }
4200  } else {
4201    *color2 = vertices[v].color;
4202  }
4203}
4204
4205void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) {
4206  double out[gfxColorMaxComps];
4207
4208  for (int j = 0; j < nFuncs; ++j) {
4209    funcs[j]->transform(&t, &out[j]);
4210  }
4211  for (int j = 0; j < gfxColorMaxComps; ++j) {
4212    color->c[j] = dblToCol(out[j]);
4213  }
4214}
4215
4216void GfxGouraudTriangleShading::getTriangle(int i,
4217                                            double *x0, double *y0, double *color0,
4218                                            double *x1, double *y1, double *color1,
4219                                            double *x2, double *y2, double *color2) {
4220  int v;
4221
4222  assert(isParameterized()); 
4223
4224  v = triangles[i][0];
4225  *x0 = vertices[v].x;
4226  *y0 = vertices[v].y;
4227  *color0 = colToDbl(vertices[v].color.c[0]);
4228  v = triangles[i][1];
4229  *x1 = vertices[v].x;
4230  *y1 = vertices[v].y;
4231  *color1 = colToDbl(vertices[v].color.c[0]);
4232  v = triangles[i][2];
4233  *x2 = vertices[v].x;
4234  *y2 = vertices[v].y;
4235  *color2 = colToDbl(vertices[v].color.c[0]);
4236}
4237
4238//------------------------------------------------------------------------
4239// GfxPatchMeshShading
4240//------------------------------------------------------------------------
4241
4242GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
4243                                         GfxPatch *patchesA, int nPatchesA,
4244                                         Function **funcsA, int nFuncsA):
4245  GfxShading(typeA)
4246{
4247  int i;
4248
4249  patches = patchesA;
4250  nPatches = nPatchesA;
4251  nFuncs = nFuncsA;
4252  for (i = 0; i < nFuncs; ++i) {
4253    funcs[i] = funcsA[i];
4254  }
4255}
4256
4257GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
4258  GfxShading(shading)
4259{
4260  int i;
4261
4262  nPatches = shading->nPatches;
4263  patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
4264  memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
4265  nFuncs = shading->nFuncs;
4266  for (i = 0; i < nFuncs; ++i) {
4267    funcs[i] = shading->funcs[i]->copy();
4268  }
4269}
4270
4271GfxPatchMeshShading::~GfxPatchMeshShading() {
4272  int i;
4273
4274  gfree(patches);
4275  for (i = 0; i < nFuncs; ++i) {
4276    delete funcs[i];
4277  }
4278}
4279
4280GfxPatchMeshShading *GfxPatchMeshShading::parse(int typeA, Dict *dict,
4281                                                Stream *str, Gfx *gfx) {
4282  GfxPatchMeshShading *shading;
4283  Function *funcsA[gfxColorMaxComps];
4284  int nFuncsA;
4285  int coordBits, compBits, flagBits;
4286  double xMin, xMax, yMin, yMax;
4287  double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
4288  double xMul, yMul;
4289  double cMul[gfxColorMaxComps];
4290  GfxPatch *patchesA, *p;
4291  int nComps, nPatchesA, patchesSize, nPts, nColors;
4292  Guint flag;
4293  double x[16], y[16];
4294  Guint xi, yi;
4295  double c[4][gfxColorMaxComps];
4296  Guint ci;
4297  GfxShadingBitBuf *bitBuf;
4298  Object obj1, obj2;
4299  int i, j;
4300
4301  if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
4302    coordBits = obj1.getInt();
4303  } else {
4304    error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4305    goto err2;
4306  }
4307  obj1.free();
4308  if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
4309    compBits = obj1.getInt();
4310  } else {
4311    error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4312    goto err2;
4313  }
4314  obj1.free();
4315  if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
4316    flagBits = obj1.getInt();
4317  } else {
4318    error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4319    goto err2;
4320  }
4321  obj1.free();
4322  if (dict->lookup("Decode", &obj1)->isArray() &&
4323      obj1.arrayGetLength() >= 6) {
4324    xMin = obj1.arrayGet(0, &obj2)->getNum();
4325    obj2.free();
4326    xMax = obj1.arrayGet(1, &obj2)->getNum();
4327    obj2.free();
4328    xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
4329    yMin = obj1.arrayGet(2, &obj2)->getNum();
4330    obj2.free();
4331    yMax = obj1.arrayGet(3, &obj2)->getNum();
4332    obj2.free();
4333    yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4334    for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4335      cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
4336      obj2.free();
4337      cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
4338      obj2.free();
4339      cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
4340    }
4341    nComps = i;
4342  } else {
4343    error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4344    goto err2;
4345  }
4346  obj1.free();
4347
4348  if (!dict->lookup("Function", &obj1)->isNull()) {
4349    if (obj1.isArray()) {
4350      nFuncsA = obj1.arrayGetLength();
4351      if (nFuncsA > gfxColorMaxComps) {
4352        error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4353        goto err1;
4354      }
4355      for (i = 0; i < nFuncsA; ++i) {
4356        obj1.arrayGet(i, &obj2);
4357        if (!(funcsA[i] = Function::parse(&obj2))) {
4358          obj1.free();
4359          obj2.free();
4360          goto err1;
4361        }
4362        obj2.free();
4363      }
4364    } else {
4365      nFuncsA = 1;
4366      if (!(funcsA[0] = Function::parse(&obj1))) {
4367        obj1.free();
4368        goto err1;
4369      }
4370    }
4371  } else {
4372    nFuncsA = 0;
4373  }
4374  obj1.free();
4375
4376  nPatchesA = 0;
4377  patchesA = NULL;
4378  patchesSize = 0;
4379  bitBuf = new GfxShadingBitBuf(str);
4380  while (1) {
4381    if (!bitBuf->getBits(flagBits, &flag)) {
4382      break;
4383    }
4384    if (typeA == 6) {
4385      switch (flag) {
4386      case 0: nPts = 12; nColors = 4; break;
4387      case 1:
4388      case 2:
4389      case 3:
4390      default: nPts =  8; nColors = 2; break;
4391      }
4392    } else {
4393      switch (flag) {
4394      case 0: nPts = 16; nColors = 4; break;
4395      case 1:
4396      case 2:
4397      case 3:
4398      default: nPts = 12; nColors = 2; break;
4399      }
4400    }
4401    for (i = 0; i < nPts; ++i) {
4402      if (!bitBuf->getBits(coordBits, &xi) ||
4403          !bitBuf->getBits(coordBits, &yi)) {
4404        break;
4405      }
4406      x[i] = xMin + xMul * (double)xi;
4407      y[i] = yMin + yMul * (double)yi;
4408    }
4409    if (i < nPts) {
4410      break;
4411    }
4412    for (i = 0; i < nColors; ++i) {
4413      for (j = 0; j < nComps; ++j) {
4414        if (!bitBuf->getBits(compBits, &ci)) {
4415          break;
4416        }
4417        c[i][j] = cMin[j] + cMul[j] * (double)ci;
4418        if( nFuncsA == 0 ) {
4419          // ... and colorspace values can also be stored into doubles.
4420          // They will be casted later.
4421          c[i][j] = dblToCol(c[i][j]);
4422        }
4423      }
4424      if (j < nComps) {
4425        break;
4426      }
4427    }
4428    if (i < nColors) {
4429      break;
4430    }
4431    if (nPatchesA == patchesSize) {
4432      int oldPatchesSize = patchesSize;
4433      patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
4434      patchesA = (GfxPatch *)greallocn(patchesA,
4435                                       patchesSize, sizeof(GfxPatch));
4436      memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch));
4437    }
4438    p = &patchesA[nPatchesA];
4439    if (typeA == 6) {
4440      switch (flag) {
4441      case 0:
4442        p->x[0][0] = x[0];
4443        p->y[0][0] = y[0];
4444        p->x[0][1] = x[1];
4445        p->y[0][1] = y[1];
4446        p->x[0][2] = x[2];
4447        p->y[0][2] = y[2];
4448        p->x[0][3] = x[3];
4449        p->y[0][3] = y[3];
4450        p->x[1][3] = x[4];
4451        p->y[1][3] = y[4];
4452        p->x[2][3] = x[5];
4453        p->y[2][3] = y[5];
4454        p->x[3][3] = x[6];
4455        p->y[3][3] = y[6];
4456        p->x[3][2] = x[7];
4457        p->y[3][2] = y[7];
4458        p->x[3][1] = x[8];
4459        p->y[3][1] = y[8];
4460        p->x[3][0] = x[9];
4461        p->y[3][0] = y[9];
4462        p->x[2][0] = x[10];
4463        p->y[2][0] = y[10];
4464        p->x[1][0] = x[11];
4465        p->y[1][0] = y[11];
4466        for (j = 0; j < nComps; ++j) {
4467          p->color[0][0].c[j] = c[0][j];
4468          p->color[0][1].c[j] = c[1][j];
4469          p->color[1][1].c[j] = c[2][j];
4470          p->color[1][0].c[j] = c[3][j];
4471        }
4472        break;
4473      case 1:
4474        if (nPatchesA == 0) {
4475          goto err1;
4476        }
4477        p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
4478        p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
4479        p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
4480        p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
4481        p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
4482        p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
4483        p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
4484        p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
4485        p->x[1][3] = x[0];
4486        p->y[1][3] = y[0];
4487        p->x[2][3] = x[1];
4488        p->y[2][3] = y[1];
4489        p->x[3][3] = x[2];
4490        p->y[3][3] = y[2];
4491        p->x[3][2] = x[3];
4492        p->y[3][2] = y[3];
4493        p->x[3][1] = x[4];
4494        p->y[3][1] = y[4];
4495        p->x[3][0] = x[5];
4496        p->y[3][0] = y[5];
4497        p->x[2][0] = x[6];
4498        p->y[2][0] = y[6];
4499        p->x[1][0] = x[7];
4500        p->y[1][0] = y[7];
4501        for (j = 0; j < nComps; ++j) {
4502          p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
4503          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
4504          p->color[1][1].c[j] = c[0][j];
4505          p->color[1][0].c[j] = c[1][j];
4506        }
4507        break;
4508      case 2:
4509        if (nPatchesA == 0) {
4510          goto err1;
4511        }
4512        p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
4513        p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
4514        p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
4515        p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
4516        p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
4517        p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
4518        p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
4519        p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
4520        p->x[1][3] = x[0];
4521        p->y[1][3] = y[0];
4522        p->x[2][3] = x[1];
4523        p->y[2][3] = y[1];
4524        p->x[3][3] = x[2];
4525        p->y[3][3] = y[2];
4526        p->x[3][2] = x[3];
4527        p->y[3][2] = y[3];
4528        p->x[3][1] = x[4];
4529        p->y[3][1] = y[4];
4530        p->x[3][0] = x[5];
4531        p->y[3][0] = y[5];
4532        p->x[2][0] = x[6];
4533        p->y[2][0] = y[6];
4534        p->x[1][0] = x[7];
4535        p->y[1][0] = y[7];
4536        for (j = 0; j < nComps; ++j) {
4537          p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
4538          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
4539          p->color[1][1].c[j] = c[0][j];
4540          p->color[1][0].c[j] = c[1][j];
4541        }
4542        break;
4543      case 3:
4544        if (nPatchesA == 0) {
4545          goto err1;
4546        }
4547        p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
4548        p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
4549        p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
4550        p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
4551        p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
4552        p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
4553        p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
4554        p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
4555        p->x[1][3] = x[0];
4556        p->y[1][3] = y[0];
4557        p->x[2][3] = x[1];
4558        p->y[2][3] = y[1];
4559        p->x[3][3] = x[2];
4560        p->y[3][3] = y[2];
4561        p->x[3][2] = x[3];
4562        p->y[3][2] = y[3];
4563        p->x[3][1] = x[4];
4564        p->y[3][1] = y[4];
4565        p->x[3][0] = x[5];
4566        p->y[3][0] = y[5];
4567        p->x[2][0] = x[6];
4568        p->y[2][0] = y[6];
4569        p->x[1][0] = x[7];
4570        p->y[1][0] = y[7];
4571        for (j = 0; j < nComps; ++j) {
4572          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
4573          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
4574          p->color[1][1].c[j] = c[0][j];
4575          p->color[1][0].c[j] = c[1][j];
4576        }
4577        break;
4578  }
4579    } else {
4580      switch (flag) {
4581      case 0:
4582        p->x[0][0] = x[0];
4583        p->y[0][0] = y[0];
4584        p->x[0][1] = x[1];
4585        p->y[0][1] = y[1];
4586        p->x[0][2] = x[2];
4587        p->y[0][2] = y[2];
4588        p->x[0][3] = x[3];
4589        p->y[0][3] = y[3];
4590        p->x[1][3] = x[4];
4591        p->y[1][3] = y[4];
4592        p->x[2][3] = x[5];
4593        p->y[2][3] = y[5];
4594        p->x[3][3] = x[6];
4595        p->y[3][3] = y[6];
4596        p->x[3][2] = x[7];
4597        p->y[3][2] = y[7];
4598        p->x[3][1] = x[8];
4599        p->y[3][1] = y[8];
4600        p->x[3][0] = x[9];
4601        p->y[3][0] = y[9];
4602        p->x[2][0] = x[10];
4603        p->y[2][0] = y[10];
4604        p->x[1][0] = x[11];
4605        p->y[1][0] = y[11];
4606        p->x[1][1] = x[12];
4607        p->y[1][1] = y[12];
4608        p->x[1][2] = x[13];
4609        p->y[1][2] = y[13];
4610        p->x[2][2] = x[14];
4611        p->y[2][2] = y[14];
4612        p->x[2][1] = x[15];
4613        p->y[2][1] = y[15];
4614        for (j = 0; j < nComps; ++j) {
4615          p->color[0][0].c[j] = c[0][j];
4616          p->color[0][1].c[j] = c[1][j];
4617          p->color[1][1].c[j] = c[2][j];
4618          p->color[1][0].c[j] = c[3][j];
4619        }
4620        break;
4621      case 1:
4622        if (nPatchesA == 0) {
4623          goto err1;
4624        }
4625        p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
4626        p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
4627        p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
4628        p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
4629        p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
4630        p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
4631        p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
4632        p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
4633        p->x[1][3] = x[0];
4634        p->y[1][3] = y[0];
4635        p->x[2][3] = x[1];
4636        p->y[2][3] = y[1];
4637        p->x[3][3] = x[2];
4638        p->y[3][3] = y[2];
4639        p->x[3][2] = x[3];
4640        p->y[3][2] = y[3];
4641        p->x[3][1] = x[4];
4642        p->y[3][1] = y[4];
4643        p->x[3][0] = x[5];
4644        p->y[3][0] = y[5];
4645        p->x[2][0] = x[6];
4646        p->y[2][0] = y[6];
4647        p->x[1][0] = x[7];
4648        p->y[1][0] = y[7];
4649        p->x[1][1] = x[8];
4650        p->y[1][1] = y[8];
4651        p->x[1][2] = x[9];
4652        p->y[1][2] = y[9];
4653        p->x[2][2] = x[10];
4654        p->y[2][2] = y[10];
4655        p->x[2][1] = x[11];
4656        p->y[2][1] = y[11];
4657        for (j = 0; j < nComps; ++j) {
4658          p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
4659          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
4660          p->color[1][1].c[j] = c[0][j];
4661          p->color[1][0].c[j] = c[1][j];
4662        }
4663        break;
4664      case 2:
4665        if (nPatchesA == 0) {
4666          goto err1;
4667        }
4668        p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
4669        p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
4670        p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
4671        p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
4672        p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
4673        p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
4674        p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
4675        p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
4676        p->x[1][3] = x[0];
4677        p->y[1][3] = y[0];
4678        p->x[2][3] = x[1];
4679        p->y[2][3] = y[1];
4680        p->x[3][3] = x[2];
4681        p->y[3][3] = y[2];
4682        p->x[3][2] = x[3];
4683        p->y[3][2] = y[3];
4684        p->x[3][1] = x[4];
4685        p->y[3][1] = y[4];
4686        p->x[3][0] = x[5];
4687        p->y[3][0] = y[5];
4688        p->x[2][0] = x[6];
4689        p->y[2][0] = y[6];
4690        p->x[1][0] = x[7];
4691        p->y[1][0] = y[7];
4692        p->x[1][1] = x[8];
4693        p->y[1][1] = y[8];
4694        p->x[1][2] = x[9];
4695        p->y[1][2] = y[9];
4696        p->x[2][2] = x[10];
4697        p->y[2][2] = y[10];
4698        p->x[2][1] = x[11];
4699        p->y[2][1] = y[11];
4700        for (j = 0; j < nComps; ++j) {
4701          p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
4702          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
4703          p->color[1][1].c[j] = c[0][j];
4704          p->color[1][0].c[j] = c[1][j];
4705        }
4706        break;
4707      case 3:
4708        if (nPatchesA == 0) {
4709          goto err1;
4710        }
4711        p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
4712        p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
4713        p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
4714        p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
4715        p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
4716        p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
4717        p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
4718        p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
4719        p->x[1][3] = x[0];
4720        p->y[1][3] = y[0];
4721        p->x[2][3] = x[1];
4722        p->y[2][3] = y[1];
4723        p->x[3][3] = x[2];
4724        p->y[3][3] = y[2];
4725        p->x[3][2] = x[3];
4726        p->y[3][2] = y[3];
4727        p->x[3][1] = x[4];
4728        p->y[3][1] = y[4];
4729        p->x[3][0] = x[5];
4730        p->y[3][0] = y[5];
4731        p->x[2][0] = x[6];
4732        p->y[2][0] = y[6];
4733        p->x[1][0] = x[7];
4734        p->y[1][0] = y[7];
4735        p->x[1][1] = x[8];
4736        p->y[1][1] = y[8];
4737        p->x[1][2] = x[9];
4738        p->y[1][2] = y[9];
4739        p->x[2][2] = x[10];
4740        p->y[2][2] = y[10];
4741        p->x[2][1] = x[11];
4742        p->y[2][1] = y[11];
4743        for (j = 0; j < nComps; ++j) {
4744          p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
4745          p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
4746          p->color[1][1].c[j] = c[0][j];
4747          p->color[1][0].c[j] = c[1][j];
4748        }
4749        break;
4750      }
4751    }
4752    ++nPatchesA;
4753    bitBuf->flushBits();
4754  }
4755  delete bitBuf;
4756
4757  if (typeA == 6) {
4758    for (i = 0; i < nPatchesA; ++i) {
4759      p = &patchesA[i];
4760      p->x[1][1] = (-4 * p->x[0][0]
4761                    +6 * (p->x[0][1] + p->x[1][0])
4762                    -2 * (p->x[0][3] + p->x[3][0])
4763                    +3 * (p->x[3][1] + p->x[1][3])
4764                    - p->x[3][3]) / 9;
4765      p->y[1][1] = (-4 * p->y[0][0]
4766                    +6 * (p->y[0][1] + p->y[1][0])
4767                    -2 * (p->y[0][3] + p->y[3][0])
4768                    +3 * (p->y[3][1] + p->y[1][3])
4769                    - p->y[3][3]) / 9;
4770      p->x[1][2] = (-4 * p->x[0][3]
4771                    +6 * (p->x[0][2] + p->x[1][3])
4772                    -2 * (p->x[0][0] + p->x[3][3])
4773                    +3 * (p->x[3][2] + p->x[1][0])
4774                    - p->x[3][0]) / 9;
4775      p->y[1][2] = (-4 * p->y[0][3]
4776                    +6 * (p->y[0][2] + p->y[1][3])
4777                    -2 * (p->y[0][0] + p->y[3][3])
4778                    +3 * (p->y[3][2] + p->y[1][0])
4779                    - p->y[3][0]) / 9;
4780      p->x[2][1] = (-4 * p->x[3][0]
4781                    +6 * (p->x[3][1] + p->x[2][0])
4782                    -2 * (p->x[3][3] + p->x[0][0])
4783                    +3 * (p->x[0][1] + p->x[2][3])
4784                    - p->x[0][3]) / 9;
4785      p->y[2][1] = (-4 * p->y[3][0]
4786                    +6 * (p->y[3][1] + p->y[2][0])
4787                    -2 * (p->y[3][3] + p->y[0][0])
4788                    +3 * (p->y[0][1] + p->y[2][3])
4789                    - p->y[0][3]) / 9;
4790      p->x[2][2] = (-4 * p->x[3][3]
4791                    +6 * (p->x[3][2] + p->x[2][3])
4792                    -2 * (p->x[3][0] + p->x[0][3])
4793                    +3 * (p->x[0][2] + p->x[2][0])
4794                    - p->x[0][0]) / 9;
4795      p->y[2][2] = (-4 * p->y[3][3]
4796                    +6 * (p->y[3][2] + p->y[2][3])
4797                    -2 * (p->y[3][0] + p->y[0][3])
4798                    +3 * (p->y[0][2] + p->y[2][0])
4799                    - p->y[0][0]) / 9;
4800    }
4801  }
4802
4803  shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
4804                                    funcsA, nFuncsA);
4805  if (!shading->init(dict, gfx)) {
4806    delete shading;
4807    return NULL;
4808  }
4809  return shading;
4810
4811 err2:
4812  obj1.free();
4813 err1:
4814  return NULL;
4815}
4816
4817void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) {
4818  double out[gfxColorMaxComps];
4819
4820  for (int j = 0; j < nFuncs; ++j) {
4821    funcs[j]->transform(&t, &out[j]);
4822  }
4823  for (int j = 0; j < gfxColorMaxComps; ++j) {
4824    color->c[j] = dblToCol(out[j]);
4825  }
4826}
4827
4828GfxShading *GfxPatchMeshShading::copy() {
4829  return new GfxPatchMeshShading(this);
4830}
4831
4832//------------------------------------------------------------------------
4833// GfxImageColorMap
4834//------------------------------------------------------------------------
4835
4836GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
4837                                   GfxColorSpace *colorSpaceA) {
4838  GfxIndexedColorSpace *indexedCS;
4839  GfxSeparationColorSpace *sepCS;
4840  int maxPixel, indexHigh;
4841  Guchar *indexedLookup;
4842  Function *sepFunc;
4843  Object obj;
4844  double x[gfxColorMaxComps];
4845  double y[gfxColorMaxComps];
4846  int i, j, k;
4847  double mapped;
4848  GBool useByteLookup;
4849
4850  ok = gTrue;
4851
4852  // bits per component and color space
4853  bits = bitsA;
4854  maxPixel = (1 << bits) - 1;
4855  colorSpace = colorSpaceA;
4856
4857  // this is a hack to support 16 bits images, everywhere
4858  // we assume a component fits in 8 bits, with this hack
4859  // we treat 16 bit images as 8 bit ones until it's fixed correctly.
4860  // The hack has another part on ImageStream::getLine
4861  if (maxPixel > 255) maxPixel = 255;
4862
4863  // initialize
4864  for (k = 0; k < gfxColorMaxComps; ++k) {
4865    lookup[k] = NULL;
4866    lookup2[k] = NULL;
4867  }
4868  byte_lookup = NULL;
4869
4870  // get decode map
4871  if (decode->isNull()) {
4872    nComps = colorSpace->getNComps();
4873    colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
4874  } else if (decode->isArray()) {
4875    nComps = decode->arrayGetLength() / 2;
4876    if (nComps < colorSpace->getNComps()) {
4877      goto err1;
4878    }
4879    if (nComps > colorSpace->getNComps()) {
4880      error(errSyntaxWarning, -1, "Too many elements in Decode array");
4881      nComps = colorSpace->getNComps();
4882    }
4883    for (i = 0; i < nComps; ++i) {
4884      decode->arrayGet(2*i, &obj);
4885      if (!obj.isNum()) {
4886        goto err2;
4887      }
4888      decodeLow[i] = obj.getNum();
4889      obj.free();
4890      decode->arrayGet(2*i+1, &obj);
4891      if (!obj.isNum()) {
4892        goto err2;
4893      }
4894      decodeRange[i] = obj.getNum() - decodeLow[i];
4895      obj.free();
4896    }
4897  } else {
4898    goto err1;
4899  }
4900
4901  // Construct a lookup table -- this stores pre-computed decoded
4902  // values for each component, i.e., the result of applying the
4903  // decode mapping to each possible image pixel component value.
4904  for (k = 0; k < nComps; ++k) {
4905    lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
4906                                         sizeof(GfxColorComp));
4907    for (i = 0; i <= maxPixel; ++i) {
4908      lookup[k][i] = dblToCol(decodeLow[k] +
4909                              (i * decodeRange[k]) / maxPixel);
4910    }
4911  }
4912
4913  // Optimization: for Indexed and Separation color spaces (which have
4914  // only one component), we pre-compute a second lookup table with
4915  // color values
4916  colorSpace2 = NULL;
4917  nComps2 = 0;
4918  useByteLookup = gFalse;
4919  switch (colorSpace->getMode()) {
4920  case csIndexed:
4921    // Note that indexHigh may not be the same as maxPixel --
4922    // Distiller will remove unused palette entries, resulting in
4923    // indexHigh < maxPixel.
4924    indexedCS = (GfxIndexedColorSpace *)colorSpace;
4925    colorSpace2 = indexedCS->getBase();
4926    indexHigh = indexedCS->getIndexHigh();
4927    nComps2 = colorSpace2->getNComps();
4928    indexedLookup = indexedCS->getLookup();
4929    colorSpace2->getDefaultRanges(x, y, indexHigh);
4930    if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine()) {
4931      byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
4932      useByteLookup = gTrue;
4933    }
4934    for (k = 0; k < nComps2; ++k) {
4935      lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
4936                                           sizeof(GfxColorComp));
4937      for (i = 0; i <= maxPixel; ++i) {
4938        j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
4939        if (j < 0) {
4940          j = 0;
4941        } else if (j > indexHigh) {
4942          j = indexHigh;
4943        }
4944
4945        mapped = x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k];
4946        lookup2[k][i] = dblToCol(mapped);
4947        if (useByteLookup)
4948          byte_lookup[i * nComps2 + k] = (Guchar) (mapped * 255);
4949      }
4950    }
4951    break;
4952  case csSeparation:
4953    sepCS = (GfxSeparationColorSpace *)colorSpace;
4954    colorSpace2 = sepCS->getAlt();
4955    nComps2 = colorSpace2->getNComps();
4956    sepFunc = sepCS->getFunc();
4957    if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine()) {
4958      byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
4959      useByteLookup = gTrue;
4960    }
4961    for (k = 0; k < nComps2; ++k) {
4962      lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
4963                                           sizeof(GfxColorComp));
4964      for (i = 0; i <= maxPixel; ++i) {
4965        x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
4966        sepFunc->transform(x, y);
4967        lookup2[k][i] = dblToCol(y[k]);
4968        if (useByteLookup)
4969          byte_lookup[i*nComps2 + k] = (Guchar) (y[k] * 255);
4970      }
4971    }
4972    break;
4973  default:
4974    if (colorSpace->useGetGrayLine() || colorSpace->useGetRGBLine()) {
4975      byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps);
4976      useByteLookup = gTrue;
4977    }
4978    for (k = 0; k < nComps; ++k) {
4979      lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
4980                                           sizeof(GfxColorComp));
4981      for (i = 0; i <= maxPixel; ++i) {
4982        mapped = decodeLow[k] + (i * decodeRange[k]) / maxPixel;
4983        lookup2[k][i] = dblToCol(mapped);
4984        if (useByteLookup) {
4985          int byte;
4986
4987          byte = (int) (mapped * 255.0 + 0.5);
4988          if (byte < 0)
4989            byte = 0;
4990          else if (byte > 255)
4991            byte = 255;
4992          byte_lookup[i * nComps + k] = byte;
4993        }
4994      }
4995    }
4996  }
4997
4998  return;
4999
5000 err2:
5001  obj.free();
5002 err1:
5003  ok = gFalse;
5004}
5005
5006GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
5007  int n, i, k;
5008
5009  colorSpace = colorMap->colorSpace->copy();
5010  bits = colorMap->bits;
5011  nComps = colorMap->nComps;
5012  nComps2 = colorMap->nComps2;
5013  colorSpace2 = NULL;
5014  for (k = 0; k < gfxColorMaxComps; ++k) {
5015    lookup[k] = NULL;
5016  }
5017  n = 1 << bits;
5018  if (colorSpace->getMode() == csIndexed) {
5019    colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
5020    for (k = 0; k < nComps2; ++k) {
5021      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
5022      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
5023    }
5024  } else if (colorSpace->getMode() == csSeparation) {
5025    colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
5026    for (k = 0; k < nComps2; ++k) {
5027      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
5028      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
5029    }
5030  } else {
5031    for (k = 0; k < nComps; ++k) {
5032      lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
5033      memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
5034    }
5035  }
5036  if (colorMap->byte_lookup) {
5037    int nc = colorSpace2 ? nComps2 : nComps;
5038
5039    byte_lookup = (Guchar *)gmallocn (n, nc);
5040    memcpy(byte_lookup, colorMap->byte_lookup, n * nc);
5041  }
5042  for (i = 0; i < nComps; ++i) {
5043    decodeLow[i] = colorMap->decodeLow[i];
5044    decodeRange[i] = colorMap->decodeRange[i];
5045  }
5046  ok = gTrue;
5047}
5048
5049GfxImageColorMap::~GfxImageColorMap() {
5050  int i;
5051
5052  delete colorSpace;
5053  for (i = 0; i < gfxColorMaxComps; ++i) {
5054    gfree(lookup[i]);
5055    gfree(lookup2[i]);
5056  }
5057  gfree(byte_lookup);
5058}
5059
5060void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
5061  GfxColor color;
5062  int i;
5063
5064  if (colorSpace2) {
5065    for (i = 0; i < nComps2; ++i) {
5066      color.c[i] = lookup2[i][x[0]];
5067    }
5068    colorSpace2->getGray(&color, gray);
5069  } else {
5070    for (i = 0; i < nComps; ++i) {
5071      color.c[i] = lookup2[i][x[i]];
5072    }
5073    colorSpace->getGray(&color, gray);
5074  }
5075}
5076
5077void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
5078  GfxColor color;
5079  int i;
5080
5081  if (colorSpace2) {
5082    for (i = 0; i < nComps2; ++i) {
5083      color.c[i] = lookup2[i][x[0]];
5084    }
5085    colorSpace2->getRGB(&color, rgb);
5086  } else {
5087    for (i = 0; i < nComps; ++i) {
5088      color.c[i] = lookup2[i][x[i]];
5089    }
5090    colorSpace->getRGB(&color, rgb);
5091  }
5092}
5093
5094void GfxImageColorMap::getGrayLine(Guchar *in, Guchar *out, int length) {
5095  int i, j;
5096  Guchar *inp, *tmp_line;
5097
5098  if ((colorSpace2 && !colorSpace2->useGetGrayLine ()) ||
5099      (!colorSpace2 && !colorSpace->useGetGrayLine ())) {
5100    GfxGray gray;
5101
5102    inp = in;
5103    for (i = 0; i < length; i++) {
5104      getGray (inp, &gray);
5105      out[i] = colToByte(gray);
5106      inp += nComps;
5107    }
5108    return;
5109  }
5110
5111  switch (colorSpace->getMode()) {
5112  case csIndexed:
5113  case csSeparation:
5114    tmp_line = (Guchar *) gmallocn (length, nComps2);
5115    for (i = 0; i < length; i++) {
5116      for (j = 0; j < nComps2; j++) {
5117        tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
5118      }
5119    }
5120    colorSpace2->getGrayLine(tmp_line, out, length);
5121    gfree (tmp_line);
5122    break;
5123
5124  default:
5125    inp = in;
5126    for (j = 0; j < length; j++)
5127      for (i = 0; i < nComps; i++) {
5128        *inp = byte_lookup[*inp * nComps + i];
5129        inp++;
5130      }
5131    colorSpace->getGrayLine(in, out, length);
5132    break;
5133  }
5134
5135}
5136
5137void GfxImageColorMap::getRGBLine(Guchar *in, unsigned int *out, int length) {
5138  int i, j;
5139  Guchar *inp, *tmp_line;
5140
5141  if (!useRGBLine()) {
5142    GfxRGB rgb;
5143
5144    inp = in;
5145    for (i = 0; i < length; i++) {
5146      getRGB (inp, &rgb);
5147      out[i] =
5148          ((int) colToByte(rgb.r) << 16) |
5149          ((int) colToByte(rgb.g) << 8) |
5150          ((int) colToByte(rgb.b) << 0);
5151      inp += nComps;
5152    }
5153    return;
5154  }
5155
5156  switch (colorSpace->getMode()) {
5157  case csIndexed:
5158  case csSeparation:
5159    tmp_line = (Guchar *) gmallocn (length, nComps2);
5160    for (i = 0; i < length; i++) {
5161      for (j = 0; j < nComps2; j++) {
5162        tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
5163      }
5164    }
5165    colorSpace2->getRGBLine(tmp_line, out, length);
5166    gfree (tmp_line);
5167    break;
5168
5169  default:
5170    inp = in;
5171    for (j = 0; j < length; j++)
5172      for (i = 0; i < nComps; i++) {
5173        *inp = byte_lookup[*inp * nComps + i];
5174        inp++;
5175      }
5176    colorSpace->getRGBLine(in, out, length);
5177    break;
5178  }
5179
5180}
5181
5182void GfxImageColorMap::getRGBLine(Guchar *in, Guchar *out, int length) {
5183  int i, j;
5184  Guchar *inp, *tmp_line;
5185
5186  if (!useRGBLine()) {
5187    GfxRGB rgb;
5188
5189    inp = in;
5190    for (i = 0; i < length; i++) {
5191      getRGB (inp, &rgb);
5192      *out++ = colToByte(rgb.r);
5193      *out++ = colToByte(rgb.g);
5194      *out++ = colToByte(rgb.b);
5195      inp += nComps;
5196    }
5197    return;
5198  }
5199
5200  switch (colorSpace->getMode()) {
5201  case csIndexed:
5202  case csSeparation:
5203    tmp_line = (Guchar *) gmallocn (length, nComps2);
5204    for (i = 0; i < length; i++) {
5205      for (j = 0; j < nComps2; j++) {
5206        tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
5207      }
5208    }
5209    colorSpace2->getRGBLine(tmp_line, out, length);
5210    gfree (tmp_line);
5211    break;
5212
5213  default:
5214    inp = in;
5215    for (j = 0; j < length; j++)
5216      for (i = 0; i < nComps; i++) {
5217        *inp = byte_lookup[*inp * nComps + i];
5218        inp++;
5219      }
5220    colorSpace->getRGBLine(in, out, length);
5221    break;
5222  }
5223
5224}
5225
5226void GfxImageColorMap::getRGBXLine(Guchar *in, Guchar *out, int length) {
5227  int i, j;
5228  Guchar *inp, *tmp_line;
5229
5230  if (!useRGBLine()) {
5231    GfxRGB rgb;
5232
5233    inp = in;
5234    for (i = 0; i < length; i++) {
5235      getRGB (inp, &rgb);
5236      *out++ = colToByte(rgb.r);
5237      *out++ = colToByte(rgb.g);
5238      *out++ = colToByte(rgb.b);
5239      *out++ = 255;
5240      inp += nComps;
5241    }
5242    return;
5243  }
5244
5245  switch (colorSpace->getMode()) {
5246  case csIndexed:
5247  case csSeparation:
5248    tmp_line = (Guchar *) gmallocn (length, nComps2);
5249    for (i = 0; i < length; i++) {
5250      for (j = 0; j < nComps2; j++) {
5251        tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
5252      }
5253    }
5254    colorSpace2->getRGBXLine(tmp_line, out, length);
5255    gfree (tmp_line);
5256    break;
5257
5258  default:
5259    inp = in;
5260    for (j = 0; j < length; j++)
5261      for (i = 0; i < nComps; i++) {
5262        *inp = byte_lookup[*inp * nComps + i];
5263        inp++;
5264      }
5265    colorSpace->getRGBXLine(in, out, length);
5266    break;
5267  }
5268
5269}
5270
5271void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
5272  GfxColor color;
5273  int i;
5274
5275  if (colorSpace2) {
5276    for (i = 0; i < nComps2; ++i) {
5277      color.c[i] = lookup2[i][x[0]];
5278    }
5279    colorSpace2->getCMYK(&color, cmyk);
5280  } else {
5281    for (i = 0; i < nComps; ++i) {
5282      color.c[i] = lookup[i][x[i]];
5283    }
5284    colorSpace->getCMYK(&color, cmyk);
5285  }
5286}
5287
5288void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
5289  int maxPixel, i;
5290
5291  maxPixel = (1 << bits) - 1;
5292  for (i = 0; i < nComps; ++i) {
5293    color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
5294  }
5295}
5296
5297//------------------------------------------------------------------------
5298// GfxSubpath and GfxPath
5299//------------------------------------------------------------------------
5300
5301GfxSubpath::GfxSubpath(double x1, double y1) {
5302  size = 16;
5303  x = (double *)gmallocn(size, sizeof(double));
5304  y = (double *)gmallocn(size, sizeof(double));
5305  curve = (GBool *)gmallocn(size, sizeof(GBool));
5306  n = 1;
5307  x[0] = x1;
5308  y[0] = y1;
5309  curve[0] = gFalse;
5310  closed = gFalse;
5311}
5312
5313GfxSubpath::~GfxSubpath() {
5314  gfree(x);
5315  gfree(y);
5316  gfree(curve);
5317}
5318
5319// Used for copy().
5320GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
5321  size = subpath->size;
5322  n = subpath->n;
5323  x = (double *)gmallocn(size, sizeof(double));
5324  y = (double *)gmallocn(size, sizeof(double));
5325  curve = (GBool *)gmallocn(size, sizeof(GBool));
5326  memcpy(x, subpath->x, n * sizeof(double));
5327  memcpy(y, subpath->y, n * sizeof(double));
5328  memcpy(curve, subpath->curve, n * sizeof(GBool));
5329  closed = subpath->closed;
5330}
5331
5332void GfxSubpath::lineTo(double x1, double y1) {
5333  if (n >= size) {
5334    size *= 2;
5335    x = (double *)greallocn(x, size, sizeof(double));
5336    y = (double *)greallocn(y, size, sizeof(double));
5337    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
5338  }
5339  x[n] = x1;
5340  y[n] = y1;
5341  curve[n] = gFalse;
5342  ++n;
5343}
5344
5345void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
5346                         double x3, double y3) {
5347  if (n+3 > size) {
5348    size *= 2;
5349    x = (double *)greallocn(x, size, sizeof(double));
5350    y = (double *)greallocn(y, size, sizeof(double));
5351    curve = (GBool *)greallocn(curve, size, sizeof(GBool));
5352  }
5353  x[n] = x1;
5354  y[n] = y1;
5355  x[n+1] = x2;
5356  y[n+1] = y2;
5357  x[n+2] = x3;
5358  y[n+2] = y3;
5359  curve[n] = curve[n+1] = gTrue;
5360  curve[n+2] = gFalse;
5361  n += 3;
5362}
5363
5364void GfxSubpath::close() {
5365  if (x[n-1] != x[0] || y[n-1] != y[0]) {
5366    lineTo(x[0], y[0]);
5367  }
5368  closed = gTrue;
5369}
5370
5371void GfxSubpath::offset(double dx, double dy) {
5372  int i;
5373
5374  for (i = 0; i < n; ++i) {
5375    x[i] += dx;
5376    y[i] += dy;
5377  }
5378}