source: trunk/poppler/mypoppler/poppler/GfxState.cc @ 290

Last change on this file since 290 was 290, checked in by rbri, 12 years ago

PDF plugin: Poppler library updated to version 0.12.4

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