fix getsup (HH)
[luatex.git] / source / libs / poppler / poppler-src / poppler / GfxState.cc
blob6ac18748ade169f820fd01be22cb5d1a40a52be2
1 //========================================================================
2 //
3 // GfxState.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17 // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
19 // Copyright (C) 2006-2016 Albert Astals Cid <aacid@kde.org>
20 // Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp>
21 // Copyright (C) 2009, 2011-2016 Thomas Freitag <Thomas.Freitag@alfa.de>
22 // Copyright (C) 2009 Christian Persch <chpe@gnome.org>
23 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com>
24 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
25 // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
26 // Copyright (C) 2012 William Bader <williambader@hotmail.com>
27 // Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
28 // Copyright (C) 2013 Hib Eris <hib@hiberis.nl>
29 // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it>
30 // Copyright (C) 2015 Adrian Johnson <ajohnson@redneon.com>
31 // Copyright (C) 2016 Marek Kasik <mkasik@redhat.com>
33 // To see a description of the changes please see the Changelog file that
34 // came with your tarball or type make ChangeLog if you are building from git
36 //========================================================================
38 #include <config.h>
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
42 #endif
44 #include <algorithm>
45 #include <stddef.h>
46 #include <math.h>
47 #include <string.h>
48 #include "goo/gmem.h"
49 #include "Error.h"
50 #include "Object.h"
51 #include "Array.h"
52 #include "Page.h"
53 #include "Gfx.h"
54 #include "GfxState.h"
55 #include "GfxState_helpers.h"
56 #include "GfxFont.h"
57 #include "GlobalParams.h"
58 #include "PopplerCache.h"
59 #include "OutputDev.h"
60 #include "splash/SplashTypes.h"
62 //------------------------------------------------------------------------
64 // Max depth of nested color spaces. This is used to catch infinite
65 // loops in the color space object structure.
66 #define colorSpaceRecursionLimit 8
68 //------------------------------------------------------------------------
70 GBool Matrix::invertTo(Matrix *other) const
72 double det;
74 det = 1 / determinant();
75 other->m[0] = m[3] * det;
76 other->m[1] = -m[1] * det;
77 other->m[2] = -m[2] * det;
78 other->m[3] = m[0] * det;
79 other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det;
80 other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det;
82 return gTrue;
85 void Matrix::translate(double tx, double ty)
87 double x0 = tx*m[0] + ty*m[2] + m[4];
88 double y0 = tx*m[1] + ty*m[3] + m[5];
89 m[4] = x0;
90 m[5] = y0;
93 void Matrix::scale(double sx, double sy)
95 m[0] *= sx;
96 m[1] *= sx;
97 m[2] *= sy;
98 m[3] *= sy;
101 void Matrix::transform(double x, double y, double *tx, double *ty) const
103 double temp_x, temp_y;
105 temp_x = m[0] * x + m[2] * y + m[4];
106 temp_y = m[1] * x + m[3] * y + m[5];
108 *tx = temp_x;
109 *ty = temp_y;
112 // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
113 double Matrix::norm() const
115 double f, g, h, i, j;
117 i = m[0]*m[0] + m[1]*m[1];
118 j = m[2]*m[2] + m[3]*m[3];
120 f = 0.5 * (i + j);
121 g = 0.5 * (i - j);
122 h = m[0]*m[2] + m[1]*m[3];
124 return sqrt (f + hypot (g, h));
127 //------------------------------------------------------------------------
129 struct GfxBlendModeInfo {
130 const char *name;
131 GfxBlendMode mode;
134 static const GfxBlendModeInfo gfxBlendModeNames[] = {
135 { "Normal", gfxBlendNormal },
136 { "Compatible", gfxBlendNormal },
137 { "Multiply", gfxBlendMultiply },
138 { "Screen", gfxBlendScreen },
139 { "Overlay", gfxBlendOverlay },
140 { "Darken", gfxBlendDarken },
141 { "Lighten", gfxBlendLighten },
142 { "ColorDodge", gfxBlendColorDodge },
143 { "ColorBurn", gfxBlendColorBurn },
144 { "HardLight", gfxBlendHardLight },
145 { "SoftLight", gfxBlendSoftLight },
146 { "Difference", gfxBlendDifference },
147 { "Exclusion", gfxBlendExclusion },
148 { "Hue", gfxBlendHue },
149 { "Saturation", gfxBlendSaturation },
150 { "Color", gfxBlendColor },
151 { "Luminosity", gfxBlendLuminosity }
154 #define nGfxBlendModeNames \
155 ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
157 //------------------------------------------------------------------------
159 // NB: This must match the GfxColorSpaceMode enum defined in
160 // GfxState.h
161 static const char *gfxColorSpaceModeNames[] = {
162 "DeviceGray",
163 "CalGray",
164 "DeviceRGB",
165 "CalRGB",
166 "DeviceCMYK",
167 "Lab",
168 "ICCBased",
169 "Indexed",
170 "Separation",
171 "DeviceN",
172 "Pattern"
175 #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
177 #ifdef USE_CMS
179 static const std::map<unsigned int, unsigned int>::size_type CMSCACHE_LIMIT = 2048;
181 #ifdef USE_LCMS1
182 #include <lcms.h>
183 #define cmsColorSpaceSignature icColorSpaceSignature
184 #define cmsSetLogErrorHandler cmsSetErrorHandler
185 #define cmsSigXYZData icSigXYZData
186 #define cmsSigLuvData icSigLuvData
187 #define cmsSigLabData icSigLabData
188 #define cmsSigYCbCrData icSigYCbCrData
189 #define cmsSigYxyData icSigYxyData
190 #define cmsSigRgbData icSigRgbData
191 #define cmsSigHsvData icSigHsvData
192 #define cmsSigHlsData icSigHlsData
193 #define cmsSigCmyData icSigCmyData
194 #define cmsSig3colorData icSig3colorData
195 #define cmsSigGrayData icSigGrayData
196 #define cmsSigCmykData icSigCmykData
197 #define cmsSig4colorData icSig4colorData
198 #define cmsSig2colorData icSig2colorData
199 #define cmsSig5colorData icSig5colorData
200 #define cmsSig6colorData icSig6colorData
201 #define cmsSig7colorData icSig7colorData
202 #define cmsSig8colorData icSig8colorData
203 #define cmsSig9colorData icSig9colorData
204 #define cmsSig10colorData icSig10colorData
205 #define cmsSig11colorData icSig11colorData
206 #define cmsSig12colorData icSig12colorData
207 #define cmsSig13colorData icSig13colorData
208 #define cmsSig14colorData icSig14colorData
209 #define cmsSig15colorData icSig15colorData
210 #define LCMS_FLAGS 0
211 #else
212 #include <lcms2.h>
213 #define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION
214 #endif
216 #define COLOR_PROFILE_DIR "/ColorProfiles/"
217 #define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR
219 void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) {
220 cmsDoTransform(transform, in, out, size);
223 // transformA should be a cmsHTRANSFORM
224 GfxColorTransform::GfxColorTransform(void *transformA, int cmsIntentA, unsigned int inputPixelTypeA, unsigned int transformPixelTypeA) {
225 transform = transformA;
226 refCount = 1;
227 cmsIntent = cmsIntentA;
228 inputPixelType = inputPixelTypeA;
229 transformPixelType = transformPixelTypeA;
232 GfxColorTransform::~GfxColorTransform() {
233 cmsDeleteTransform(transform);
236 void GfxColorTransform::ref() {
237 refCount++;
240 unsigned int GfxColorTransform::unref() {
241 return --refCount;
244 static cmsHPROFILE RGBProfile = NULL;
245 static GooString *displayProfileName = NULL; // display profile file Name
246 static cmsHPROFILE displayProfile = NULL; // display profile
247 static unsigned int displayPixelType = 0;
248 static GfxColorTransform *XYZ2DisplayTransform = NULL;
250 // convert color space signature to cmsColor type
251 static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs);
252 static unsigned int getCMSNChannels(cmsColorSpaceSignature cs);
253 static cmsHPROFILE loadColorProfile(const char *fileName);
255 void GfxColorSpace::setDisplayProfile(void *displayProfileA) {
256 displayProfile = displayProfileA;
257 if (displayProfile != NULL) {
258 cmsHTRANSFORM transform;
259 unsigned int nChannels;
261 displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
262 nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
263 // create transform from XYZ
264 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
265 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
266 displayProfile,
267 COLORSPACE_SH(displayPixelType) |
268 CHANNELS_SH(nChannels) | BYTES_SH(1),
269 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
270 error(errSyntaxWarning, -1, "Can't create Lab transform");
271 } else {
272 XYZ2DisplayTransform = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, displayPixelType);
274 cmsCloseProfile(XYZProfile);
278 void GfxColorSpace::setDisplayProfileName(GooString *name) {
279 displayProfileName = name->copy();
282 cmsHPROFILE GfxColorSpace::getRGBProfile() {
283 return RGBProfile;
286 cmsHPROFILE GfxColorSpace::getDisplayProfile() {
287 return displayProfile;
290 #endif
292 //------------------------------------------------------------------------
293 // GfxColorSpace
294 //------------------------------------------------------------------------
296 GfxColorSpace::GfxColorSpace() {
297 overprintMask = 0x0f;
298 mapping = NULL;
301 GfxColorSpace::~GfxColorSpace() {
304 GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion) {
305 GfxColorSpace *cs;
306 Object obj1;
308 if (recursion > colorSpaceRecursionLimit) {
309 error(errSyntaxError, -1, "Loop detected in color space objects");
310 return NULL;
313 cs = NULL;
314 if (csObj->isName()) {
315 if (csObj->isName("DeviceGray") || csObj->isName("G")) {
316 if (res != NULL) {
317 Object objCS;
318 res->lookupColorSpace("DefaultGray", &objCS);
319 if (objCS.isNull()) {
320 cs = new GfxDeviceGrayColorSpace();
321 } else {
322 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
324 objCS.free();
325 } else {
326 cs = new GfxDeviceGrayColorSpace();
328 } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
329 if (res != NULL) {
330 Object objCS;
331 res->lookupColorSpace("DefaultRGB", &objCS);
332 if (objCS.isNull()) {
333 cs = new GfxDeviceRGBColorSpace();
334 } else {
335 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
337 objCS.free();
338 } else {
339 cs = new GfxDeviceRGBColorSpace();
341 } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
342 if (res != NULL) {
343 Object objCS;
344 res->lookupColorSpace("DefaultCMYK", &objCS);
345 if (objCS.isNull()) {
346 cs = new GfxDeviceCMYKColorSpace();
347 } else {
348 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
350 objCS.free();
351 } else {
352 cs = new GfxDeviceCMYKColorSpace();
354 } else if (csObj->isName("Pattern")) {
355 cs = new GfxPatternColorSpace(NULL);
356 } else {
357 error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName());
359 } else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
360 csObj->arrayGet(0, &obj1);
361 if (obj1.isName("DeviceGray") || obj1.isName("G")) {
362 if (res != NULL) {
363 Object objCS;
364 res->lookupColorSpace("DefaultGray", &objCS);
365 if (objCS.isNull()) {
366 cs = new GfxDeviceGrayColorSpace();
367 } else {
368 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
370 objCS.free();
371 } else {
372 cs = new GfxDeviceGrayColorSpace();
374 } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
375 if (res != NULL) {
376 Object objCS;
377 res->lookupColorSpace("DefaultRGB", &objCS);
378 if (objCS.isNull()) {
379 cs = new GfxDeviceRGBColorSpace();
380 } else {
381 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
383 objCS.free();
384 } else {
385 cs = new GfxDeviceRGBColorSpace();
387 } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
388 if (res != NULL) {
389 Object objCS;
390 res->lookupColorSpace("DefaultCMYK", &objCS);
391 if (objCS.isNull()) {
392 cs = new GfxDeviceCMYKColorSpace();
393 } else {
394 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
396 objCS.free();
397 } else {
398 cs = new GfxDeviceCMYKColorSpace();
400 } else if (obj1.isName("CalGray")) {
401 cs = GfxCalGrayColorSpace::parse(csObj->getArray(), state);
402 } else if (obj1.isName("CalRGB")) {
403 cs = GfxCalRGBColorSpace::parse(csObj->getArray(), state);
404 } else if (obj1.isName("Lab")) {
405 cs = GfxLabColorSpace::parse(csObj->getArray(), state);
406 } else if (obj1.isName("ICCBased")) {
407 cs = GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion);
408 } else if (obj1.isName("Indexed") || obj1.isName("I")) {
409 cs = GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion);
410 } else if (obj1.isName("Separation")) {
411 cs = GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion);
412 } else if (obj1.isName("DeviceN")) {
413 cs = GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion);
414 } else if (obj1.isName("Pattern")) {
415 cs = GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion);
416 } else {
417 error(errSyntaxWarning, -1, "Bad color space");
419 obj1.free();
420 } else if (csObj->isDict()) {
421 csObj->dictLookup("ColorSpace", &obj1);
422 if (obj1.isName("DeviceGray")) {
423 if (res != NULL) {
424 Object objCS;
425 res->lookupColorSpace("DefaultGray", &objCS);
426 if (objCS.isNull()) {
427 cs = new GfxDeviceGrayColorSpace();
428 } else {
429 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
431 objCS.free();
432 } else {
433 cs = new GfxDeviceGrayColorSpace();
435 } else if (obj1.isName("DeviceRGB")) {
436 if (res != NULL) {
437 Object objCS;
438 res->lookupColorSpace("DefaultRGB", &objCS);
439 if (objCS.isNull()) {
440 cs = new GfxDeviceRGBColorSpace();
441 } else {
442 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
444 objCS.free();
445 } else {
446 cs = new GfxDeviceRGBColorSpace();
448 } else if (obj1.isName("DeviceCMYK")) {
449 if (res != NULL) {
450 Object objCS;
451 res->lookupColorSpace("DefaultCMYK", &objCS);
452 if (objCS.isNull()) {
453 cs = new GfxDeviceCMYKColorSpace();
454 } else {
455 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
457 objCS.free();
458 } else {
459 cs = new GfxDeviceCMYKColorSpace();
461 } else {
462 error(errSyntaxWarning, -1, "Bad color space dict'");
464 obj1.free();
465 } else {
466 error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict");
468 return cs;
471 void GfxColorSpace::createMapping(GooList *separationList, int maxSepComps) {
472 return;
475 void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
476 int maxImgPixel) {
477 int i;
479 for (i = 0; i < getNComps(); ++i) {
480 decodeLow[i] = 0;
481 decodeRange[i] = 1;
485 int GfxColorSpace::getNumColorSpaceModes() {
486 return nGfxColorSpaceModes;
489 const char *GfxColorSpace::getColorSpaceModeName(int idx) {
490 return gfxColorSpaceModeNames[idx];
493 #ifdef USE_CMS
494 cmsHPROFILE loadColorProfile(const char *fileName)
496 cmsHPROFILE hp = NULL;
497 FILE *fp;
499 if (fileName[0] == '/') {
500 // full path
501 // check if open the file
502 if ((fp = fopen(fileName,"r")) != NULL) {
503 fclose(fp);
504 hp = cmsOpenProfileFromFile(fileName,"r");
506 return hp;
508 // try to load from global directory
509 GooString *path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
510 path->append(fileName);
511 // check if open the file
512 if ((fp = fopen(path->getCString(),"r")) != NULL) {
513 fclose(fp);
514 hp = cmsOpenProfileFromFile(path->getCString(),"r");
516 delete path;
517 return hp;
520 #ifdef USE_LCMS1
521 static int CMSError(int ecode, const char *msg)
523 error(errSyntaxWarning, -1, "{0:s}", msg);
524 return 1;
526 #else
527 static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text)
529 error(errSyntaxWarning, -1, "{0:s}", text);
531 #endif
533 int GfxColorSpace::setupColorProfiles()
535 static GBool initialized = gFalse;
536 cmsHTRANSFORM transform;
537 unsigned int nChannels;
539 // do only once
540 if (initialized) return 0;
541 initialized = gTrue;
543 // set error handlor
544 cmsSetLogErrorHandler(CMSError);
546 if (displayProfile == NULL) {
547 // load display profile if it was not already loaded.
548 if (displayProfileName == NULL) {
549 displayProfile = loadColorProfile("display.icc");
550 } else if (displayProfileName->getLength() > 0) {
551 displayProfile = loadColorProfile(displayProfileName->getCString());
554 // load RGB profile
555 RGBProfile = loadColorProfile("RGB.icc");
556 if (RGBProfile == NULL) {
557 /* use built in sRGB profile */
558 RGBProfile = cmsCreate_sRGBProfile();
560 // create transforms
561 if (displayProfile != NULL) {
562 displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
563 nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
564 // create transform from XYZ
565 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
566 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
567 displayProfile,
568 COLORSPACE_SH(displayPixelType) |
569 CHANNELS_SH(nChannels) | BYTES_SH(1),
570 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
571 error(errSyntaxWarning, -1, "Can't create Lab transform");
572 } else {
573 XYZ2DisplayTransform = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, displayPixelType);
575 cmsCloseProfile(XYZProfile);
577 return 0;
580 unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs)
582 switch (cs) {
583 case cmsSigXYZData:
584 return PT_XYZ;
585 break;
586 case cmsSigLabData:
587 return PT_Lab;
588 break;
589 case cmsSigLuvData:
590 return PT_YUV;
591 break;
592 case cmsSigYCbCrData:
593 return PT_YCbCr;
594 break;
595 case cmsSigYxyData:
596 return PT_Yxy;
597 break;
598 case cmsSigRgbData:
599 return PT_RGB;
600 break;
601 case cmsSigGrayData:
602 return PT_GRAY;
603 break;
604 case cmsSigHsvData:
605 return PT_HSV;
606 break;
607 case cmsSigHlsData:
608 return PT_HLS;
609 break;
610 case cmsSigCmykData:
611 return PT_CMYK;
612 break;
613 case cmsSigCmyData:
614 return PT_CMY;
615 break;
616 case cmsSig2colorData:
617 case cmsSig3colorData:
618 case cmsSig4colorData:
619 case cmsSig5colorData:
620 case cmsSig6colorData:
621 case cmsSig7colorData:
622 case cmsSig8colorData:
623 case cmsSig9colorData:
624 case cmsSig10colorData:
625 case cmsSig11colorData:
626 case cmsSig12colorData:
627 case cmsSig13colorData:
628 case cmsSig14colorData:
629 case cmsSig15colorData:
630 default:
631 break;
633 return PT_RGB;
636 unsigned int getCMSNChannels(cmsColorSpaceSignature cs)
638 switch (cs) {
639 case cmsSigXYZData:
640 case cmsSigLuvData:
641 case cmsSigLabData:
642 case cmsSigYCbCrData:
643 case cmsSigYxyData:
644 case cmsSigRgbData:
645 case cmsSigHsvData:
646 case cmsSigHlsData:
647 case cmsSigCmyData:
648 case cmsSig3colorData:
649 return 3;
650 break;
651 case cmsSigGrayData:
652 return 1;
653 break;
654 case cmsSigCmykData:
655 case cmsSig4colorData:
656 return 4;
657 break;
658 case cmsSig2colorData:
659 return 2;
660 break;
661 case cmsSig5colorData:
662 return 5;
663 break;
664 case cmsSig6colorData:
665 return 6;
666 break;
667 case cmsSig7colorData:
668 return 7;
669 break;
670 case cmsSig8colorData:
671 return 8;
672 break;
673 case cmsSig9colorData:
674 return 9;
675 break;
676 case cmsSig10colorData:
677 return 10;
678 break;
679 case cmsSig11colorData:
680 return 11;
681 break;
682 case cmsSig12colorData:
683 return 12;
684 break;
685 case cmsSig13colorData:
686 return 13;
687 break;
688 case cmsSig14colorData:
689 return 14;
690 break;
691 case cmsSig15colorData:
692 return 15;
693 default:
694 break;
696 return 3;
698 #endif
700 //------------------------------------------------------------------------
701 // GfxDeviceGrayColorSpace
702 //------------------------------------------------------------------------
704 GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
707 GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
710 GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
711 return new GfxDeviceGrayColorSpace();
714 void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
715 *gray = clip01(color->c[0]);
718 void GfxDeviceGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
719 memcpy (out, in, length);
722 void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
723 rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
726 void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, unsigned int *out,
727 int length) {
728 int i;
730 for (i = 0; i < length; i++)
731 out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0);
734 void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
735 for (int i = 0; i < length; i++) {
736 *out++ = in[i];
737 *out++ = in[i];
738 *out++ = in[i];
742 void GfxDeviceGrayColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
743 for (int i = 0; i < length; i++) {
744 *out++ = in[i];
745 *out++ = in[i];
746 *out++ = in[i];
747 *out++ = 255;
751 void GfxDeviceGrayColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
752 for (int i = 0; i < length; i++) {
753 *out++ = 0;
754 *out++ = 0;
755 *out++ = 0;
756 *out++ = in[i];
760 void GfxDeviceGrayColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
761 for (int i = 0; i < length; i++) {
762 for (int j = 0; j < SPOT_NCOMPS+4; j++)
763 out[j] = 0;
764 out[4] = in[i];
765 out += (SPOT_NCOMPS+4);
769 void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
770 cmyk->c = cmyk->m = cmyk->y = 0;
771 cmyk->k = clip01(gfxColorComp1 - color->c[0]);
774 void GfxDeviceGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
775 for (int i = 0; i < gfxColorMaxComps; i++)
776 deviceN->c[i] = 0;
777 deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]);
780 void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
781 color->c[0] = 0;
784 //------------------------------------------------------------------------
785 // GfxCalGrayColorSpace
786 //------------------------------------------------------------------------
788 GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
789 whiteX = whiteY = whiteZ = 1;
790 blackX = blackY = blackZ = 0;
791 gamma = 1;
794 GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
795 #ifdef USE_CMS
796 if (transform != NULL) {
797 if (transform->unref() == 0) delete transform;
799 #endif
802 GfxColorSpace *GfxCalGrayColorSpace::copy() {
803 GfxCalGrayColorSpace *cs;
805 cs = new GfxCalGrayColorSpace();
806 cs->whiteX = whiteX;
807 cs->whiteY = whiteY;
808 cs->whiteZ = whiteZ;
809 cs->blackX = blackX;
810 cs->blackY = blackY;
811 cs->blackZ = blackZ;
812 cs->gamma = gamma;
813 cs->kr = kr;
814 cs->kg = kg;
815 cs->kb = kb;
816 #ifdef USE_CMS
817 cs->transform = transform;
818 if (transform != NULL) transform->ref();
819 #endif
820 return cs;
823 // This is the inverse of MatrixLMN in Example 4.10 from the PostScript
824 // Language Reference, Third Edition.
825 static const double xyzrgb[3][3] = {
826 { 3.240449, -1.537136, -0.498531 },
827 { -0.969265, 1.876011, 0.041556 },
828 { 0.055643, -0.204026, 1.057229 }
831 GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, GfxState *state) {
832 GfxCalGrayColorSpace *cs;
833 Object obj1, obj2, obj3;
835 arr->get(1, &obj1);
836 if (!obj1.isDict()) {
837 error(errSyntaxWarning, -1, "Bad CalGray color space");
838 obj1.free();
839 return NULL;
841 cs = new GfxCalGrayColorSpace();
842 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
843 obj2.arrayGetLength() == 3) {
844 obj2.arrayGet(0, &obj3);
845 if (likely(obj3.isNum()))
846 cs->whiteX = obj3.getNum();
847 obj3.free();
848 obj2.arrayGet(1, &obj3);
849 if (likely(obj3.isNum()))
850 cs->whiteY = obj3.getNum();
851 obj3.free();
852 obj2.arrayGet(2, &obj3);
853 if (likely(obj3.isNum()))
854 cs->whiteZ = obj3.getNum();
855 obj3.free();
857 obj2.free();
858 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
859 obj2.arrayGetLength() == 3) {
860 obj2.arrayGet(0, &obj3);
861 if (likely(obj3.isNum()))
862 cs->blackX = obj3.getNum();
863 obj3.free();
864 obj2.arrayGet(1, &obj3);
865 if (likely(obj3.isNum()))
866 cs->blackY = obj3.getNum();
867 obj3.free();
868 obj2.arrayGet(2, &obj3);
869 if (likely(obj3.isNum()))
870 cs->blackZ = obj3.getNum();
871 obj3.free();
873 obj2.free();
874 if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
875 cs->gamma = obj2.getNum();
877 obj2.free();
878 obj1.free();
880 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
881 xyzrgb[0][1] * cs->whiteY +
882 xyzrgb[0][2] * cs->whiteZ);
883 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
884 xyzrgb[1][1] * cs->whiteY +
885 xyzrgb[1][2] * cs->whiteZ);
886 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
887 xyzrgb[2][1] * cs->whiteY +
888 xyzrgb[2][2] * cs->whiteZ);
889 #ifdef USE_CMS
890 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
891 if (cs->transform != NULL) cs->transform->ref();
892 #endif
893 return cs;
896 // convert CalGray to media XYZ color space
897 // (not multiply by the white point)
898 void GfxCalGrayColorSpace::getXYZ(GfxColor *color,
899 double *pX, double *pY, double *pZ) {
900 const double A = colToDbl(color->c[0]);
901 const double xyzColor = pow(A,gamma);
902 *pX = xyzColor;
903 *pY = xyzColor;
904 *pZ = xyzColor;
907 void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
908 GfxRGB rgb;
910 #ifdef USE_CMS
911 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
912 Guchar out[gfxColorMaxComps];
913 double in[gfxColorMaxComps];
914 double X, Y, Z;
916 getXYZ(color,&X,&Y,&Z);
917 in[0] = clip01(X);
918 in[1] = clip01(Y);
919 in[2] = clip01(Z);
920 transform->doTransform(in,out,1);
921 *gray = byteToCol(out[0]);
922 return;
924 #endif
925 getRGB(color, &rgb);
926 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
927 0.587 * rgb.g +
928 0.114 * rgb.b + 0.5));
931 void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
932 double X, Y, Z;
933 double r, g, b;
935 getXYZ(color,&X,&Y,&Z);
936 #ifdef USE_CMS
937 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
938 Guchar out[gfxColorMaxComps];
939 double in[gfxColorMaxComps];
941 in[0] = clip01(X);
942 in[1] = clip01(Y);
943 in[2] = clip01(Z);
944 transform->doTransform(in,out,1);
945 rgb->r = byteToCol(out[0]);
946 rgb->g = byteToCol(out[1]);
947 rgb->b = byteToCol(out[2]);
948 return;
950 #endif
951 X *= whiteX;
952 Y *= whiteY;
953 Z *= whiteZ;
954 // convert XYZ to RGB, including gamut mapping and gamma correction
955 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
956 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
957 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
958 rgb->r = dblToCol(sqrt(clip01(r * kr)));
959 rgb->g = dblToCol(sqrt(clip01(g * kg)));
960 rgb->b = dblToCol(sqrt(clip01(b * kb)));
963 void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
964 GfxRGB rgb;
965 GfxColorComp c, m, y, k;
967 #ifdef USE_CMS
968 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
969 double in[gfxColorMaxComps];
970 Guchar out[gfxColorMaxComps];
971 double X, Y, Z;
973 getXYZ(color,&X,&Y,&Z);
974 in[0] = clip01(X);
975 in[1] = clip01(Y);
976 in[2] = clip01(Z);
978 transform->doTransform(in,out,1);
979 cmyk->c = byteToCol(out[0]);
980 cmyk->m = byteToCol(out[1]);
981 cmyk->y = byteToCol(out[2]);
982 cmyk->k = byteToCol(out[3]);
983 return;
985 #endif
986 getRGB(color, &rgb);
987 c = clip01(gfxColorComp1 - rgb.r);
988 m = clip01(gfxColorComp1 - rgb.g);
989 y = clip01(gfxColorComp1 - rgb.b);
990 k = c;
991 if (m < k) {
992 k = m;
994 if (y < k) {
995 k = y;
997 cmyk->c = c - k;
998 cmyk->m = m - k;
999 cmyk->y = y - k;
1000 cmyk->k = k;
1003 void GfxCalGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1004 GfxCMYK cmyk;
1005 for (int i = 0; i < gfxColorMaxComps; i++)
1006 deviceN->c[i] = 0;
1007 getCMYK(color, &cmyk);
1008 deviceN->c[0] = cmyk.c;
1009 deviceN->c[1] = cmyk.m;
1010 deviceN->c[2] = cmyk.y;
1011 deviceN->c[3] = cmyk.k;
1014 void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
1015 color->c[0] = 0;
1018 //------------------------------------------------------------------------
1019 // GfxDeviceRGBColorSpace
1020 //------------------------------------------------------------------------
1022 GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
1025 GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
1028 GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
1029 return new GfxDeviceRGBColorSpace();
1032 void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1033 *gray = clip01((GfxColorComp)(0.3 * color->c[0] +
1034 0.59 * color->c[1] +
1035 0.11 * color->c[2] + 0.5));
1038 void GfxDeviceRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
1039 int i;
1041 for (i = 0; i < length; i++) {
1042 out[i] =
1043 (in[i * 3 + 0] * 19595 +
1044 in[i * 3 + 1] * 38469 +
1045 in[i * 3 + 2] * 7472) / 65536;
1049 void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1050 rgb->r = clip01(color->c[0]);
1051 rgb->g = clip01(color->c[1]);
1052 rgb->b = clip01(color->c[2]);
1055 void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out,
1056 int length) {
1057 Guchar *p;
1058 int i;
1060 for (i = 0, p = in; i < length; i++, p += 3)
1061 out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0);
1064 void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
1065 for (int i = 0; i < length; i++) {
1066 *out++ = *in++;
1067 *out++ = *in++;
1068 *out++ = *in++;
1072 void GfxDeviceRGBColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
1073 for (int i = 0; i < length; i++) {
1074 *out++ = *in++;
1075 *out++ = *in++;
1076 *out++ = *in++;
1077 *out++ = 255;
1081 void GfxDeviceRGBColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
1082 GfxColorComp c, m, y, k;
1084 for (int i = 0; i < length; i++) {
1085 c = byteToCol(255 - *in++);
1086 m = byteToCol(255 - *in++);
1087 y = byteToCol(255 - *in++);
1088 k = c;
1089 if (m < k) {
1090 k = m;
1092 if (y < k) {
1093 k = y;
1095 *out++ = colToByte(c - k);
1096 *out++ = colToByte(m - k);
1097 *out++ = colToByte(y - k);
1098 *out++ = colToByte(k);
1102 void GfxDeviceRGBColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
1103 GfxColorComp c, m, y, k;
1105 for (int i = 0; i < length; i++) {
1106 for (int j = 0; j < SPOT_NCOMPS+4; j++)
1107 out[j] = 0;
1108 c = byteToCol(255 - *in++);
1109 m = byteToCol(255 - *in++);
1110 y = byteToCol(255 - *in++);
1111 k = c;
1112 if (m < k) {
1113 k = m;
1115 if (y < k) {
1116 k = y;
1118 out[0] = colToByte(c - k);
1119 out[1] = colToByte(m - k);
1120 out[2] = colToByte(y - k);
1121 out[3] = colToByte(k);
1122 out += (SPOT_NCOMPS+4);
1126 void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1127 GfxColorComp c, m, y, k;
1129 c = clip01(gfxColorComp1 - color->c[0]);
1130 m = clip01(gfxColorComp1 - color->c[1]);
1131 y = clip01(gfxColorComp1 - color->c[2]);
1132 k = c;
1133 if (m < k) {
1134 k = m;
1136 if (y < k) {
1137 k = y;
1139 cmyk->c = c - k;
1140 cmyk->m = m - k;
1141 cmyk->y = y - k;
1142 cmyk->k = k;
1145 void GfxDeviceRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1146 GfxCMYK cmyk;
1147 for (int i = 0; i < gfxColorMaxComps; i++)
1148 deviceN->c[i] = 0;
1149 getCMYK(color, &cmyk);
1150 deviceN->c[0] = cmyk.c;
1151 deviceN->c[1] = cmyk.m;
1152 deviceN->c[2] = cmyk.y;
1153 deviceN->c[3] = cmyk.k;
1156 void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
1157 color->c[0] = 0;
1158 color->c[1] = 0;
1159 color->c[2] = 0;
1162 //------------------------------------------------------------------------
1163 // GfxCalRGBColorSpace
1164 //------------------------------------------------------------------------
1166 GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
1167 whiteX = whiteY = whiteZ = 1;
1168 blackX = blackY = blackZ = 0;
1169 gammaR = gammaG = gammaB = 1;
1170 mat[0] = 1; mat[1] = 0; mat[2] = 0;
1171 mat[3] = 0; mat[4] = 1; mat[5] = 0;
1172 mat[6] = 0; mat[7] = 0; mat[8] = 1;
1175 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
1176 #ifdef USE_CMS
1177 if (transform != NULL) {
1178 if (transform->unref() == 0) delete transform;
1180 #endif
1183 GfxColorSpace *GfxCalRGBColorSpace::copy() {
1184 GfxCalRGBColorSpace *cs;
1185 int i;
1187 cs = new GfxCalRGBColorSpace();
1188 cs->whiteX = whiteX;
1189 cs->whiteY = whiteY;
1190 cs->whiteZ = whiteZ;
1191 cs->blackX = blackX;
1192 cs->blackY = blackY;
1193 cs->blackZ = blackZ;
1194 cs->gammaR = gammaR;
1195 cs->gammaG = gammaG;
1196 cs->gammaB = gammaB;
1197 cs->kr = kr;
1198 cs->kg = kg;
1199 cs->kb = kb;
1200 for (i = 0; i < 9; ++i) {
1201 cs->mat[i] = mat[i];
1203 #ifdef USE_CMS
1204 cs->transform = transform;
1205 if (transform != NULL) transform->ref();
1206 #endif
1207 return cs;
1210 GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, GfxState *state) {
1211 GfxCalRGBColorSpace *cs;
1212 Object obj1, obj2, obj3;
1213 int i;
1215 arr->get(1, &obj1);
1216 if (!obj1.isDict()) {
1217 error(errSyntaxWarning, -1, "Bad CalRGB color space");
1218 obj1.free();
1219 return NULL;
1221 cs = new GfxCalRGBColorSpace();
1222 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
1223 obj2.arrayGetLength() == 3) {
1224 obj2.arrayGet(0, &obj3);
1225 if (likely(obj3.isNum()))
1226 cs->whiteX = obj3.getNum();
1227 obj3.free();
1228 obj2.arrayGet(1, &obj3);
1229 if (likely(obj3.isNum()))
1230 cs->whiteY = obj3.getNum();
1231 obj3.free();
1232 obj2.arrayGet(2, &obj3);
1233 if (likely(obj3.isNum()))
1234 cs->whiteZ = obj3.getNum();
1235 obj3.free();
1237 obj2.free();
1238 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
1239 obj2.arrayGetLength() == 3) {
1240 obj2.arrayGet(0, &obj3);
1241 if (likely(obj3.isNum()))
1242 cs->blackX = obj3.getNum();
1243 obj3.free();
1244 obj2.arrayGet(1, &obj3);
1245 if (likely(obj3.isNum()))
1246 cs->blackY = obj3.getNum();
1247 obj3.free();
1248 obj2.arrayGet(2, &obj3);
1249 if (likely(obj3.isNum()))
1250 cs->blackZ = obj3.getNum();
1251 obj3.free();
1253 obj2.free();
1254 if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
1255 obj2.arrayGetLength() == 3) {
1256 obj2.arrayGet(0, &obj3);
1257 if (likely(obj3.isNum()))
1258 cs->gammaR = obj3.getNum();
1259 obj3.free();
1260 obj2.arrayGet(1, &obj3);
1261 if (likely(obj3.isNum()))
1262 cs->gammaG = obj3.getNum();
1263 obj3.free();
1264 obj2.arrayGet(2, &obj3);
1265 if (likely(obj3.isNum()))
1266 cs->gammaB = obj3.getNum();
1267 obj3.free();
1269 obj2.free();
1270 if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
1271 obj2.arrayGetLength() == 9) {
1272 for (i = 0; i < 9; ++i) {
1273 obj2.arrayGet(i, &obj3);
1274 if (likely(obj3.isNum()))
1275 cs->mat[i] = obj3.getNum();
1276 obj3.free();
1279 obj2.free();
1280 obj1.free();
1282 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1283 xyzrgb[0][1] * cs->whiteY +
1284 xyzrgb[0][2] * cs->whiteZ);
1285 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1286 xyzrgb[1][1] * cs->whiteY +
1287 xyzrgb[1][2] * cs->whiteZ);
1288 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1289 xyzrgb[2][1] * cs->whiteY +
1290 xyzrgb[2][2] * cs->whiteZ);
1292 #ifdef USE_CMS
1293 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
1294 if (cs->transform != NULL) cs->transform->ref();
1295 #endif
1296 return cs;
1299 // convert CalRGB to XYZ color space
1300 void GfxCalRGBColorSpace::getXYZ(GfxColor *color,
1301 double *pX, double *pY, double *pZ) {
1302 double A, B, C;
1304 A = pow(colToDbl(color->c[0]), gammaR);
1305 B = pow(colToDbl(color->c[1]), gammaG);
1306 C = pow(colToDbl(color->c[2]), gammaB);
1307 *pX = mat[0] * A + mat[3] * B + mat[6] * C;
1308 *pY = mat[1] * A + mat[4] * B + mat[7] * C;
1309 *pZ = mat[2] * A + mat[5] * B + mat[8] * C;
1312 void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1313 GfxRGB rgb;
1315 #ifdef USE_CMS
1316 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
1317 Guchar out[gfxColorMaxComps];
1318 double in[gfxColorMaxComps];
1319 double X, Y, Z;
1321 getXYZ(color,&X,&Y,&Z);
1322 in[0] = clip01(X);
1323 in[1] = clip01(Y);
1324 in[2] = clip01(Z);
1325 transform->doTransform(in,out,1);
1326 *gray = byteToCol(out[0]);
1327 return;
1329 #endif
1330 getRGB(color, &rgb);
1331 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1332 0.587 * rgb.g +
1333 0.114 * rgb.b + 0.5));
1336 void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1337 double X, Y, Z;
1338 double r, g, b;
1340 getXYZ(color,&X,&Y,&Z);
1341 #ifdef USE_CMS
1342 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
1343 Guchar out[gfxColorMaxComps];
1344 double in[gfxColorMaxComps];
1346 in[0] = clip01(X/whiteX);
1347 in[1] = clip01(Y/whiteY);
1348 in[2] = clip01(Z/whiteZ);
1349 transform->doTransform(in,out,1);
1350 rgb->r = byteToCol(out[0]);
1351 rgb->g = byteToCol(out[1]);
1352 rgb->b = byteToCol(out[2]);
1353 return;
1355 #endif
1356 // convert XYZ to RGB, including gamut mapping and gamma correction
1357 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1358 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1359 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1360 rgb->r = dblToCol(sqrt(clip01(r)));
1361 rgb->g = dblToCol(sqrt(clip01(g)));
1362 rgb->b = dblToCol(sqrt(clip01(b)));
1365 void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1366 GfxRGB rgb;
1367 GfxColorComp c, m, y, k;
1369 #ifdef USE_CMS
1370 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1371 double in[gfxColorMaxComps];
1372 Guchar out[gfxColorMaxComps];
1373 double X, Y, Z;
1375 getXYZ(color,&X,&Y,&Z);
1376 in[0] = clip01(X);
1377 in[1] = clip01(Y);
1378 in[2] = clip01(Z);
1379 transform->doTransform(in,out,1);
1380 cmyk->c = byteToCol(out[0]);
1381 cmyk->m = byteToCol(out[1]);
1382 cmyk->y = byteToCol(out[2]);
1383 cmyk->k = byteToCol(out[3]);
1384 return;
1386 #endif
1387 getRGB(color, &rgb);
1388 c = clip01(gfxColorComp1 - rgb.r);
1389 m = clip01(gfxColorComp1 - rgb.g);
1390 y = clip01(gfxColorComp1 - rgb.b);
1391 k = c;
1392 if (m < k) {
1393 k = m;
1395 if (y < k) {
1396 k = y;
1398 cmyk->c = c - k;
1399 cmyk->m = m - k;
1400 cmyk->y = y - k;
1401 cmyk->k = k;
1404 void GfxCalRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1405 GfxCMYK cmyk;
1406 for (int i = 0; i < gfxColorMaxComps; i++)
1407 deviceN->c[i] = 0;
1408 getCMYK(color, &cmyk);
1409 deviceN->c[0] = cmyk.c;
1410 deviceN->c[1] = cmyk.m;
1411 deviceN->c[2] = cmyk.y;
1412 deviceN->c[3] = cmyk.k;
1415 void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
1416 color->c[0] = 0;
1417 color->c[1] = 0;
1418 color->c[2] = 0;
1421 //------------------------------------------------------------------------
1422 // GfxDeviceCMYKColorSpace
1423 //------------------------------------------------------------------------
1425 GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
1428 GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
1431 GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
1432 return new GfxDeviceCMYKColorSpace();
1435 void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1436 *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
1437 - 0.3 * color->c[0]
1438 - 0.59 * color->c[1]
1439 - 0.11 * color->c[2] + 0.5));
1442 void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1443 double c, m, y, k, c1, m1, y1, k1, r, g, b;
1445 c = colToDbl(color->c[0]);
1446 m = colToDbl(color->c[1]);
1447 y = colToDbl(color->c[2]);
1448 k = colToDbl(color->c[3]);
1449 c1 = 1 - c;
1450 m1 = 1 - m;
1451 y1 = 1 - y;
1452 k1 = 1 - k;
1453 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1454 rgb->r = clip01(dblToCol(r));
1455 rgb->g = clip01(dblToCol(g));
1456 rgb->b = clip01(dblToCol(b));
1459 static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(Guchar *&in, double &r, double &g, double &b)
1461 double c, m, y, k, c1, m1, y1, k1;
1463 c = byteToDbl(*in++);
1464 m = byteToDbl(*in++);
1465 y = byteToDbl(*in++);
1466 k = byteToDbl(*in++);
1467 c1 = 1 - c;
1468 m1 = 1 - m;
1469 y1 = 1 - y;
1470 k1 = 1 - k;
1471 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1474 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length)
1476 double r, g, b;
1477 for (int i = 0; i < length; i++) {
1478 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1479 *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b));
1483 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
1485 double r, g, b;
1487 for (int i = 0; i < length; i++) {
1488 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1489 *out++ = dblToByte(clip01(r));
1490 *out++ = dblToByte(clip01(g));
1491 *out++ = dblToByte(clip01(b));
1495 void GfxDeviceCMYKColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
1497 double r, g, b;
1499 for (int i = 0; i < length; i++) {
1500 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1501 *out++ = dblToByte(clip01(r));
1502 *out++ = dblToByte(clip01(g));
1503 *out++ = dblToByte(clip01(b));
1504 *out++ = 255;
1508 void GfxDeviceCMYKColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length)
1510 for (int i = 0; i < length; i++) {
1511 *out++ = *in++;
1512 *out++ = *in++;
1513 *out++ = *in++;
1514 *out++ = *in++;
1518 void GfxDeviceCMYKColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length)
1520 for (int i = 0; i < length; i++) {
1521 for (int j = 0; j < SPOT_NCOMPS+4; j++)
1522 out[j] = 0;
1523 out[0] = *in++;
1524 out[1] = *in++;
1525 out[2] = *in++;
1526 out[3] = *in++;
1527 out += (SPOT_NCOMPS+4);
1531 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1532 cmyk->c = clip01(color->c[0]);
1533 cmyk->m = clip01(color->c[1]);
1534 cmyk->y = clip01(color->c[2]);
1535 cmyk->k = clip01(color->c[3]);
1538 void GfxDeviceCMYKColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1539 for (int i = 0; i < gfxColorMaxComps; i++)
1540 deviceN->c[i] = 0;
1541 deviceN->c[0] = clip01(color->c[0]);
1542 deviceN->c[1] = clip01(color->c[1]);
1543 deviceN->c[2] = clip01(color->c[2]);
1544 deviceN->c[3] = clip01(color->c[3]);
1547 void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
1548 color->c[0] = 0;
1549 color->c[1] = 0;
1550 color->c[2] = 0;
1551 color->c[3] = gfxColorComp1;
1554 //------------------------------------------------------------------------
1555 // GfxLabColorSpace
1556 //------------------------------------------------------------------------
1558 GfxLabColorSpace::GfxLabColorSpace() {
1559 whiteX = whiteY = whiteZ = 1;
1560 blackX = blackY = blackZ = 0;
1561 aMin = bMin = -100;
1562 aMax = bMax = 100;
1565 GfxLabColorSpace::~GfxLabColorSpace() {
1566 #ifdef USE_CMS
1567 if (transform != NULL) {
1568 if (transform->unref() == 0) delete transform;
1570 #endif
1573 GfxColorSpace *GfxLabColorSpace::copy() {
1574 GfxLabColorSpace *cs;
1576 cs = new GfxLabColorSpace();
1577 cs->whiteX = whiteX;
1578 cs->whiteY = whiteY;
1579 cs->whiteZ = whiteZ;
1580 cs->blackX = blackX;
1581 cs->blackY = blackY;
1582 cs->blackZ = blackZ;
1583 cs->aMin = aMin;
1584 cs->aMax = aMax;
1585 cs->bMin = bMin;
1586 cs->bMax = bMax;
1587 cs->kr = kr;
1588 cs->kg = kg;
1589 cs->kb = kb;
1590 #ifdef USE_CMS
1591 cs->transform = transform;
1592 if (transform != NULL) transform->ref();
1593 #endif
1594 return cs;
1597 GfxColorSpace *GfxLabColorSpace::parse(Array *arr, GfxState *state) {
1598 GfxLabColorSpace *cs;
1599 Object obj1, obj2, obj3;
1601 arr->get(1, &obj1);
1602 if (!obj1.isDict()) {
1603 error(errSyntaxWarning, -1, "Bad Lab color space");
1604 obj1.free();
1605 return NULL;
1607 cs = new GfxLabColorSpace();
1608 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
1609 obj2.arrayGetLength() == 3) {
1610 obj2.arrayGet(0, &obj3);
1611 cs->whiteX = obj3.getNum();
1612 obj3.free();
1613 obj2.arrayGet(1, &obj3);
1614 cs->whiteY = obj3.getNum();
1615 obj3.free();
1616 obj2.arrayGet(2, &obj3);
1617 cs->whiteZ = obj3.getNum();
1618 obj3.free();
1620 obj2.free();
1621 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
1622 obj2.arrayGetLength() == 3) {
1623 obj2.arrayGet(0, &obj3);
1624 cs->blackX = obj3.getNum();
1625 obj3.free();
1626 obj2.arrayGet(1, &obj3);
1627 cs->blackY = obj3.getNum();
1628 obj3.free();
1629 obj2.arrayGet(2, &obj3);
1630 cs->blackZ = obj3.getNum();
1631 obj3.free();
1633 obj2.free();
1634 if (obj1.dictLookup("Range", &obj2)->isArray() &&
1635 obj2.arrayGetLength() == 4) {
1636 obj2.arrayGet(0, &obj3);
1637 cs->aMin = obj3.getNum();
1638 obj3.free();
1639 obj2.arrayGet(1, &obj3);
1640 cs->aMax = obj3.getNum();
1641 obj3.free();
1642 obj2.arrayGet(2, &obj3);
1643 cs->bMin = obj3.getNum();
1644 obj3.free();
1645 obj2.arrayGet(3, &obj3);
1646 cs->bMax = obj3.getNum();
1647 obj3.free();
1649 obj2.free();
1650 obj1.free();
1652 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1653 xyzrgb[0][1] * cs->whiteY +
1654 xyzrgb[0][2] * cs->whiteZ);
1655 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1656 xyzrgb[1][1] * cs->whiteY +
1657 xyzrgb[1][2] * cs->whiteZ);
1658 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1659 xyzrgb[2][1] * cs->whiteY +
1660 xyzrgb[2][2] * cs->whiteZ);
1662 #ifdef USE_CMS
1663 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
1664 if (cs->transform != NULL) cs->transform->ref();
1665 #endif
1666 return cs;
1669 void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1670 GfxRGB rgb;
1672 #ifdef USE_CMS
1673 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
1674 Guchar out[gfxColorMaxComps];
1675 double in[gfxColorMaxComps];
1677 getXYZ(color, &in[0], &in[1], &in[2]);
1678 transform->doTransform(in,out,1);
1679 *gray = byteToCol(out[0]);
1680 return;
1682 #endif
1683 getRGB(color, &rgb);
1684 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1685 0.587 * rgb.g +
1686 0.114 * rgb.b + 0.5));
1689 // convert L*a*b* to media XYZ color space
1690 // (not multiply by the white point)
1691 void GfxLabColorSpace::getXYZ(GfxColor *color,
1692 double *pX, double *pY, double *pZ) {
1693 double X, Y, Z;
1694 double t1, t2;
1696 t1 = (colToDbl(color->c[0]) + 16) / 116;
1697 t2 = t1 + colToDbl(color->c[1]) / 500;
1698 if (t2 >= (6.0 / 29.0)) {
1699 X = t2 * t2 * t2;
1700 } else {
1701 X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1703 if (t1 >= (6.0 / 29.0)) {
1704 Y = t1 * t1 * t1;
1705 } else {
1706 Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
1708 t2 = t1 - colToDbl(color->c[2]) / 200;
1709 if (t2 >= (6.0 / 29.0)) {
1710 Z = t2 * t2 * t2;
1711 } else {
1712 Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1714 *pX = X;
1715 *pY = Y;
1716 *pZ = Z;
1719 void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1720 double X, Y, Z;
1721 double r, g, b;
1723 getXYZ(color, &X, &Y, &Z);
1724 #ifdef USE_CMS
1725 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
1726 Guchar out[gfxColorMaxComps];
1727 double in[gfxColorMaxComps];
1729 in[0] = clip01(X);
1730 in[1] = clip01(Y);
1731 in[2] = clip01(Z);
1732 transform->doTransform(in,out,1);
1733 rgb->r = byteToCol(out[0]);
1734 rgb->g = byteToCol(out[1]);
1735 rgb->b = byteToCol(out[2]);
1736 return;
1737 } else if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1738 Guchar out[gfxColorMaxComps];
1739 double in[gfxColorMaxComps];
1740 double c, m, y, k, c1, m1, y1, k1, r, g, b;
1742 in[0] = clip01(X);
1743 in[1] = clip01(Y);
1744 in[2] = clip01(Z);
1745 transform->doTransform(in,out,1);
1746 c = byteToDbl(out[0]);
1747 m = byteToDbl(out[1]);
1748 y = byteToDbl(out[2]);
1749 k = byteToDbl(out[3]);
1750 c1 = 1 - c;
1751 m1 = 1 - m;
1752 y1 = 1 - y;
1753 k1 = 1 - k;
1754 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1755 rgb->r = clip01(dblToCol(r));
1756 rgb->g = clip01(dblToCol(g));
1757 rgb->b = clip01(dblToCol(b));
1758 return;
1760 #endif
1761 X *= whiteX;
1762 Y *= whiteY;
1763 Z *= whiteZ;
1764 // convert XYZ to RGB, including gamut mapping and gamma correction
1765 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1766 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1767 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1768 rgb->r = dblToCol(sqrt(clip01(r * kr)));
1769 rgb->g = dblToCol(sqrt(clip01(g * kg)));
1770 rgb->b = dblToCol(sqrt(clip01(b * kb)));
1773 void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1774 GfxRGB rgb;
1775 GfxColorComp c, m, y, k;
1777 #ifdef USE_CMS
1778 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1779 double in[gfxColorMaxComps];
1780 Guchar out[gfxColorMaxComps];
1782 getXYZ(color, &in[0], &in[1], &in[2]);
1783 transform->doTransform(in,out,1);
1784 cmyk->c = byteToCol(out[0]);
1785 cmyk->m = byteToCol(out[1]);
1786 cmyk->y = byteToCol(out[2]);
1787 cmyk->k = byteToCol(out[3]);
1788 return;
1790 #endif
1791 getRGB(color, &rgb);
1792 c = clip01(gfxColorComp1 - rgb.r);
1793 m = clip01(gfxColorComp1 - rgb.g);
1794 y = clip01(gfxColorComp1 - rgb.b);
1795 k = c;
1796 if (m < k) {
1797 k = m;
1799 if (y < k) {
1800 k = y;
1802 cmyk->c = c - k;
1803 cmyk->m = m - k;
1804 cmyk->y = y - k;
1805 cmyk->k = k;
1808 void GfxLabColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1809 GfxCMYK cmyk;
1810 for (int i = 0; i < gfxColorMaxComps; i++)
1811 deviceN->c[i] = 0;
1812 getCMYK(color, &cmyk);
1813 deviceN->c[0] = cmyk.c;
1814 deviceN->c[1] = cmyk.m;
1815 deviceN->c[2] = cmyk.y;
1816 deviceN->c[3] = cmyk.k;
1819 void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
1820 color->c[0] = 0;
1821 if (aMin > 0) {
1822 color->c[1] = dblToCol(aMin);
1823 } else if (aMax < 0) {
1824 color->c[1] = dblToCol(aMax);
1825 } else {
1826 color->c[1] = 0;
1828 if (bMin > 0) {
1829 color->c[2] = dblToCol(bMin);
1830 } else if (bMax < 0) {
1831 color->c[2] = dblToCol(bMax);
1832 } else {
1833 color->c[2] = 0;
1837 void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
1838 int maxImgPixel) {
1839 decodeLow[0] = 0;
1840 decodeRange[0] = 100;
1841 decodeLow[1] = aMin;
1842 decodeRange[1] = aMax - aMin;
1843 decodeLow[2] = bMin;
1844 decodeRange[2] = bMax - bMin;
1847 //------------------------------------------------------------------------
1848 // GfxICCBasedColorSpace
1849 //------------------------------------------------------------------------
1851 class GfxICCBasedColorSpaceKey : public PopplerCacheKey
1853 public:
1854 GfxICCBasedColorSpaceKey(int numA, int genA) : num(numA), gen(genA)
1858 bool operator==(const PopplerCacheKey &key) const
1860 const GfxICCBasedColorSpaceKey *k = static_cast<const GfxICCBasedColorSpaceKey*>(&key);
1861 return k->num == num && k->gen == gen;
1864 int num, gen;
1867 class GfxICCBasedColorSpaceItem : public PopplerCacheItem
1869 public:
1870 GfxICCBasedColorSpaceItem(GfxICCBasedColorSpace *csA)
1872 cs = static_cast<GfxICCBasedColorSpace*>(csA->copy());
1875 ~GfxICCBasedColorSpaceItem()
1877 delete cs;
1880 GfxICCBasedColorSpace *cs;
1883 GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
1884 Ref *iccProfileStreamA) {
1885 nComps = nCompsA;
1886 alt = altA;
1887 iccProfileStream = *iccProfileStreamA;
1888 rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
1889 rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
1890 #ifdef USE_CMS
1891 transform = NULL;
1892 lineTransform = NULL;
1893 #endif
1896 GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
1897 delete alt;
1898 #ifdef USE_CMS
1899 if (transform != NULL) {
1900 if (transform->unref() == 0) delete transform;
1902 if (lineTransform != NULL) {
1903 if (lineTransform->unref() == 0) delete lineTransform;
1905 #endif
1908 GfxColorSpace *GfxICCBasedColorSpace::copy() {
1909 GfxICCBasedColorSpace *cs;
1910 int i;
1912 cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
1913 for (i = 0; i < 4; ++i) {
1914 cs->rangeMin[i] = rangeMin[i];
1915 cs->rangeMax[i] = rangeMax[i];
1917 #ifdef USE_CMS
1918 cs->transform = transform;
1919 if (transform != NULL) transform->ref();
1920 cs->lineTransform = lineTransform;
1921 if (lineTransform != NULL) lineTransform->ref();
1922 #endif
1923 return cs;
1926 GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState *state, int recursion) {
1927 GfxICCBasedColorSpace *cs;
1928 Ref iccProfileStreamA;
1929 int nCompsA;
1930 GfxColorSpace *altA;
1931 Dict *dict;
1932 Object obj1, obj2, obj3;
1933 int i;
1935 if (arr->getLength() < 2) {
1936 error(errSyntaxError, -1, "Bad ICCBased color space");
1937 return NULL;
1939 arr->getNF(1, &obj1);
1940 if (obj1.isRef()) {
1941 iccProfileStreamA = obj1.getRef();
1942 } else {
1943 iccProfileStreamA.num = 0;
1944 iccProfileStreamA.gen = 0;
1946 obj1.free();
1947 #ifdef USE_CMS
1948 // check cache
1949 if (out && iccProfileStreamA.num > 0) {
1950 GfxICCBasedColorSpaceKey k(iccProfileStreamA.num, iccProfileStreamA.gen);
1951 GfxICCBasedColorSpaceItem *item = static_cast<GfxICCBasedColorSpaceItem *>(out->getIccColorSpaceCache()->lookup(k));
1952 if (item != NULL)
1954 cs = static_cast<GfxICCBasedColorSpace*>(item->cs->copy());
1955 int transformIntent = cs->getIntent();
1956 int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
1957 if (state != NULL) {
1958 const char *intent = state->getRenderingIntent();
1959 if (intent != NULL) {
1960 if (strcmp(intent, "AbsoluteColorimetric") == 0) {
1961 cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC;
1962 } else if (strcmp(intent, "Saturation") == 0) {
1963 cmsIntent = INTENT_SATURATION;
1964 } else if (strcmp(intent, "Perceptual") == 0) {
1965 cmsIntent = INTENT_PERCEPTUAL;
1969 if (transformIntent == cmsIntent) {
1970 return cs;
1972 delete cs;
1975 #endif
1976 arr->get(1, &obj1);
1977 if (!obj1.isStream()) {
1978 error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)");
1979 obj1.free();
1980 return NULL;
1982 dict = obj1.streamGetDict();
1983 if (!dict->lookup("N", &obj2)->isInt()) {
1984 error(errSyntaxWarning, -1, "Bad ICCBased color space (N)");
1985 obj2.free();
1986 obj1.free();
1987 return NULL;
1989 nCompsA = obj2.getInt();
1990 obj2.free();
1991 if (nCompsA > 4) {
1992 error(errSyntaxError, -1,
1993 "ICCBased color space with too many ({0:d} > 4) components",
1994 nCompsA);
1995 nCompsA = 4;
1997 if (dict->lookup("Alternate", &obj2)->isNull() ||
1998 !(altA = GfxColorSpace::parse(NULL, &obj2, out, state, recursion + 1))) {
1999 switch (nCompsA) {
2000 case 1:
2001 altA = new GfxDeviceGrayColorSpace();
2002 break;
2003 case 3:
2004 altA = new GfxDeviceRGBColorSpace();
2005 break;
2006 case 4:
2007 altA = new GfxDeviceCMYKColorSpace();
2008 break;
2009 default:
2010 error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N");
2011 obj2.free();
2012 obj1.free();
2013 return NULL;
2016 obj2.free();
2017 if (altA->getNComps() != nCompsA) {
2018 error(errSyntaxWarning, -1, "Bad ICCBased color space - N doesn't match alt color space");
2019 delete altA;
2020 obj1.free();
2021 return NULL;
2023 cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
2024 if (dict->lookup("Range", &obj2)->isArray() &&
2025 obj2.arrayGetLength() == 2 * nCompsA) {
2026 Object obj4;
2027 for (i = 0; i < nCompsA; ++i) {
2028 obj2.arrayGet(2*i, &obj3);
2029 obj2.arrayGet(2*i+1, &obj4);
2030 if (obj3.isNum() && obj4.isNum()) {
2031 cs->rangeMin[i] = obj3.getNum();
2032 cs->rangeMax[i] = obj4.getNum();
2034 obj3.free();
2035 obj4.free();
2038 obj2.free();
2039 obj1.free();
2041 #ifdef USE_CMS
2042 arr->get(1, &obj1);
2043 Guchar *profBuf;
2044 Stream *iccStream = obj1.getStream();
2045 int length = 0;
2047 profBuf = iccStream->toUnsignedChars(&length, 65536, 65536);
2048 cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,length);
2049 gfree(profBuf);
2050 if (hp == 0) {
2051 error(errSyntaxWarning, -1, "read ICCBased color space profile error");
2052 } else {
2053 cmsHPROFILE dhp = (state != NULL && state->getDisplayProfile() != NULL) ? state->getDisplayProfile() : displayProfile;
2054 if (dhp == NULL) {
2055 if (unlikely(RGBProfile == NULL)) {
2056 GfxColorSpace::setupColorProfiles();
2058 dhp = RGBProfile;
2060 unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(hp));
2061 unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp));
2062 unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp));
2063 cmsHTRANSFORM transform;
2065 int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
2066 if (state != NULL) {
2067 const char *intent = state->getRenderingIntent();
2068 if (intent != NULL) {
2069 if (strcmp(intent, "AbsoluteColorimetric") == 0) {
2070 cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC;
2071 } else if (strcmp(intent, "Saturation") == 0) {
2072 cmsIntent = INTENT_SATURATION;
2073 } else if (strcmp(intent, "Perceptual") == 0) {
2074 cmsIntent = INTENT_PERCEPTUAL;
2078 if ((transform = cmsCreateTransform(hp,
2079 COLORSPACE_SH(cst) |CHANNELS_SH(nCompsA) | BYTES_SH(1),
2080 dhp,
2081 COLORSPACE_SH(dcst) |
2082 CHANNELS_SH(dNChannels) | BYTES_SH(1),
2083 cmsIntent, LCMS_FLAGS)) == 0) {
2084 error(errSyntaxWarning, -1, "Can't create transform");
2085 cs->transform = NULL;
2086 } else {
2087 cs->transform = new GfxColorTransform(transform, cmsIntent, cst, dcst);
2089 if (dcst == PT_RGB || dcst == PT_CMYK) {
2090 // create line transform only when the display is RGB type color space
2091 if ((transform = cmsCreateTransform(hp,
2092 CHANNELS_SH(nCompsA) | BYTES_SH(1),dhp,
2093 (dcst == PT_RGB) ? TYPE_RGB_8 : TYPE_CMYK_8, cmsIntent, LCMS_FLAGS)) == 0) {
2094 error(errSyntaxWarning, -1, "Can't create transform");
2095 cs->lineTransform = NULL;
2096 } else {
2097 cs->lineTransform = new GfxColorTransform(transform, cmsIntent, cst, dcst);
2100 cmsCloseProfile(hp);
2102 obj1.free();
2103 // put this colorSpace into cache
2104 if (out && iccProfileStreamA.num > 0) {
2105 GfxICCBasedColorSpaceKey *k = new GfxICCBasedColorSpaceKey(iccProfileStreamA.num, iccProfileStreamA.gen);
2106 GfxICCBasedColorSpaceItem *item = new GfxICCBasedColorSpaceItem(cs);
2107 out->getIccColorSpaceCache()->put(k, item);
2109 #endif
2110 return cs;
2113 void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2114 #ifdef USE_CMS
2115 if (transform != 0 && transform->getTransformPixelType() == PT_GRAY) {
2116 Guchar in[gfxColorMaxComps];
2117 Guchar out[gfxColorMaxComps];
2119 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2120 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2121 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2122 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2123 } else {
2124 for (int i = 0;i < nComps;i++) {
2125 in[i] = colToByte(color->c[i]);
2128 if (nComps <= 4) {
2129 unsigned int key = 0;
2130 for (int j = 0; j < nComps; j++) {
2131 key = (key << 8) + in[j];
2133 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2134 if (it != cmsCache.end()) {
2135 unsigned int value = it->second;
2136 *gray = byteToCol(value & 0xff);
2137 return;
2140 transform->doTransform(in,out,1);
2141 *gray = byteToCol(out[0]);
2142 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2143 unsigned int key = 0;
2144 for (int j = 0; j < nComps; j++) {
2145 key = (key << 8) + in[j];
2147 unsigned int value = out[0];
2148 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2150 } else {
2151 GfxRGB rgb;
2152 getRGB(color,&rgb);
2153 *gray = clip01((GfxColorComp)(0.3 * rgb.r +
2154 0.59 * rgb.g +
2155 0.11 * rgb.b + 0.5));
2157 #else
2158 alt->getGray(color, gray);
2159 #endif
2162 void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2163 #ifdef USE_CMS
2164 if (transform != 0 && transform->getTransformPixelType() == PT_RGB) {
2165 Guchar in[gfxColorMaxComps];
2166 Guchar out[gfxColorMaxComps];
2168 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2169 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2170 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2171 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2172 } else {
2173 for (int i = 0;i < nComps;i++) {
2174 in[i] = colToByte(color->c[i]);
2177 if (nComps <= 4) {
2178 unsigned int key = 0;
2179 for (int j = 0; j < nComps; j++) {
2180 key = (key << 8) + in[j];
2182 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2183 if (it != cmsCache.end()) {
2184 unsigned int value = it->second;
2185 rgb->r = byteToCol(value >> 16);
2186 rgb->g = byteToCol((value >> 8) & 0xff);
2187 rgb->b = byteToCol(value & 0xff);
2188 return;
2191 transform->doTransform(in,out,1);
2192 rgb->r = byteToCol(out[0]);
2193 rgb->g = byteToCol(out[1]);
2194 rgb->b = byteToCol(out[2]);
2195 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2196 unsigned int key = 0;
2197 for (int j = 0; j < nComps; j++) {
2198 key = (key << 8) + in[j];
2200 unsigned int value = (out[0] << 16) + (out[1] << 8) + out[2];
2201 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2203 } else if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
2204 Guchar in[gfxColorMaxComps];
2205 Guchar out[gfxColorMaxComps];
2206 double c, m, y, k, c1, m1, y1, k1, r, g, b;
2208 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2209 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2210 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2211 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2212 } else {
2213 for (int i = 0;i < nComps;i++) {
2214 in[i] = colToByte(color->c[i]);
2217 if (nComps <= 4) {
2218 unsigned int key = 0;
2219 for (int j = 0; j < nComps; j++) {
2220 key = (key << 8) + in[j];
2222 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2223 if (it != cmsCache.end()) {
2224 unsigned int value = it->second;
2225 rgb->r = byteToCol(value >> 16);
2226 rgb->g = byteToCol((value >> 8) & 0xff);
2227 rgb->b = byteToCol(value & 0xff);
2228 return;
2231 transform->doTransform(in,out,1);
2232 c = byteToDbl(out[0]);
2233 m = byteToDbl(out[1]);
2234 y = byteToDbl(out[2]);
2235 k = byteToDbl(out[3]);
2236 c1 = 1 - c;
2237 m1 = 1 - m;
2238 y1 = 1 - y;
2239 k1 = 1 - k;
2240 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
2241 rgb->r = clip01(dblToCol(r));
2242 rgb->g = clip01(dblToCol(g));
2243 rgb->b = clip01(dblToCol(b));
2244 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2245 unsigned int key = 0;
2246 for (int j = 0; j < nComps; j++) {
2247 key = (key << 8) + in[j];
2249 unsigned int value = (dblToByte(r) << 16) + (dblToByte(g) << 8) + dblToByte(b);
2250 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2252 } else {
2253 alt->getRGB(color, rgb);
2255 #else
2256 alt->getRGB(color, rgb);
2257 #endif
2260 void GfxICCBasedColorSpace::getRGBLine(Guchar *in, unsigned int *out,
2261 int length) {
2262 #ifdef USE_CMS
2263 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2264 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2265 lineTransform->doTransform(in, tmp, length);
2266 for (int i = 0; i < length; ++i) {
2267 Guchar *current = tmp + (i * 3);
2268 out[i] = (current[0] << 16) | (current[1] << 8) | current[2];
2270 gfree(tmp);
2271 } else {
2272 alt->getRGBLine(in, out, length);
2274 #else
2275 alt->getRGBLine(in, out, length);
2276 #endif
2279 void GfxICCBasedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
2280 #ifdef USE_CMS
2281 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2282 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2283 lineTransform->doTransform(in, tmp, length);
2284 Guchar *current = tmp;
2285 for (int i = 0; i < length; ++i) {
2286 *out++ = *current++;
2287 *out++ = *current++;
2288 *out++ = *current++;
2290 gfree(tmp);
2291 } else if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2292 Guchar* tmp = (Guchar *)gmallocn(4 * length, sizeof(Guchar));
2293 lineTransform->doTransform(in, tmp, length);
2294 Guchar *current = tmp;
2295 double c, m, y, k, c1, m1, y1, k1, r, g, b;
2296 for (int i = 0; i < length; ++i) {
2297 c = byteToDbl(*current++);
2298 m = byteToDbl(*current++);
2299 y = byteToDbl(*current++);
2300 k = byteToDbl(*current++);
2301 c1 = 1 - c;
2302 m1 = 1 - m;
2303 y1 = 1 - y;
2304 k1 = 1 - k;
2305 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
2306 *out++ = dblToByte(r);
2307 *out++ = dblToByte(g);
2308 *out++ = dblToByte(b);
2310 gfree(tmp);
2311 } else {
2312 alt->getRGBLine(in, out, length);
2314 #else
2315 alt->getRGBLine(in, out, length);
2316 #endif
2319 void GfxICCBasedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
2320 #ifdef USE_CMS
2321 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2322 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2323 lineTransform->doTransform(in, tmp, length);
2324 Guchar *current = tmp;
2325 for (int i = 0; i < length; ++i) {
2326 *out++ = *current++;
2327 *out++ = *current++;
2328 *out++ = *current++;
2329 *out++ = 255;
2331 gfree(tmp);
2332 } else {
2333 alt->getRGBXLine(in, out, length);
2335 #else
2336 alt->getRGBXLine(in, out, length);
2337 #endif
2340 void GfxICCBasedColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
2341 #ifdef USE_CMS
2342 if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2343 transform->doTransform(in,out,length);
2344 } else if (lineTransform != NULL && nComps != 4) {
2345 GfxColorComp c, m, y, k;
2346 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2347 getRGBLine(in, tmp, length);
2348 Guchar *p = tmp;
2349 for (int i = 0; i < length; i++) {
2350 c = byteToCol(255 - *p++);
2351 m = byteToCol(255 - *p++);
2352 y = byteToCol(255 - *p++);
2353 k = c;
2354 if (m < k) {
2355 k = m;
2357 if (y < k) {
2358 k = y;
2360 *out++ = colToByte(c - k);
2361 *out++ = colToByte(m - k);
2362 *out++ = colToByte(y - k);
2363 *out++ = colToByte(k);
2365 gfree(tmp);
2366 } else {
2367 alt->getCMYKLine(in, out, length);
2369 #else
2370 alt->getCMYKLine(in, out, length);
2371 #endif
2374 void GfxICCBasedColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
2375 #ifdef USE_CMS
2376 if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2377 Guchar* tmp = (Guchar *)gmallocn(4 * length, sizeof(Guchar));
2378 transform->doTransform(in,tmp,length);
2379 Guchar *p = tmp;
2380 for (int i = 0; i < length; i++) {
2381 for (int j = 0; j < 4; j++)
2382 *out++ = *p++;
2383 for (int j = 4; j < SPOT_NCOMPS+4; j++)
2384 *out++ = 0;
2386 gfree(tmp);
2387 } else if (lineTransform != NULL && nComps != 4) {
2388 GfxColorComp c, m, y, k;
2389 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2390 getRGBLine(in, tmp, length);
2391 Guchar *p = tmp;
2392 for (int i = 0; i < length; i++) {
2393 for (int j = 0; j < SPOT_NCOMPS+4; j++)
2394 out[j] = 0;
2395 c = byteToCol(255 - *p++);
2396 m = byteToCol(255 - *p++);
2397 y = byteToCol(255 - *p++);
2398 k = c;
2399 if (m < k) {
2400 k = m;
2402 if (y < k) {
2403 k = y;
2405 out[0] = colToByte(c - k);
2406 out[1] = colToByte(m - k);
2407 out[2] = colToByte(y - k);
2408 out[3] = colToByte(k);
2409 out += (SPOT_NCOMPS+4);
2411 gfree(tmp);
2412 } else {
2413 alt->getDeviceNLine(in, out, length);
2415 #else
2416 alt->getDeviceNLine(in, out, length);
2417 #endif
2420 void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2421 #ifdef USE_CMS
2422 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
2423 Guchar in[gfxColorMaxComps];
2424 Guchar out[gfxColorMaxComps];
2426 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2427 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2428 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2429 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2430 } else {
2431 for (int i = 0;i < nComps;i++) {
2432 in[i] = colToByte(color->c[i]);
2435 if (nComps <= 4) {
2436 unsigned int key = 0;
2437 for (int j = 0; j < nComps; j++) {
2438 key = (key << 8) + in[j];
2440 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2441 if (it != cmsCache.end()) {
2442 unsigned int value = it->second;
2443 cmyk->c = byteToCol(value >> 24);
2444 cmyk->m = byteToCol((value >> 16) & 0xff);
2445 cmyk->y = byteToCol((value >> 8) & 0xff);
2446 cmyk->k = byteToCol(value & 0xff);
2447 return;
2450 transform->doTransform(in,out,1);
2451 cmyk->c = byteToCol(out[0]);
2452 cmyk->m = byteToCol(out[1]);
2453 cmyk->y = byteToCol(out[2]);
2454 cmyk->k = byteToCol(out[3]);
2455 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2456 unsigned int key = 0;
2457 for (int j = 0; j < nComps; j++) {
2458 key = (key << 8) + in[j];
2460 unsigned int value = (out[0] << 24) + (out[1] << 16) + (out[2] << 8) + out[3];
2461 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2463 } else if (nComps != 4 && transform != NULL && transform->getTransformPixelType() == PT_RGB) {
2464 GfxRGB rgb;
2465 GfxColorComp c, m, y, k;
2467 getRGB(color,&rgb);
2468 c = clip01(gfxColorComp1 - rgb.r);
2469 m = clip01(gfxColorComp1 - rgb.g);
2470 y = clip01(gfxColorComp1 - rgb.b);
2471 k = c;
2472 if (m < k) {
2473 k = m;
2475 if (y < k) {
2476 k = y;
2478 cmyk->c = c - k;
2479 cmyk->m = m - k;
2480 cmyk->y = y - k;
2481 cmyk->k = k;
2482 } else {
2483 alt->getCMYK(color, cmyk);
2485 #else
2486 alt->getCMYK(color, cmyk);
2487 #endif
2490 GBool GfxICCBasedColorSpace::useGetRGBLine() {
2491 #ifdef USE_CMS
2492 return lineTransform != NULL || alt->useGetRGBLine();
2493 #else
2494 return alt->useGetRGBLine();
2495 #endif
2498 GBool GfxICCBasedColorSpace::useGetCMYKLine() {
2499 #ifdef USE_CMS
2500 return lineTransform != NULL || alt->useGetCMYKLine();
2501 #else
2502 return alt->useGetCMYKLine();
2503 #endif
2506 GBool GfxICCBasedColorSpace::useGetDeviceNLine() {
2507 #ifdef USE_CMS
2508 return lineTransform != NULL || alt->useGetDeviceNLine();
2509 #else
2510 return alt->useGetDeviceNLine();
2511 #endif
2514 void GfxICCBasedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2515 GfxCMYK cmyk;
2516 for (int i = 0; i < gfxColorMaxComps; i++)
2517 deviceN->c[i] = 0;
2518 getCMYK(color, &cmyk);
2519 deviceN->c[0] = cmyk.c;
2520 deviceN->c[1] = cmyk.m;
2521 deviceN->c[2] = cmyk.y;
2522 deviceN->c[3] = cmyk.k;
2525 void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
2526 int i;
2528 for (i = 0; i < nComps; ++i) {
2529 if (rangeMin[i] > 0) {
2530 color->c[i] = dblToCol(rangeMin[i]);
2531 } else if (rangeMax[i] < 0) {
2532 color->c[i] = dblToCol(rangeMax[i]);
2533 } else {
2534 color->c[i] = 0;
2539 void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
2540 double *decodeRange,
2541 int maxImgPixel) {
2542 alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
2544 #if 0
2545 // this is nominally correct, but some PDF files don't set the
2546 // correct ranges in the ICCBased dict
2547 int i;
2549 for (i = 0; i < nComps; ++i) {
2550 decodeLow[i] = rangeMin[i];
2551 decodeRange[i] = rangeMax[i] - rangeMin[i];
2553 #endif
2556 //------------------------------------------------------------------------
2557 // GfxIndexedColorSpace
2558 //------------------------------------------------------------------------
2560 GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
2561 int indexHighA) {
2562 base = baseA;
2563 indexHigh = indexHighA;
2564 lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
2565 sizeof(Guchar));
2566 overprintMask = base->getOverprintMask();
2569 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
2570 delete base;
2571 gfree(lookup);
2574 GfxColorSpace *GfxIndexedColorSpace::copy() {
2575 GfxIndexedColorSpace *cs;
2577 cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
2578 memcpy(cs->lookup, lookup,
2579 (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
2580 return cs;
2583 GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
2584 GfxIndexedColorSpace *cs;
2585 GfxColorSpace *baseA;
2586 int indexHighA;
2587 Object obj1;
2588 char *s;
2589 int n, i, j;
2591 if (arr->getLength() != 4) {
2592 error(errSyntaxWarning, -1, "Bad Indexed color space");
2593 goto err1;
2595 arr->get(1, &obj1);
2596 if (!(baseA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2597 error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)");
2598 goto err2;
2600 obj1.free();
2601 if (!arr->get(2, &obj1)->isInt()) {
2602 error(errSyntaxWarning, -1, "Bad Indexed color space (hival)");
2603 delete baseA;
2604 goto err2;
2606 indexHighA = obj1.getInt();
2607 if (indexHighA < 0 || indexHighA > 255) {
2608 // the PDF spec requires indexHigh to be in [0,255] -- allowing
2609 // values larger than 255 creates a security hole: if nComps *
2610 // indexHigh is greater than 2^31, the loop below may overwrite
2611 // past the end of the array
2612 int previousValue = indexHighA;
2613 if (indexHighA < 0) indexHighA = 0;
2614 else indexHighA = 255;
2615 error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA);
2617 obj1.free();
2618 cs = new GfxIndexedColorSpace(baseA, indexHighA);
2619 arr->get(3, &obj1);
2620 n = baseA->getNComps();
2621 if (obj1.isStream()) {
2622 obj1.streamReset();
2623 for (i = 0; i <= indexHighA; ++i) {
2624 const int readChars = obj1.streamGetChars(n, &cs->lookup[i*n]);
2625 for (j = readChars; j < n; ++j) {
2626 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes");
2627 cs->lookup[i*n + j] = 0;
2630 obj1.streamClose();
2631 } else if (obj1.isString()) {
2632 if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
2633 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)");
2634 goto err3;
2636 s = obj1.getString()->getCString();
2637 for (i = 0; i <= indexHighA; ++i) {
2638 for (j = 0; j < n; ++j) {
2639 cs->lookup[i*n + j] = (Guchar)*s++;
2642 } else {
2643 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)");
2644 goto err3;
2646 obj1.free();
2647 return cs;
2649 err3:
2650 delete cs;
2651 err2:
2652 obj1.free();
2653 err1:
2654 return NULL;
2657 GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
2658 GfxColor *baseColor) {
2659 Guchar *p;
2660 double low[gfxColorMaxComps], range[gfxColorMaxComps];
2661 int n, i;
2663 n = base->getNComps();
2664 base->getDefaultRanges(low, range, indexHigh);
2665 const int idx = (int)(colToDbl(color->c[0]) + 0.5) * n;
2666 if (likely((idx + n < (indexHigh + 1) * base->getNComps()) && idx >= 0)) {
2667 p = &lookup[idx];
2668 for (i = 0; i < n; ++i) {
2669 baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
2671 } else {
2672 for (i = 0; i < n; ++i) {
2673 baseColor->c[i] = 0;
2676 return baseColor;
2679 void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2680 GfxColor color2;
2682 base->getGray(mapColorToBase(color, &color2), gray);
2685 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2686 GfxColor color2;
2688 base->getRGB(mapColorToBase(color, &color2), rgb);
2691 void GfxIndexedColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) {
2692 Guchar *line;
2693 int i, j, n;
2695 n = base->getNComps();
2696 line = (Guchar *) gmallocn (length, n);
2697 for (i = 0; i < length; i++)
2698 for (j = 0; j < n; j++)
2699 line[i * n + j] = lookup[in[i] * n + j];
2701 base->getRGBLine(line, out, length);
2703 gfree (line);
2706 void GfxIndexedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
2708 Guchar *line;
2709 int i, j, n;
2711 n = base->getNComps();
2712 line = (Guchar *) gmallocn (length, n);
2713 for (i = 0; i < length; i++)
2714 for (j = 0; j < n; j++)
2715 line[i * n + j] = lookup[in[i] * n + j];
2717 base->getRGBLine(line, out, length);
2719 gfree (line);
2722 void GfxIndexedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
2724 Guchar *line;
2725 int i, j, n;
2727 n = base->getNComps();
2728 line = (Guchar *) gmallocn (length, n);
2729 for (i = 0; i < length; i++)
2730 for (j = 0; j < n; j++)
2731 line[i * n + j] = lookup[in[i] * n + j];
2733 base->getRGBXLine(line, out, length);
2735 gfree (line);
2738 void GfxIndexedColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
2739 Guchar *line;
2740 int i, j, n;
2742 n = base->getNComps();
2743 line = (Guchar *) gmallocn (length, n);
2744 for (i = 0; i < length; i++)
2745 for (j = 0; j < n; j++)
2746 line[i * n + j] = lookup[in[i] * n + j];
2748 base->getCMYKLine(line, out, length);
2750 gfree (line);
2753 void GfxIndexedColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
2754 Guchar *line;
2755 int i, j, n;
2757 n = base->getNComps();
2758 line = (Guchar *) gmallocn (length, n);
2759 for (i = 0; i < length; i++)
2760 for (j = 0; j < n; j++)
2761 line[i * n + j] = lookup[in[i] * n + j];
2763 base->getDeviceNLine(line, out, length);
2765 gfree (line);
2768 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2769 GfxColor color2;
2771 base->getCMYK(mapColorToBase(color, &color2), cmyk);
2774 void GfxIndexedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2775 GfxColor color2;
2777 base->getDeviceN(mapColorToBase(color, &color2), deviceN);
2780 void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
2781 color->c[0] = 0;
2784 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
2785 double *decodeRange,
2786 int maxImgPixel) {
2787 decodeLow[0] = 0;
2788 decodeRange[0] = maxImgPixel;
2791 //------------------------------------------------------------------------
2792 // GfxSeparationColorSpace
2793 //------------------------------------------------------------------------
2795 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2796 GfxColorSpace *altA,
2797 Function *funcA) {
2798 name = nameA;
2799 alt = altA;
2800 func = funcA;
2801 nonMarking = !name->cmp("None");
2802 if (!name->cmp("Cyan")) {
2803 overprintMask = 0x01;
2804 } else if (!name->cmp("Magenta")) {
2805 overprintMask = 0x02;
2806 } else if (!name->cmp("Yellow")) {
2807 overprintMask = 0x04;
2808 } else if (!name->cmp("Black")) {
2809 overprintMask = 0x08;
2810 } else if (!name->cmp("All")) {
2811 overprintMask = 0xffffffff;
2815 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2816 GfxColorSpace *altA,
2817 Function *funcA,
2818 GBool nonMarkingA,
2819 Guint overprintMaskA,
2820 int *mappingA) {
2821 name = nameA;
2822 alt = altA;
2823 func = funcA;
2824 nonMarking = nonMarkingA;
2825 overprintMask = overprintMaskA;
2826 mapping = mappingA;
2829 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
2830 delete name;
2831 delete alt;
2832 delete func;
2833 if (mapping != NULL)
2834 gfree(mapping);
2837 GfxColorSpace *GfxSeparationColorSpace::copy() {
2838 int *mappingA = NULL;
2839 if (mapping != NULL) {
2840 mappingA = (int *) gmalloc(sizeof(int));
2841 *mappingA = *mapping;
2843 return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
2844 nonMarking, overprintMask, mappingA);
2847 //~ handle the 'All' and 'None' colorants
2848 GfxColorSpace *GfxSeparationColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
2849 GfxSeparationColorSpace *cs;
2850 GooString *nameA;
2851 GfxColorSpace *altA;
2852 Function *funcA;
2853 Object obj1;
2855 if (arr->getLength() != 4) {
2856 error(errSyntaxWarning, -1, "Bad Separation color space");
2857 goto err1;
2859 if (!arr->get(1, &obj1)->isName()) {
2860 error(errSyntaxWarning, -1, "Bad Separation color space (name)");
2861 goto err2;
2863 nameA = new GooString(obj1.getName());
2864 obj1.free();
2865 arr->get(2, &obj1);
2866 if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2867 error(errSyntaxWarning, -1, "Bad Separation color space (alternate color space)");
2868 goto err3;
2870 obj1.free();
2871 arr->get(3, &obj1);
2872 if (!(funcA = Function::parse(&obj1))) {
2873 goto err4;
2875 if (funcA->getInputSize() != 1) {
2876 error(errSyntaxWarning, -1, "Bad SeparationColorSpace function");
2877 goto err5;
2879 obj1.free();
2880 cs = new GfxSeparationColorSpace(nameA, altA, funcA);
2881 return cs;
2883 err5:
2884 delete funcA;
2885 err4:
2886 delete altA;
2887 err3:
2888 delete nameA;
2889 err2:
2890 obj1.free();
2891 err1:
2892 return NULL;
2895 void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2896 double x;
2897 double c[gfxColorMaxComps];
2898 GfxColor color2;
2899 int i;
2901 if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) {
2902 *gray = clip01(gfxColorComp1 - color->c[0]);
2903 } else {
2904 x = colToDbl(color->c[0]);
2905 func->transform(&x, c);
2906 for (i = 0; i < alt->getNComps(); ++i) {
2907 color2.c[i] = dblToCol(c[i]);
2909 alt->getGray(&color2, gray);
2913 void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2914 double x;
2915 double c[gfxColorMaxComps];
2916 GfxColor color2;
2917 int i;
2919 if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) {
2920 rgb->r = clip01(gfxColorComp1 - color->c[0]);
2921 rgb->g = clip01(gfxColorComp1 - color->c[0]);
2922 rgb->b = clip01(gfxColorComp1 - color->c[0]);
2923 } else {
2924 x = colToDbl(color->c[0]);
2925 func->transform(&x, c);
2926 for (i = 0; i < alt->getNComps(); ++i) {
2927 color2.c[i] = dblToCol(c[i]);
2929 alt->getRGB(&color2, rgb);
2933 void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2934 double x;
2935 double c[gfxColorMaxComps];
2936 GfxColor color2;
2937 int i;
2939 if (name->cmp("Black") == 0) {
2940 cmyk->c = 0;
2941 cmyk->m = 0;
2942 cmyk->y = 0;
2943 cmyk->k = color->c[0];
2944 } else if (name->cmp("Cyan") == 0) {
2945 cmyk->c = color->c[0];
2946 cmyk->m = 0;
2947 cmyk->y = 0;
2948 cmyk->k = 0;
2949 } else if (name->cmp("Magenta") == 0) {
2950 cmyk->c = 0;
2951 cmyk->m = color->c[0];
2952 cmyk->y = 0;
2953 cmyk->k = 0;
2954 } else if (name->cmp("Yellow") == 0) {
2955 cmyk->c = 0;
2956 cmyk->m = 0;
2957 cmyk->y = color->c[0];
2958 cmyk->k = 0;
2959 } else {
2960 x = colToDbl(color->c[0]);
2961 func->transform(&x, c);
2962 for (i = 0; i < alt->getNComps(); ++i) {
2963 color2.c[i] = dblToCol(c[i]);
2965 alt->getCMYK(&color2, cmyk);
2969 void GfxSeparationColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2970 for (int i = 0; i < gfxColorMaxComps; i++)
2971 deviceN->c[i] = 0;
2972 if (mapping == NULL || mapping[0] == -1) {
2973 GfxCMYK cmyk;
2975 getCMYK(color, &cmyk);
2976 deviceN->c[0] = cmyk.c;
2977 deviceN->c[1] = cmyk.m;
2978 deviceN->c[2] = cmyk.y;
2979 deviceN->c[3] = cmyk.k;
2980 } else {
2981 deviceN->c[mapping[0]] = color->c[0];
2985 void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
2986 color->c[0] = gfxColorComp1;
2989 void GfxSeparationColorSpace::createMapping(GooList *separationList, int maxSepComps) {
2990 if (nonMarking)
2991 return;
2992 mapping = (int *)gmalloc(sizeof(int));
2993 switch (overprintMask) {
2994 case 0x01:
2995 *mapping = 0;
2996 break;
2997 case 0x02:
2998 *mapping = 1;
2999 break;
3000 case 0x04:
3001 *mapping = 2;
3002 break;
3003 case 0x08:
3004 *mapping = 3;
3005 break;
3006 default:
3007 Guint newOverprintMask = 0x10;
3008 for (int i = 0; i < separationList->getLength(); i++) {
3009 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
3010 if (!sepCS->getName()->cmp(name)) {
3011 if (sepCS->getFunc()->hasDifferentResultSet(func)) {
3012 error(errSyntaxWarning, -1,
3013 "Different functions found for '{0:t}', convert immediately", name);
3014 gfree(mapping);
3015 mapping = NULL;
3016 return;
3018 *mapping = i+4;
3019 overprintMask = newOverprintMask;
3020 return;
3022 newOverprintMask <<=1;
3024 if (separationList->getLength() == maxSepComps) {
3025 error(errSyntaxWarning, -1,
3026 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, name);
3027 gfree(mapping);
3028 mapping = NULL;
3029 return;
3031 *mapping = separationList->getLength() + 4;
3032 separationList->append(copy());
3033 overprintMask = newOverprintMask;
3034 break;
3038 //------------------------------------------------------------------------
3039 // GfxDeviceNColorSpace
3040 //------------------------------------------------------------------------
3042 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
3043 GooString **namesA,
3044 GfxColorSpace *altA,
3045 Function *funcA,
3046 GooList *sepsCSA) {
3047 int i;
3049 nComps = nCompsA;
3050 alt = altA;
3051 func = funcA;
3052 sepsCS = sepsCSA;
3053 nonMarking = gTrue;
3054 overprintMask = 0;
3055 mapping = NULL;
3056 for (i = 0; i < nComps; ++i) {
3057 names[i] = namesA[i];
3058 if (names[i]->cmp("None")) {
3059 nonMarking = gFalse;
3061 if (!names[i]->cmp("Cyan")) {
3062 overprintMask |= 0x01;
3063 } else if (!names[i]->cmp("Magenta")) {
3064 overprintMask |= 0x02;
3065 } else if (!names[i]->cmp("Yellow")) {
3066 overprintMask |= 0x04;
3067 } else if (!names[i]->cmp("Black")) {
3068 overprintMask |= 0x08;
3069 } else if (!names[i]->cmp("All")) {
3070 overprintMask = 0xffffffff;
3071 } else {
3072 overprintMask = 0x0f;
3077 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
3078 GooString **namesA,
3079 GfxColorSpace *altA,
3080 Function *funcA,
3081 GooList *sepsCSA,
3082 int *mappingA,
3083 GBool nonMarkingA,
3084 Guint overprintMaskA) {
3085 int i;
3087 nComps = nCompsA;
3088 alt = altA;
3089 func = funcA;
3090 sepsCS = sepsCSA;
3091 mapping = mappingA;
3092 nonMarking = nonMarkingA;
3093 overprintMask = overprintMaskA;
3094 for (i = 0; i < nComps; ++i) {
3095 names[i] = namesA[i]->copy();
3099 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
3100 int i;
3102 for (i = 0; i < nComps; ++i) {
3103 delete names[i];
3105 delete alt;
3106 delete func;
3107 deleteGooList(sepsCS, GfxSeparationColorSpace);
3108 if (mapping != NULL)
3109 gfree(mapping);
3112 GfxColorSpace *GfxDeviceNColorSpace::copy() {
3113 int i;
3114 int *mappingA = NULL;
3116 GooList *sepsCSA = new GooList(sepsCS->getLength());
3117 for (i = 0; i < sepsCS->getLength(); i++) {
3118 GfxSeparationColorSpace *scs = (GfxSeparationColorSpace *) sepsCS->get(i);
3119 if (likely(scs != NULL)) {
3120 sepsCSA->append(scs->copy());
3123 if (mapping != NULL) {
3124 mappingA = (int *)gmalloc(sizeof(int) * nComps);
3125 for (i = 0; i < nComps; i++)
3126 mappingA[i] = mapping[i];
3128 return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
3129 sepsCSA, mappingA, nonMarking, overprintMask);
3132 //~ handle the 'None' colorant
3133 GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
3134 GfxDeviceNColorSpace *cs;
3135 int nCompsA;
3136 GooString *namesA[gfxColorMaxComps];
3137 GfxColorSpace *altA;
3138 Function *funcA;
3139 Object obj1, obj2;
3140 int i;
3141 GooList *separationList = new GooList();
3143 if (arr->getLength() != 4 && arr->getLength() != 5) {
3144 error(errSyntaxWarning, -1, "Bad DeviceN color space");
3145 goto err1;
3147 if (!arr->get(1, &obj1)->isArray()) {
3148 error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
3149 goto err2;
3151 nCompsA = obj1.arrayGetLength();
3152 if (nCompsA > gfxColorMaxComps) {
3153 error(errSyntaxWarning, -1, "DeviceN color space with too many ({0:d} > {1:d}) components",
3154 nCompsA, gfxColorMaxComps);
3155 nCompsA = gfxColorMaxComps;
3157 for (i = 0; i < nCompsA; ++i) {
3158 if (!obj1.arrayGet(i, &obj2)->isName()) {
3159 error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
3160 obj2.free();
3161 goto err2;
3163 namesA[i] = new GooString(obj2.getName());
3164 obj2.free();
3166 obj1.free();
3167 arr->get(2, &obj1);
3168 if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
3169 error(errSyntaxWarning, -1, "Bad DeviceN color space (alternate color space)");
3170 goto err3;
3172 obj1.free();
3173 arr->get(3, &obj1);
3174 if (!(funcA = Function::parse(&obj1))) {
3175 goto err4;
3177 obj1.free();
3178 if (arr->getLength() == 5) {
3179 if (!arr->get(4, &obj1)->isDict()) {
3180 error(errSyntaxWarning, -1, "Bad DeviceN color space (attributes)");
3181 goto err4;
3183 Dict *attribs = obj1.getDict();
3184 attribs->lookup("Colorants", &obj2);
3185 if (obj2.isDict()) {
3186 Dict *colorants = obj2.getDict();
3187 for (i = 0; i < colorants->getLength(); i++) {
3188 Object obj3;
3189 colorants->getVal(i, &obj3);
3190 if (obj3.isArray()) {
3191 separationList->append(GfxSeparationColorSpace::parse(res, obj3.getArray(), out, state, recursion));
3192 } else {
3193 obj3.free();
3194 obj2.free();
3195 error(errSyntaxWarning, -1, "Bad DeviceN color space (colorant value entry is not an Array)");
3196 goto err4;
3198 obj3.free();
3201 obj2.free();
3202 obj1.free();
3204 cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA, separationList);
3205 return cs;
3207 err4:
3208 delete altA;
3209 err3:
3210 for (i = 0; i < nCompsA; ++i) {
3211 delete namesA[i];
3213 err2:
3214 obj1.free();
3215 err1:
3216 delete separationList;
3217 return NULL;
3220 void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
3221 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3222 GfxColor color2;
3223 int i;
3225 for (i = 0; i < nComps; ++i) {
3226 x[i] = colToDbl(color->c[i]);
3228 func->transform(x, c);
3229 for (i = 0; i < alt->getNComps(); ++i) {
3230 color2.c[i] = dblToCol(c[i]);
3232 alt->getGray(&color2, gray);
3235 void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
3236 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3237 GfxColor color2;
3238 int i;
3240 for (i = 0; i < nComps; ++i) {
3241 x[i] = colToDbl(color->c[i]);
3243 func->transform(x, c);
3244 for (i = 0; i < alt->getNComps(); ++i) {
3245 color2.c[i] = dblToCol(c[i]);
3247 alt->getRGB(&color2, rgb);
3250 void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
3251 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3252 GfxColor color2;
3253 int i;
3255 for (i = 0; i < nComps; ++i) {
3256 x[i] = colToDbl(color->c[i]);
3258 func->transform(x, c);
3259 for (i = 0; i < alt->getNComps(); ++i) {
3260 color2.c[i] = dblToCol(c[i]);
3262 alt->getCMYK(&color2, cmyk);
3265 void GfxDeviceNColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
3266 for (int i = 0; i < gfxColorMaxComps; i++)
3267 deviceN->c[i] = 0;
3268 if (mapping == NULL) {
3269 GfxCMYK cmyk;
3271 getCMYK(color, &cmyk);
3272 deviceN->c[0] = cmyk.c;
3273 deviceN->c[1] = cmyk.m;
3274 deviceN->c[2] = cmyk.y;
3275 deviceN->c[3] = cmyk.k;
3276 } else {
3277 for (int j = 0; j < nComps; j++)
3278 if (mapping[j] != -1)
3279 deviceN->c[mapping[j]] = color->c[j];
3283 void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
3284 int i;
3286 for (i = 0; i < nComps; ++i) {
3287 color->c[i] = gfxColorComp1;
3291 void GfxDeviceNColorSpace::createMapping(GooList *separationList, int maxSepComps) {
3292 if (nonMarking) // None
3293 return;
3294 mapping = (int *)gmalloc(sizeof(int) * nComps);
3295 Guint newOverprintMask = 0;
3296 for (int i = 0; i < nComps; i++) {
3297 if (!names[i]->cmp("None")) {
3298 mapping[i] = -1;
3299 } else if (!names[i]->cmp("Cyan")) {
3300 newOverprintMask |= 0x01;
3301 mapping[i] = 0;
3302 } else if (!names[i]->cmp("Magenta")) {
3303 newOverprintMask |= 0x02;
3304 mapping[i] = 1;
3305 } else if (!names[i]->cmp("Yellow")) {
3306 newOverprintMask |= 0x04;
3307 mapping[i] = 2;
3308 } else if (!names[i]->cmp("Black")) {
3309 newOverprintMask |= 0x08;
3310 mapping[i] = 3;
3311 } else {
3312 Guint startOverprintMask = 0x10;
3313 GBool found = gFalse;
3314 Function *sepFunc = NULL;
3315 if (nComps == 1)
3316 sepFunc = func;
3317 else {
3318 for (int k = 0; k < sepsCS->getLength(); k++) {
3319 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
3320 if (!sepCS->getName()->cmp(names[i])) {
3321 sepFunc = sepCS->getFunc();
3322 break;
3326 for (int j = 0; j < separationList->getLength(); j++) {
3327 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(j);
3328 if (!sepCS->getName()->cmp(names[i])) {
3329 if (sepFunc != NULL && sepCS->getFunc()->hasDifferentResultSet(sepFunc)) {
3330 error(errSyntaxWarning, -1,
3331 "Different functions found for '{0:t}', convert immediately", names[i]);
3332 gfree(mapping);
3333 mapping = NULL;
3334 overprintMask = 0xffffffff;
3335 return;
3337 mapping[i] = j+4;
3338 newOverprintMask |= startOverprintMask;
3339 found = gTrue;
3340 break;
3342 startOverprintMask <<=1;
3344 if (!found) {
3345 if (separationList->getLength() == maxSepComps) {
3346 error(errSyntaxWarning, -1,
3347 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, names[i]);
3348 gfree(mapping);
3349 mapping = NULL;
3350 overprintMask = 0xffffffff;
3351 return;
3353 mapping[i] = separationList->getLength() + 4;
3354 newOverprintMask |= startOverprintMask;
3355 if (nComps == 1)
3356 separationList->append(new GfxSeparationColorSpace(names[i]->copy(),alt->copy(), func->copy()));
3357 else {
3358 for (int k = 0; k < sepsCS->getLength(); k++) {
3359 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
3360 if (!sepCS->getName()->cmp(names[i])) {
3361 found = gTrue;
3362 separationList->append(sepCS->copy());
3363 break;
3366 if(!found) {
3367 error(errSyntaxWarning, -1, "DeviceN has no suitable colorant");
3368 gfree(mapping);
3369 mapping = NULL;
3370 overprintMask = 0xffffffff;
3371 return;
3377 overprintMask = newOverprintMask;
3380 //------------------------------------------------------------------------
3381 // GfxPatternColorSpace
3382 //------------------------------------------------------------------------
3384 GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
3385 under = underA;
3388 GfxPatternColorSpace::~GfxPatternColorSpace() {
3389 if (under) {
3390 delete under;
3394 GfxColorSpace *GfxPatternColorSpace::copy() {
3395 return new GfxPatternColorSpace(under ? under->copy() :
3396 (GfxColorSpace *)NULL);
3399 GfxColorSpace *GfxPatternColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
3400 GfxPatternColorSpace *cs;
3401 GfxColorSpace *underA;
3402 Object obj1;
3404 if (arr->getLength() != 1 && arr->getLength() != 2) {
3405 error(errSyntaxWarning, -1, "Bad Pattern color space");
3406 return NULL;
3408 underA = NULL;
3409 if (arr->getLength() == 2) {
3410 arr->get(1, &obj1);
3411 if (!(underA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
3412 error(errSyntaxWarning, -1, "Bad Pattern color space (underlying color space)");
3413 obj1.free();
3414 return NULL;
3416 obj1.free();
3418 cs = new GfxPatternColorSpace(underA);
3419 return cs;
3422 void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
3423 *gray = 0;
3426 void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
3427 rgb->r = rgb->g = rgb->b = 0;
3430 void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
3431 cmyk->c = cmyk->m = cmyk->y = 0;
3432 cmyk->k = 1;
3435 void GfxPatternColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
3436 for (int i = 0; i < gfxColorMaxComps; i++)
3437 deviceN->c[i] = 0;
3438 deviceN->c[3] = 1;
3441 void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
3442 color->c[0]=0;
3445 //------------------------------------------------------------------------
3446 // Pattern
3447 //------------------------------------------------------------------------
3449 GfxPattern::GfxPattern(int typeA) {
3450 type = typeA;
3453 GfxPattern::~GfxPattern() {
3456 GfxPattern *GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) {
3457 GfxPattern *pattern;
3458 Object obj1;
3460 if (obj->isDict()) {
3461 obj->dictLookup("PatternType", &obj1);
3462 } else if (obj->isStream()) {
3463 obj->streamGetDict()->lookup("PatternType", &obj1);
3464 } else {
3465 return NULL;
3467 pattern = NULL;
3468 if (obj1.isInt() && obj1.getInt() == 1) {
3469 pattern = GfxTilingPattern::parse(obj);
3470 } else if (obj1.isInt() && obj1.getInt() == 2) {
3471 pattern = GfxShadingPattern::parse(res, obj, out, state);
3473 obj1.free();
3474 return pattern;
3477 //------------------------------------------------------------------------
3478 // GfxTilingPattern
3479 //------------------------------------------------------------------------
3481 GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
3482 GfxTilingPattern *pat;
3483 Dict *dict;
3484 int paintTypeA, tilingTypeA;
3485 double bboxA[4], matrixA[6];
3486 double xStepA, yStepA;
3487 Object resDictA;
3488 Object obj1, obj2;
3489 int i;
3491 if (!patObj->isStream()) {
3492 return NULL;
3494 dict = patObj->streamGetDict();
3496 if (dict->lookup("PaintType", &obj1)->isInt()) {
3497 paintTypeA = obj1.getInt();
3498 } else {
3499 paintTypeA = 1;
3500 error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
3502 obj1.free();
3503 if (dict->lookup("TilingType", &obj1)->isInt()) {
3504 tilingTypeA = obj1.getInt();
3505 } else {
3506 tilingTypeA = 1;
3507 error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
3509 obj1.free();
3510 bboxA[0] = bboxA[1] = 0;
3511 bboxA[2] = bboxA[3] = 1;
3512 if (dict->lookup("BBox", &obj1)->isArray() &&
3513 obj1.arrayGetLength() == 4) {
3514 for (i = 0; i < 4; ++i) {
3515 if (obj1.arrayGet(i, &obj2)->isNum()) {
3516 bboxA[i] = obj2.getNum();
3518 obj2.free();
3520 } else {
3521 error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern");
3523 obj1.free();
3524 if (dict->lookup("XStep", &obj1)->isNum()) {
3525 xStepA = obj1.getNum();
3526 } else {
3527 xStepA = 1;
3528 error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern");
3530 obj1.free();
3531 if (dict->lookup("YStep", &obj1)->isNum()) {
3532 yStepA = obj1.getNum();
3533 } else {
3534 yStepA = 1;
3535 error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern");
3537 obj1.free();
3538 if (!dict->lookup("Resources", &resDictA)->isDict()) {
3539 resDictA.free();
3540 resDictA.initNull();
3541 error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern");
3543 matrixA[0] = 1; matrixA[1] = 0;
3544 matrixA[2] = 0; matrixA[3] = 1;
3545 matrixA[4] = 0; matrixA[5] = 0;
3546 if (dict->lookup("Matrix", &obj1)->isArray() &&
3547 obj1.arrayGetLength() == 6) {
3548 for (i = 0; i < 6; ++i) {
3549 if (obj1.arrayGet(i, &obj2)->isNum()) {
3550 matrixA[i] = obj2.getNum();
3552 obj2.free();
3555 obj1.free();
3557 pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
3558 &resDictA, matrixA, patObj);
3559 resDictA.free();
3560 return pat;
3563 GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
3564 double *bboxA, double xStepA, double yStepA,
3565 Object *resDictA, double *matrixA,
3566 Object *contentStreamA):
3567 GfxPattern(1)
3569 int i;
3571 paintType = paintTypeA;
3572 tilingType = tilingTypeA;
3573 for (i = 0; i < 4; ++i) {
3574 bbox[i] = bboxA[i];
3576 xStep = xStepA;
3577 yStep = yStepA;
3578 resDictA->copy(&resDict);
3579 for (i = 0; i < 6; ++i) {
3580 matrix[i] = matrixA[i];
3582 contentStreamA->copy(&contentStream);
3585 GfxTilingPattern::~GfxTilingPattern() {
3586 resDict.free();
3587 contentStream.free();
3590 GfxPattern *GfxTilingPattern::copy() {
3591 return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
3592 &resDict, matrix, &contentStream);
3595 //------------------------------------------------------------------------
3596 // GfxShadingPattern
3597 //------------------------------------------------------------------------
3599 GfxShadingPattern *GfxShadingPattern::parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state) {
3600 Dict *dict;
3601 GfxShading *shadingA;
3602 double matrixA[6];
3603 Object obj1, obj2;
3604 int i;
3606 if (!patObj->isDict()) {
3607 return NULL;
3609 dict = patObj->getDict();
3611 dict->lookup("Shading", &obj1);
3612 shadingA = GfxShading::parse(res, &obj1, out, state);
3613 obj1.free();
3614 if (!shadingA) {
3615 return NULL;
3618 matrixA[0] = 1; matrixA[1] = 0;
3619 matrixA[2] = 0; matrixA[3] = 1;
3620 matrixA[4] = 0; matrixA[5] = 0;
3621 if (dict->lookup("Matrix", &obj1)->isArray() &&
3622 obj1.arrayGetLength() == 6) {
3623 for (i = 0; i < 6; ++i) {
3624 if (obj1.arrayGet(i, &obj2)->isNum()) {
3625 matrixA[i] = obj2.getNum();
3627 obj2.free();
3630 obj1.free();
3632 return new GfxShadingPattern(shadingA, matrixA);
3635 GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
3636 GfxPattern(2)
3638 int i;
3640 shading = shadingA;
3641 for (i = 0; i < 6; ++i) {
3642 matrix[i] = matrixA[i];
3646 GfxShadingPattern::~GfxShadingPattern() {
3647 delete shading;
3650 GfxPattern *GfxShadingPattern::copy() {
3651 return new GfxShadingPattern(shading->copy(), matrix);
3654 //------------------------------------------------------------------------
3655 // GfxShading
3656 //------------------------------------------------------------------------
3658 GfxShading::GfxShading(int typeA) {
3659 type = typeA;
3660 colorSpace = NULL;
3663 GfxShading::GfxShading(GfxShading *shading) {
3664 int i;
3666 type = shading->type;
3667 colorSpace = shading->colorSpace->copy();
3668 for (i = 0; i < gfxColorMaxComps; ++i) {
3669 background.c[i] = shading->background.c[i];
3671 hasBackground = shading->hasBackground;
3672 xMin = shading->xMin;
3673 yMin = shading->yMin;
3674 xMax = shading->xMax;
3675 yMax = shading->yMax;
3676 hasBBox = shading->hasBBox;
3679 GfxShading::~GfxShading() {
3680 if (colorSpace) {
3681 delete colorSpace;
3685 GfxShading *GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) {
3686 GfxShading *shading;
3687 Dict *dict;
3688 int typeA;
3689 Object obj1;
3691 if (obj->isDict()) {
3692 dict = obj->getDict();
3693 } else if (obj->isStream()) {
3694 dict = obj->streamGetDict();
3695 } else {
3696 return NULL;
3699 if (!dict->lookup("ShadingType", &obj1)->isInt()) {
3700 error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary");
3701 obj1.free();
3702 return NULL;
3704 typeA = obj1.getInt();
3705 obj1.free();
3707 switch (typeA) {
3708 case 1:
3709 shading = GfxFunctionShading::parse(res, dict, out, state);
3710 break;
3711 case 2:
3712 shading = GfxAxialShading::parse(res, dict, out, state);
3713 break;
3714 case 3:
3715 shading = GfxRadialShading::parse(res, dict, out, state);
3716 break;
3717 case 4:
3718 if (obj->isStream()) {
3719 shading = GfxGouraudTriangleShading::parse(res, 4, dict, obj->getStream(), out, state);
3720 } else {
3721 error(errSyntaxWarning, -1, "Invalid Type 4 shading object");
3722 goto err1;
3724 break;
3725 case 5:
3726 if (obj->isStream()) {
3727 shading = GfxGouraudTriangleShading::parse(res, 5, dict, obj->getStream(), out, state);
3728 } else {
3729 error(errSyntaxWarning, -1, "Invalid Type 5 shading object");
3730 goto err1;
3732 break;
3733 case 6:
3734 if (obj->isStream()) {
3735 shading = GfxPatchMeshShading::parse(res, 6, dict, obj->getStream(), out, state);
3736 } else {
3737 error(errSyntaxWarning, -1, "Invalid Type 6 shading object");
3738 goto err1;
3740 break;
3741 case 7:
3742 if (obj->isStream()) {
3743 shading = GfxPatchMeshShading::parse(res, 7, dict, obj->getStream(), out, state);
3744 } else {
3745 error(errSyntaxWarning, -1, "Invalid Type 7 shading object");
3746 goto err1;
3748 break;
3749 default:
3750 error(errSyntaxWarning, -1, "Unimplemented shading type {0:d}", typeA);
3751 goto err1;
3754 return shading;
3756 err1:
3757 return NULL;
3760 GBool GfxShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
3761 Object obj1, obj2;
3762 int i;
3764 dict->lookup("ColorSpace", &obj1);
3765 if (!(colorSpace = GfxColorSpace::parse(res, &obj1, out, state))) {
3766 error(errSyntaxWarning, -1, "Bad color space in shading dictionary");
3767 obj1.free();
3768 return gFalse;
3770 obj1.free();
3772 for (i = 0; i < gfxColorMaxComps; ++i) {
3773 background.c[i] = 0;
3775 hasBackground = gFalse;
3776 if (dict->lookup("Background", &obj1)->isArray()) {
3777 if (obj1.arrayGetLength() == colorSpace->getNComps()) {
3778 hasBackground = gTrue;
3779 for (i = 0; i < colorSpace->getNComps(); ++i) {
3780 background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
3781 obj2.free();
3783 } else {
3784 error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
3787 obj1.free();
3789 xMin = yMin = xMax = yMax = 0;
3790 hasBBox = gFalse;
3791 if (dict->lookup("BBox", &obj1)->isArray()) {
3792 if (obj1.arrayGetLength() == 4) {
3793 Object obj3, obj4, obj5;
3794 obj1.arrayGet(0, &obj2);
3795 obj1.arrayGet(1, &obj3);
3796 obj1.arrayGet(2, &obj4);
3797 obj1.arrayGet(3, &obj5);
3798 if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum())
3800 hasBBox = gTrue;
3801 xMin = obj2.getNum();
3802 yMin = obj3.getNum();
3803 xMax = obj4.getNum();
3804 yMax = obj5.getNum();
3805 } else {
3806 error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)");
3808 obj2.free();
3809 obj3.free();
3810 obj4.free();
3811 obj5.free();
3812 } else {
3813 error(errSyntaxWarning, -1, "Bad BBox in shading dictionary");
3816 obj1.free();
3818 return gTrue;
3821 //------------------------------------------------------------------------
3822 // GfxFunctionShading
3823 //------------------------------------------------------------------------
3825 GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
3826 double x1A, double y1A,
3827 double *matrixA,
3828 Function **funcsA, int nFuncsA):
3829 GfxShading(1)
3831 int i;
3833 x0 = x0A;
3834 y0 = y0A;
3835 x1 = x1A;
3836 y1 = y1A;
3837 for (i = 0; i < 6; ++i) {
3838 matrix[i] = matrixA[i];
3840 nFuncs = nFuncsA;
3841 for (i = 0; i < nFuncs; ++i) {
3842 funcs[i] = funcsA[i];
3846 GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
3847 GfxShading(shading)
3849 int i;
3851 x0 = shading->x0;
3852 y0 = shading->y0;
3853 x1 = shading->x1;
3854 y1 = shading->y1;
3855 for (i = 0; i < 6; ++i) {
3856 matrix[i] = shading->matrix[i];
3858 nFuncs = shading->nFuncs;
3859 for (i = 0; i < nFuncs; ++i) {
3860 funcs[i] = shading->funcs[i]->copy();
3864 GfxFunctionShading::~GfxFunctionShading() {
3865 int i;
3867 for (i = 0; i < nFuncs; ++i) {
3868 delete funcs[i];
3872 GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
3873 GfxFunctionShading *shading;
3874 double x0A, y0A, x1A, y1A;
3875 double matrixA[6];
3876 Function *funcsA[gfxColorMaxComps];
3877 int nFuncsA;
3878 Object obj1, obj2;
3879 int i;
3881 x0A = y0A = 0;
3882 x1A = y1A = 1;
3883 if (dict->lookup("Domain", &obj1)->isArray() &&
3884 obj1.arrayGetLength() == 4) {
3885 x0A = obj1.arrayGet(0, &obj2)->getNum();
3886 obj2.free();
3887 x1A = obj1.arrayGet(1, &obj2)->getNum();
3888 obj2.free();
3889 y0A = obj1.arrayGet(2, &obj2)->getNum();
3890 obj2.free();
3891 y1A = obj1.arrayGet(3, &obj2)->getNum();
3892 obj2.free();
3894 obj1.free();
3896 matrixA[0] = 1; matrixA[1] = 0;
3897 matrixA[2] = 0; matrixA[3] = 1;
3898 matrixA[4] = 0; matrixA[5] = 0;
3899 if (dict->lookup("Matrix", &obj1)->isArray() &&
3900 obj1.arrayGetLength() == 6) {
3901 matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
3902 obj2.free();
3903 matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
3904 obj2.free();
3905 matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
3906 obj2.free();
3907 matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
3908 obj2.free();
3909 matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
3910 obj2.free();
3911 matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
3912 obj2.free();
3914 obj1.free();
3916 dict->lookup("Function", &obj1);
3917 if (obj1.isArray()) {
3918 nFuncsA = obj1.arrayGetLength();
3919 if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) {
3920 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3921 goto err1;
3923 for (i = 0; i < nFuncsA; ++i) {
3924 obj1.arrayGet(i, &obj2);
3925 if (!(funcsA[i] = Function::parse(&obj2))) {
3926 goto err2;
3928 obj2.free();
3930 } else {
3931 nFuncsA = 1;
3932 if (!(funcsA[0] = Function::parse(&obj1))) {
3933 goto err1;
3936 obj1.free();
3938 shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
3939 funcsA, nFuncsA);
3940 if (!shading->init(res, dict, out, state)) {
3941 delete shading;
3942 return NULL;
3944 return shading;
3946 err2:
3947 obj2.free();
3948 err1:
3949 obj1.free();
3950 return NULL;
3953 GfxShading *GfxFunctionShading::copy() {
3954 return new GfxFunctionShading(this);
3957 void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
3958 double in[2], out[gfxColorMaxComps];
3959 int i;
3961 // NB: there can be one function with n outputs or n functions with
3962 // one output each (where n = number of color components)
3963 for (i = 0; i < gfxColorMaxComps; ++i) {
3964 out[i] = 0;
3966 in[0] = x;
3967 in[1] = y;
3968 for (i = 0; i < nFuncs; ++i) {
3969 funcs[i]->transform(in, &out[i]);
3971 for (i = 0; i < gfxColorMaxComps; ++i) {
3972 color->c[i] = dblToCol(out[i]);
3976 //------------------------------------------------------------------------
3977 // GfxUnivariateShading
3978 //------------------------------------------------------------------------
3980 GfxUnivariateShading::GfxUnivariateShading(int typeA,
3981 double t0A, double t1A,
3982 Function **funcsA, int nFuncsA,
3983 GBool extend0A, GBool extend1A):
3984 GfxShading(typeA)
3986 int i;
3988 t0 = t0A;
3989 t1 = t1A;
3990 nFuncs = nFuncsA;
3991 for (i = 0; i < nFuncs; ++i) {
3992 funcs[i] = funcsA[i];
3994 extend0 = extend0A;
3995 extend1 = extend1A;
3997 cacheSize = 0;
3998 lastMatch = 0;
3999 cacheBounds = NULL;
4000 cacheCoeff = NULL;
4001 cacheValues = NULL;
4004 GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading *shading):
4005 GfxShading(shading)
4007 int i;
4009 t0 = shading->t0;
4010 t1 = shading->t1;
4011 nFuncs = shading->nFuncs;
4012 for (i = 0; i < nFuncs; ++i) {
4013 funcs[i] = shading->funcs[i]->copy();
4015 extend0 = shading->extend0;
4016 extend1 = shading->extend1;
4018 cacheSize = 0;
4019 lastMatch = 0;
4020 cacheBounds = NULL;
4021 cacheCoeff = NULL;
4022 cacheValues = NULL;
4025 GfxUnivariateShading::~GfxUnivariateShading() {
4026 int i;
4028 for (i = 0; i < nFuncs; ++i) {
4029 delete funcs[i];
4032 gfree (cacheBounds);
4035 void GfxUnivariateShading::getColor(double t, GfxColor *color) {
4036 double out[gfxColorMaxComps];
4037 int i, nComps;
4039 // NB: there can be one function with n outputs or n functions with
4040 // one output each (where n = number of color components)
4041 nComps = nFuncs * funcs[0]->getOutputSize();
4043 if (cacheSize > 0) {
4044 double x, ix, *l, *u, *upper;
4046 if (cacheBounds[lastMatch - 1] >= t) {
4047 upper = std::lower_bound (cacheBounds, cacheBounds + lastMatch - 1, t);
4048 lastMatch = upper - cacheBounds;
4049 lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
4050 } else if (cacheBounds[lastMatch] < t) {
4051 upper = std::lower_bound (cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t);
4052 lastMatch = upper - cacheBounds;
4053 lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
4056 x = (t - cacheBounds[lastMatch-1]) * cacheCoeff[lastMatch];
4057 ix = 1.0 - x;
4058 u = cacheValues + lastMatch * nComps;
4059 l = u - nComps;
4061 for (i = 0; i < nComps; ++i) {
4062 out[i] = ix * l[i] + x * u[i];
4064 } else {
4065 for (i = 0; i < nComps; ++i) {
4066 out[i] = 0;
4068 for (i = 0; i < nFuncs; ++i) {
4069 if (funcs[i]->getInputSize() != 1) {
4070 error(errSyntaxWarning, -1, "Invalid shading function (input != 1)");
4071 break;
4073 funcs[i]->transform(&t, &out[i]);
4077 for (i = 0; i < nComps; ++i) {
4078 color->c[i] = dblToCol(out[i]);
4082 void GfxUnivariateShading::setupCache(const Matrix *ctm,
4083 double xMin, double yMin,
4084 double xMax, double yMax) {
4085 double sMin, sMax, tMin, tMax, upperBound;
4086 int i, j, nComps, maxSize;
4088 gfree (cacheBounds);
4089 cacheBounds = NULL;
4090 cacheSize = 0;
4092 // NB: there can be one function with n outputs or n functions with
4093 // one output each (where n = number of color components)
4094 nComps = nFuncs * funcs[0]->getOutputSize();
4096 getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
4097 upperBound = ctm->norm() * getDistance(sMin, sMax);
4098 maxSize = ceil(upperBound);
4099 maxSize = std::max<int>(maxSize, 2);
4102 double x[4], y[4];
4104 ctm->transform(xMin, yMin, &x[0], &y[0]);
4105 ctm->transform(xMax, yMin, &x[1], &y[1]);
4106 ctm->transform(xMin, yMax, &x[2], &y[2]);
4107 ctm->transform(xMax, yMax, &x[3], &y[3]);
4109 xMin = xMax = x[0];
4110 yMin = yMax = y[0];
4111 for (i = 1; i < 4; i++) {
4112 xMin = std::min<double>(xMin, x[i]);
4113 yMin = std::min<double>(yMin, y[i]);
4114 xMax = std::max<double>(xMax, x[i]);
4115 yMax = std::max<double>(yMax, y[i]);
4119 if (maxSize > (xMax-xMin) * (yMax-yMin)) {
4120 return;
4123 if (t0 < t1) {
4124 tMin = t0 + sMin * (t1 - t0);
4125 tMax = t0 + sMax * (t1 - t0);
4126 } else {
4127 tMin = t0 + sMax * (t1 - t0);
4128 tMax = t0 + sMin * (t1 - t0);
4131 cacheBounds = (double *)gmallocn(maxSize, sizeof(double) * (nComps + 2));
4132 cacheCoeff = cacheBounds + maxSize;
4133 cacheValues = cacheCoeff + maxSize;
4135 if (cacheSize != 0) {
4136 for (j = 0; j < cacheSize; ++j) {
4137 cacheCoeff[j] = 1 / (cacheBounds[j+1] - cacheBounds[j]);
4139 } else if (tMax != tMin) {
4140 double step = (tMax - tMin) / (maxSize - 1);
4141 double coeff = (maxSize - 1) / (tMax - tMin);
4143 cacheSize = maxSize;
4145 for (j = 0; j < cacheSize; ++j) {
4146 cacheBounds[j] = tMin + j * step;
4147 cacheCoeff[j] = coeff;
4149 for (i = 0; i < nComps; ++i) {
4150 cacheValues[j*nComps + i] = 0;
4152 for (i = 0; i < nFuncs; ++i) {
4153 funcs[i]->transform(&cacheBounds[j], &cacheValues[j*nComps + i]);
4158 lastMatch = 1;
4162 //------------------------------------------------------------------------
4163 // GfxAxialShading
4164 //------------------------------------------------------------------------
4166 GfxAxialShading::GfxAxialShading(double x0A, double y0A,
4167 double x1A, double y1A,
4168 double t0A, double t1A,
4169 Function **funcsA, int nFuncsA,
4170 GBool extend0A, GBool extend1A):
4171 GfxUnivariateShading(2, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
4173 x0 = x0A;
4174 y0 = y0A;
4175 x1 = x1A;
4176 y1 = y1A;
4179 GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
4180 GfxUnivariateShading(shading)
4182 x0 = shading->x0;
4183 y0 = shading->y0;
4184 x1 = shading->x1;
4185 y1 = shading->y1;
4188 GfxAxialShading::~GfxAxialShading() {
4191 GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
4192 GfxAxialShading *shading;
4193 double x0A, y0A, x1A, y1A;
4194 double t0A, t1A;
4195 Function *funcsA[gfxColorMaxComps];
4196 int nFuncsA;
4197 GBool extend0A, extend1A;
4198 Object obj1, obj2;
4199 int i;
4201 x0A = y0A = x1A = y1A = 0;
4202 if (dict->lookup("Coords", &obj1)->isArray() &&
4203 obj1.arrayGetLength() == 4) {
4204 Object obj3, obj4, obj5;
4205 obj1.arrayGet(0, &obj2);
4206 obj1.arrayGet(1, &obj3);
4207 obj1.arrayGet(2, &obj4);
4208 obj1.arrayGet(3, &obj5);
4209 if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum()) {
4210 x0A = obj2.getNum();
4211 y0A = obj3.getNum();
4212 x1A = obj4.getNum();
4213 y1A = obj5.getNum();
4215 obj2.free();
4216 obj3.free();
4217 obj4.free();
4218 obj5.free();
4219 } else {
4220 error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
4221 goto err1;
4223 obj1.free();
4225 t0A = 0;
4226 t1A = 1;
4227 if (dict->lookup("Domain", &obj1)->isArray() &&
4228 obj1.arrayGetLength() == 2) {
4229 Object obj3;
4230 obj1.arrayGet(0, &obj2);
4231 obj1.arrayGet(1, &obj3);
4232 if (obj2.isNum() && obj3.isNum()) {
4233 t0A = obj2.getNum();
4234 t1A = obj3.getNum();
4236 obj2.free();
4237 obj3.free();
4239 obj1.free();
4241 dict->lookup("Function", &obj1);
4242 if (obj1.isArray()) {
4243 nFuncsA = obj1.arrayGetLength();
4244 if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) {
4245 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4246 goto err1;
4248 for (i = 0; i < nFuncsA; ++i) {
4249 obj1.arrayGet(i, &obj2);
4250 if (!(funcsA[i] = Function::parse(&obj2))) {
4251 obj1.free();
4252 obj2.free();
4253 goto err1;
4255 obj2.free();
4257 } else {
4258 nFuncsA = 1;
4259 if (!(funcsA[0] = Function::parse(&obj1))) {
4260 obj1.free();
4261 goto err1;
4264 obj1.free();
4266 extend0A = extend1A = gFalse;
4267 if (dict->lookup("Extend", &obj1)->isArray() &&
4268 obj1.arrayGetLength() == 2) {
4269 obj1.arrayGet(0, &obj2);
4270 if (obj2.isBool()) {
4271 extend0A = obj2.getBool();
4272 } else {
4273 error(errSyntaxWarning, -1, "Invalid axial shading extend (0)");
4275 obj2.free();
4276 obj1.arrayGet(1, &obj2);
4277 if (obj2.isBool()) {
4278 extend1A = obj2.getBool();
4279 } else {
4280 error(errSyntaxWarning, -1, "Invalid axial shading extend (1)");
4282 obj2.free();
4284 obj1.free();
4286 shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
4287 funcsA, nFuncsA, extend0A, extend1A);
4288 if (!shading->init(res, dict, out, state)) {
4289 delete shading;
4290 return NULL;
4292 return shading;
4294 err1:
4295 return NULL;
4298 GfxShading *GfxAxialShading::copy() {
4299 return new GfxAxialShading(this);
4302 double GfxAxialShading::getDistance(double sMin, double sMax) {
4303 double xMin, yMin, xMax, yMax;
4305 xMin = x0 + sMin * (x1 - x0);
4306 yMin = y0 + sMin * (y1 - y0);
4307 xMax = x0 + sMax * (x1 - x0);
4308 yMax = y0 + sMax * (y1 - y0);
4310 return hypot(xMax-xMin, yMax-yMin);
4313 void GfxAxialShading::getParameterRange(double *lower, double *upper,
4314 double xMin, double yMin,
4315 double xMax, double yMax) {
4316 double pdx, pdy, invsqnorm, tdx, tdy, t, range[2];
4318 // Linear gradients are orthogonal to the line passing through their
4319 // extremes. Because of convexity, the parameter range can be
4320 // computed as the convex hull (one the real line) of the parameter
4321 // values of the 4 corners of the box.
4323 // The parameter value t for a point (x,y) can be computed as:
4325 // t = (p2 - p1) . (x,y) / |p2 - p1|^2
4327 // t0 is the t value for the top left corner
4328 // tdx is the difference between left and right corners
4329 // tdy is the difference between top and bottom corners
4331 pdx = x1 - x0;
4332 pdy = y1 - y0;
4333 invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
4334 pdx *= invsqnorm;
4335 pdy *= invsqnorm;
4337 t = (xMin - x0) * pdx + (yMin - y0) * pdy;
4338 tdx = (xMax - xMin) * pdx;
4339 tdy = (yMax - yMin) * pdy;
4341 // Because of the linearity of the t value, tdx can simply be added
4342 // the t0 to move along the top edge. After this, *lower and *upper
4343 // represent the parameter range for the top edge, so extending it
4344 // to include the whole box simply requires adding tdy to the
4345 // correct extreme.
4347 range[0] = range[1] = t;
4348 if (tdx < 0)
4349 range[0] += tdx;
4350 else
4351 range[1] += tdx;
4353 if (tdy < 0)
4354 range[0] += tdy;
4355 else
4356 range[1] += tdy;
4358 *lower = std::max<double>(0., std::min<double>(1., range[0]));
4359 *upper = std::max<double>(0., std::min<double>(1., range[1]));
4362 //------------------------------------------------------------------------
4363 // GfxRadialShading
4364 //------------------------------------------------------------------------
4366 #ifndef RADIAL_EPSILON
4367 #define RADIAL_EPSILON (1. / 1024 / 1024)
4368 #endif
4370 GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
4371 double x1A, double y1A, double r1A,
4372 double t0A, double t1A,
4373 Function **funcsA, int nFuncsA,
4374 GBool extend0A, GBool extend1A):
4375 GfxUnivariateShading(3, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
4377 x0 = x0A;
4378 y0 = y0A;
4379 r0 = r0A;
4380 x1 = x1A;
4381 y1 = y1A;
4382 r1 = r1A;
4385 GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
4386 GfxUnivariateShading(shading)
4388 x0 = shading->x0;
4389 y0 = shading->y0;
4390 r0 = shading->r0;
4391 x1 = shading->x1;
4392 y1 = shading->y1;
4393 r1 = shading->r1;
4396 GfxRadialShading::~GfxRadialShading() {
4399 GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
4400 GfxRadialShading *shading;
4401 double x0A, y0A, r0A, x1A, y1A, r1A;
4402 double t0A, t1A;
4403 Function *funcsA[gfxColorMaxComps];
4404 int nFuncsA;
4405 GBool extend0A, extend1A;
4406 Object obj1, obj2;
4407 int i;
4409 x0A = y0A = r0A = x1A = y1A = r1A = 0;
4410 if (dict->lookup("Coords", &obj1)->isArray() &&
4411 obj1.arrayGetLength() == 6) {
4412 x0A = obj1.arrayGet(0, &obj2)->getNum();
4413 obj2.free();
4414 y0A = obj1.arrayGet(1, &obj2)->getNum();
4415 obj2.free();
4416 r0A = obj1.arrayGet(2, &obj2)->getNum();
4417 obj2.free();
4418 x1A = obj1.arrayGet(3, &obj2)->getNum();
4419 obj2.free();
4420 y1A = obj1.arrayGet(4, &obj2)->getNum();
4421 obj2.free();
4422 r1A = obj1.arrayGet(5, &obj2)->getNum();
4423 obj2.free();
4424 } else {
4425 error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
4426 goto err1;
4428 obj1.free();
4430 t0A = 0;
4431 t1A = 1;
4432 if (dict->lookup("Domain", &obj1)->isArray() &&
4433 obj1.arrayGetLength() == 2) {
4434 t0A = obj1.arrayGet(0, &obj2)->getNum();
4435 obj2.free();
4436 t1A = obj1.arrayGet(1, &obj2)->getNum();
4437 obj2.free();
4439 obj1.free();
4441 dict->lookup("Function", &obj1);
4442 if (obj1.isArray()) {
4443 nFuncsA = obj1.arrayGetLength();
4444 if (nFuncsA > gfxColorMaxComps) {
4445 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4446 goto err1;
4448 for (i = 0; i < nFuncsA; ++i) {
4449 obj1.arrayGet(i, &obj2);
4450 if (!(funcsA[i] = Function::parse(&obj2))) {
4451 obj1.free();
4452 obj2.free();
4453 goto err1;
4455 obj2.free();
4457 } else {
4458 nFuncsA = 1;
4459 if (!(funcsA[0] = Function::parse(&obj1))) {
4460 obj1.free();
4461 goto err1;
4464 obj1.free();
4466 extend0A = extend1A = gFalse;
4467 if (dict->lookup("Extend", &obj1)->isArray() &&
4468 obj1.arrayGetLength() == 2) {
4469 extend0A = obj1.arrayGet(0, &obj2)->getBool();
4470 obj2.free();
4471 extend1A = obj1.arrayGet(1, &obj2)->getBool();
4472 obj2.free();
4474 obj1.free();
4476 shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
4477 funcsA, nFuncsA, extend0A, extend1A);
4478 if (!shading->init(res, dict, out, state)) {
4479 delete shading;
4480 return NULL;
4482 return shading;
4484 err1:
4485 return NULL;
4488 GfxShading *GfxRadialShading::copy() {
4489 return new GfxRadialShading(this);
4492 double GfxRadialShading::getDistance(double sMin, double sMax) {
4493 double xMin, yMin, rMin, xMax, yMax, rMax;
4495 xMin = x0 + sMin * (x1 - x0);
4496 yMin = y0 + sMin * (y1 - y0);
4497 rMin = r0 + sMin * (r1 - r0);
4499 xMax = x0 + sMax * (x1 - x0);
4500 yMax = y0 + sMax * (y1 - y0);
4501 rMax = r0 + sMax * (r1 - r0);
4503 return hypot(xMax-xMin, yMax-yMin) + fabs(rMax-rMin);
4506 // extend range, adapted from cairo, radialExtendRange
4507 static GBool
4508 radialExtendRange (double range[2], double value, GBool valid)
4510 if (!valid)
4511 range[0] = range[1] = value;
4512 else if (value < range[0])
4513 range[0] = value;
4514 else if (value > range[1])
4515 range[1] = value;
4517 return gTrue;
4520 inline void radialEdge(double num, double den, double delta, double lower, double upper,
4521 double dr, double mindr, GBool &valid, double *range)
4523 if (fabs (den) >= RADIAL_EPSILON) {
4524 double t_edge, v;
4525 t_edge = (num) / (den);
4526 v = t_edge * (delta);
4527 if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))
4528 valid = radialExtendRange (range, t_edge, valid);
4532 inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr,
4533 double dr, double mindr, GBool &valid, double *range)
4535 b = (x) * dx + (y) * dy + cr * dr;
4536 if (fabs (b) >= RADIAL_EPSILON) {
4537 double t_corner;
4538 double x2 = (x) * (x);
4539 double y2 = (y) * (y);
4540 double cr2 = (cr) * (cr);
4541 double c = x2 + y2 - cr2;
4543 t_corner = 0.5 * c / b;
4544 if (t_corner * dr >= mindr)
4545 valid = radialExtendRange (range, t_corner, valid);
4549 inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr,
4550 double inva, double dr, double mindr, GBool &valid, double *range)
4552 b = (x) * dx + (y) * dy + cr * dr;
4553 c = (x) * (x) + (y) * (y) - cr * cr;
4554 d = b * b - a * c;
4555 if (d >= 0) {
4556 double t_corner;
4558 d = sqrt (d);
4559 t_corner = (b + d) * inva;
4560 if (t_corner * dr >= mindr)
4561 valid = radialExtendRange (range, t_corner, valid);
4562 t_corner = (b - d) * inva;
4563 if (t_corner * dr >= mindr)
4564 valid = radialExtendRange (range, t_corner, valid);
4567 void GfxRadialShading::getParameterRange(double *lower, double *upper,
4568 double xMin, double yMin,
4569 double xMax, double yMax) {
4570 double cx, cy, cr, dx, dy, dr;
4571 double a, x_focus, y_focus;
4572 double mindr, minx, miny, maxx, maxy;
4573 double range[2];
4574 GBool valid;
4576 // A radial pattern is considered degenerate if it can be
4577 // represented as a solid or clear pattern. This corresponds to one
4578 // of the two cases:
4580 // 1) The radii are both very small:
4581 // |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
4583 // 2) The two circles have about the same radius and are very
4584 // close to each other (approximately a cylinder gradient that
4585 // doesn't move with the parameter):
4586 // |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
4588 if (xMin >= xMax || yMin >=yMax ||
4589 (fabs (r0 - r1) < RADIAL_EPSILON &&
4590 (std::min<double>(r0, r1) < RADIAL_EPSILON ||
4591 std::max<double>(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) {
4592 *lower = *upper = 0;
4593 return;
4596 range[0] = range[1] = 0;
4597 valid = gFalse;
4599 x_focus = y_focus = 0; // silence gcc
4601 cx = x0;
4602 cy = y0;
4603 cr = r0;
4604 dx = x1 - cx;
4605 dy = y1 - cy;
4606 dr = r1 - cr;
4608 // translate by -(cx, cy) to simplify computations
4609 xMin -= cx;
4610 yMin -= cy;
4611 xMax -= cx;
4612 yMax -= cy;
4614 // enlarge boundaries slightly to avoid rounding problems in the
4615 // parameter range computation
4616 xMin -= RADIAL_EPSILON;
4617 yMin -= RADIAL_EPSILON;
4618 xMax += RADIAL_EPSILON;
4619 yMax += RADIAL_EPSILON;
4621 // enlarge boundaries even more to avoid rounding problems when
4622 // testing if a point belongs to the box
4623 minx = xMin - RADIAL_EPSILON;
4624 miny = yMin - RADIAL_EPSILON;
4625 maxx = xMax + RADIAL_EPSILON;
4626 maxy = yMax + RADIAL_EPSILON;
4628 // we dont' allow negative radiuses, so we will be checking that
4629 // t*dr >= mindr to consider t valid
4630 mindr = -(cr + RADIAL_EPSILON);
4632 // After the previous transformations, the start circle is centered
4633 // in the origin and has radius cr. A 1-unit change in the t
4634 // parameter corresponds to dx,dy,dr changes in the x,y,r of the
4635 // circle (center coordinates, radius).
4637 // To compute the minimum range needed to correctly draw the
4638 // pattern, we start with an empty range and extend it to include
4639 // the circles touching the bounding box or within it.
4641 // Focus, the point where the circle has radius == 0.
4643 // r = cr + t * dr = 0
4644 // t = -cr / dr
4646 // If the radius is constant (dr == 0) there is no focus (the
4647 // gradient represents a cylinder instead of a cone).
4648 if (fabs (dr) >= RADIAL_EPSILON) {
4649 double t_focus;
4651 t_focus = -cr / dr;
4652 x_focus = t_focus * dx;
4653 y_focus = t_focus * dy;
4654 if (minx <= x_focus && x_focus <= maxx &&
4655 miny <= y_focus && y_focus <= maxy)
4657 valid = radialExtendRange (range, t_focus, valid);
4661 // Circles externally tangent to box edges.
4663 // All circles have center in (dx, dy) * t
4665 // If the circle is tangent to the line defined by the edge of the
4666 // box, then at least one of the following holds true:
4668 // (dx*t) + (cr + dr*t) == x0 (left edge)
4669 // (dx*t) - (cr + dr*t) == x1 (right edge)
4670 // (dy*t) + (cr + dr*t) == y0 (top edge)
4671 // (dy*t) - (cr + dr*t) == y1 (bottom edge)
4673 // The solution is only valid if the tangent point is actually on
4674 // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
4675 // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
4677 // For the first equation:
4679 // (dx + dr) * t = x0 - cr
4680 // t = (x0 - cr) / (dx + dr)
4681 // y = dy * t
4683 // in the code this becomes:
4685 // t_edge = (num) / (den)
4686 // v = (delta) * t_edge
4688 // If the denominator in t is 0, the pattern is tangent to a line
4689 // parallel to the edge under examination. The corner-case where the
4690 // boundary line is the same as the edge is handled by the focus
4691 // point case and/or by the a==0 case.
4693 // circles tangent (externally) to left/right/top/bottom edge
4694 radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range);
4695 radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range);
4696 radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range);
4697 radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range);
4699 // Circles passing through a corner.
4701 // A circle passing through the point (x,y) satisfies:
4703 // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
4705 // If we set:
4706 // a = dx^2 + dy^2 - dr^2
4707 // b = x*dx + y*dy + cr*dr
4708 // c = x^2 + y^2 - cr^2
4709 // we have:
4710 // a*t^2 - 2*b*t + c == 0
4712 a = dx * dx + dy * dy - dr * dr;
4713 if (fabs (a) < RADIAL_EPSILON * RADIAL_EPSILON) {
4714 double b;
4716 // Ensure that gradients with both a and dr small are
4717 // considered degenerate.
4718 // The floating point version of the degeneracy test implemented
4719 // in _radial_pattern_is_degenerate() is:
4721 // 1) The circles are practically the same size:
4722 // |dr| < RADIAL_EPSILON
4723 // AND
4724 // 2a) The circles are both very small:
4725 // min (r0, r1) < RADIAL_EPSILON
4726 // OR
4727 // 2b) The circles are very close to each other:
4728 // max (|dx|, |dy|) < 2 * RADIAL_EPSILON
4730 // Assuming that the gradient is not degenerate, we want to
4731 // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
4733 // If the gradient is not degenerate yet it has |dr| <
4734 // RADIAL_EPSILON, (2b) is false, thus:
4736 // max (|dx|, |dy|) >= 2*RADIAL_EPSILON
4737 // which implies:
4738 // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
4740 // From the definition of a, we get:
4741 // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
4742 // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
4743 // 3*RADIAL_EPSILON^2 < dr^2
4745 // which is inconsistent with the hypotheses, thus |dr| <
4746 // RADIAL_EPSILON is false or the gradient is degenerate.
4748 assert (fabs (dr) >= RADIAL_EPSILON);
4750 // If a == 0, all the circles are tangent to a line in the
4751 // focus point. If this line is within the box extents, we
4752 // should add the circle with infinite radius, but this would
4753 // make the range unbounded. We will be limiting the range to
4754 // [0,1] anyway, so we simply add the biggest legitimate
4755 // circle (it happens for 0 or for 1).
4756 if (dr < 0) {
4757 valid = radialExtendRange (range, 0, valid);
4758 } else {
4759 valid = radialExtendRange (range, 1, valid);
4762 // Nondegenerate, nonlimit circles passing through the corners.
4764 // a == 0 && a*t^2 - 2*b*t + c == 0
4766 // t = c / (2*b)
4768 // The b == 0 case has just been handled, so we only have to
4769 // compute this if b != 0.
4771 // circles touching each corner
4772 radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4773 radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4774 radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4775 radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4776 } else {
4777 double inva, b, c, d;
4779 inva = 1 / a;
4781 // Nondegenerate, nonlimit circles passing through the corners.
4783 // a != 0 && a*t^2 - 2*b*t + c == 0
4785 // t = (b +- sqrt (b*b - a*c)) / a
4787 // If the argument of sqrt() is negative, then no circle
4788 // passes through the corner.
4790 // circles touching each corner
4791 radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4792 radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4793 radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4794 radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4797 *lower = std::max<double>(0., std::min<double>(1., range[0]));
4798 *upper = std::max<double>(0., std::min<double>(1., range[1]));
4801 //------------------------------------------------------------------------
4802 // GfxShadingBitBuf
4803 //------------------------------------------------------------------------
4805 class GfxShadingBitBuf {
4806 public:
4808 GfxShadingBitBuf(Stream *strA);
4809 ~GfxShadingBitBuf();
4810 GBool getBits(int n, Guint *val);
4811 void flushBits();
4813 private:
4815 Stream *str;
4816 int bitBuf;
4817 int nBits;
4820 GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
4821 str = strA;
4822 str->reset();
4823 bitBuf = 0;
4824 nBits = 0;
4827 GfxShadingBitBuf::~GfxShadingBitBuf() {
4828 str->close();
4831 GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
4832 int x;
4834 if (nBits >= n) {
4835 x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
4836 nBits -= n;
4837 } else {
4838 x = 0;
4839 if (nBits > 0) {
4840 x = bitBuf & ((1 << nBits) - 1);
4841 n -= nBits;
4842 nBits = 0;
4844 while (n > 0) {
4845 if ((bitBuf = str->getChar()) == EOF) {
4846 nBits = 0;
4847 return gFalse;
4849 if (n >= 8) {
4850 x = (x << 8) | bitBuf;
4851 n -= 8;
4852 } else {
4853 x = (x << n) | (bitBuf >> (8 - n));
4854 nBits = 8 - n;
4855 n = 0;
4859 *val = x;
4860 return gTrue;
4863 void GfxShadingBitBuf::flushBits() {
4864 bitBuf = 0;
4865 nBits = 0;
4868 //------------------------------------------------------------------------
4869 // GfxGouraudTriangleShading
4870 //------------------------------------------------------------------------
4872 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4873 int typeA,
4874 GfxGouraudVertex *verticesA, int nVerticesA,
4875 int (*trianglesA)[3], int nTrianglesA,
4876 Function **funcsA, int nFuncsA):
4877 GfxShading(typeA)
4879 int i;
4881 vertices = verticesA;
4882 nVertices = nVerticesA;
4883 triangles = trianglesA;
4884 nTriangles = nTrianglesA;
4885 nFuncs = nFuncsA;
4886 for (i = 0; i < nFuncs; ++i) {
4887 funcs[i] = funcsA[i];
4891 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4892 GfxGouraudTriangleShading *shading):
4893 GfxShading(shading)
4895 int i;
4897 nVertices = shading->nVertices;
4898 vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
4899 memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
4900 nTriangles = shading->nTriangles;
4901 triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
4902 memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
4903 nFuncs = shading->nFuncs;
4904 for (i = 0; i < nFuncs; ++i) {
4905 funcs[i] = shading->funcs[i]->copy();
4909 GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
4910 int i;
4912 gfree(vertices);
4913 gfree(triangles);
4914 for (i = 0; i < nFuncs; ++i) {
4915 delete funcs[i];
4919 GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, int typeA,
4920 Dict *dict,
4921 Stream *str,
4922 OutputDev *out, GfxState *gfxState) {
4923 GfxGouraudTriangleShading *shading;
4924 Function *funcsA[gfxColorMaxComps];
4925 int nFuncsA;
4926 int coordBits, compBits, flagBits, vertsPerRow, nRows;
4927 double xMin, xMax, yMin, yMax;
4928 double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
4929 double xMul, yMul;
4930 double cMul[gfxColorMaxComps];
4931 GfxGouraudVertex *verticesA;
4932 int (*trianglesA)[3];
4933 int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
4934 Guint x, y, flag;
4935 Guint c[gfxColorMaxComps];
4936 GfxShadingBitBuf *bitBuf;
4937 Object obj1, obj2;
4938 int i, j, k, state;
4940 if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
4941 coordBits = obj1.getInt();
4942 } else {
4943 error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4944 goto err2;
4946 obj1.free();
4947 if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
4948 compBits = obj1.getInt();
4949 } else {
4950 error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4951 goto err2;
4953 obj1.free();
4954 flagBits = vertsPerRow = 0; // make gcc happy
4955 if (typeA == 4) {
4956 if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
4957 flagBits = obj1.getInt();
4958 } else {
4959 error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4960 goto err2;
4962 obj1.free();
4963 } else {
4964 if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
4965 vertsPerRow = obj1.getInt();
4966 } else {
4967 error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary");
4968 goto err2;
4970 obj1.free();
4972 if (dict->lookup("Decode", &obj1)->isArray() &&
4973 obj1.arrayGetLength() >= 6) {
4974 xMin = obj1.arrayGet(0, &obj2)->getNum();
4975 obj2.free();
4976 xMax = obj1.arrayGet(1, &obj2)->getNum();
4977 obj2.free();
4978 xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
4979 yMin = obj1.arrayGet(2, &obj2)->getNum();
4980 obj2.free();
4981 yMax = obj1.arrayGet(3, &obj2)->getNum();
4982 obj2.free();
4983 yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4984 for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4985 cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
4986 obj2.free();
4987 cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
4988 obj2.free();
4989 cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
4991 nComps = i;
4992 } else {
4993 error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4994 goto err2;
4996 obj1.free();
4998 if (!dict->lookup("Function", &obj1)->isNull()) {
4999 if (obj1.isArray()) {
5000 nFuncsA = obj1.arrayGetLength();
5001 if (nFuncsA > gfxColorMaxComps) {
5002 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
5003 goto err1;
5005 for (i = 0; i < nFuncsA; ++i) {
5006 obj1.arrayGet(i, &obj2);
5007 if (!(funcsA[i] = Function::parse(&obj2))) {
5008 obj1.free();
5009 obj2.free();
5010 goto err1;
5012 obj2.free();
5014 } else {
5015 nFuncsA = 1;
5016 if (!(funcsA[0] = Function::parse(&obj1))) {
5017 obj1.free();
5018 goto err1;
5021 } else {
5022 nFuncsA = 0;
5024 obj1.free();
5026 nVerticesA = nTrianglesA = 0;
5027 verticesA = NULL;
5028 trianglesA = NULL;
5029 vertSize = triSize = 0;
5030 state = 0;
5031 flag = 0; // make gcc happy
5032 bitBuf = new GfxShadingBitBuf(str);
5033 while (1) {
5034 if (typeA == 4) {
5035 if (!bitBuf->getBits(flagBits, &flag)) {
5036 break;
5039 if (!bitBuf->getBits(coordBits, &x) ||
5040 !bitBuf->getBits(coordBits, &y)) {
5041 break;
5043 for (i = 0; i < nComps; ++i) {
5044 if (!bitBuf->getBits(compBits, &c[i])) {
5045 break;
5048 if (i < nComps) {
5049 break;
5051 if (nVerticesA == vertSize) {
5052 int oldVertSize = vertSize;
5053 vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
5054 verticesA = (GfxGouraudVertex *)
5055 greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
5056 memset(verticesA + oldVertSize, 0, (vertSize - oldVertSize) * sizeof(GfxGouraudVertex));
5058 verticesA[nVerticesA].x = xMin + xMul * (double)x;
5059 verticesA[nVerticesA].y = yMin + yMul * (double)y;
5060 for (i = 0; i < nComps; ++i) {
5061 verticesA[nVerticesA].color.c[i] =
5062 dblToCol(cMin[i] + cMul[i] * (double)c[i]);
5064 ++nVerticesA;
5065 bitBuf->flushBits();
5066 if (typeA == 4) {
5067 if (state == 0 || state == 1) {
5068 ++state;
5069 } else if (state == 2 || flag > 0) {
5070 if (nTrianglesA == triSize) {
5071 triSize = (triSize == 0) ? 16 : 2 * triSize;
5072 trianglesA = (int (*)[3])
5073 greallocn(trianglesA, triSize * 3, sizeof(int));
5075 if (state == 2) {
5076 trianglesA[nTrianglesA][0] = nVerticesA - 3;
5077 trianglesA[nTrianglesA][1] = nVerticesA - 2;
5078 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5079 ++state;
5080 } else if (flag == 1) {
5081 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
5082 trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
5083 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5084 } else { // flag == 2
5085 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
5086 trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
5087 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5089 ++nTrianglesA;
5090 } else { // state == 3 && flag == 0
5091 state = 1;
5095 delete bitBuf;
5096 if (typeA == 5 && nVerticesA > 0) {
5097 nRows = nVerticesA / vertsPerRow;
5098 nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
5099 trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
5100 k = 0;
5101 for (i = 0; i < nRows - 1; ++i) {
5102 for (j = 0; j < vertsPerRow - 1; ++j) {
5103 trianglesA[k][0] = i * vertsPerRow + j;
5104 trianglesA[k][1] = i * vertsPerRow + j+1;
5105 trianglesA[k][2] = (i+1) * vertsPerRow + j;
5106 ++k;
5107 trianglesA[k][0] = i * vertsPerRow + j+1;
5108 trianglesA[k][1] = (i+1) * vertsPerRow + j;
5109 trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
5110 ++k;
5115 shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
5116 trianglesA, nTrianglesA,
5117 funcsA, nFuncsA);
5118 if (!shading->init(res, dict, out, gfxState)) {
5119 delete shading;
5120 return NULL;
5122 return shading;
5124 err2:
5125 obj1.free();
5126 err1:
5127 return NULL;
5130 GfxShading *GfxGouraudTriangleShading::copy() {
5131 return new GfxGouraudTriangleShading(this);
5134 void GfxGouraudTriangleShading::getTriangle(
5135 int i,
5136 double *x0, double *y0, GfxColor *color0,
5137 double *x1, double *y1, GfxColor *color1,
5138 double *x2, double *y2, GfxColor *color2) {
5139 double in;
5140 double out[gfxColorMaxComps];
5141 int v, j;
5143 assert(!isParameterized());
5145 v = triangles[i][0];
5146 *x0 = vertices[v].x;
5147 *y0 = vertices[v].y;
5148 if (nFuncs > 0) {
5149 in = colToDbl(vertices[v].color.c[0]);
5150 for (j = 0; j < nFuncs; ++j) {
5151 funcs[j]->transform(&in, &out[j]);
5153 for (j = 0; j < gfxColorMaxComps; ++j) {
5154 color0->c[j] = dblToCol(out[j]);
5156 } else {
5157 *color0 = vertices[v].color;
5159 v = triangles[i][1];
5160 *x1 = vertices[v].x;
5161 *y1 = vertices[v].y;
5162 if (nFuncs > 0) {
5163 in = colToDbl(vertices[v].color.c[0]);
5164 for (j = 0; j < nFuncs; ++j) {
5165 funcs[j]->transform(&in, &out[j]);
5167 for (j = 0; j < gfxColorMaxComps; ++j) {
5168 color1->c[j] = dblToCol(out[j]);
5170 } else {
5171 *color1 = vertices[v].color;
5173 v = triangles[i][2];
5174 *x2 = vertices[v].x;
5175 *y2 = vertices[v].y;
5176 if (nFuncs > 0) {
5177 in = colToDbl(vertices[v].color.c[0]);
5178 for (j = 0; j < nFuncs; ++j) {
5179 funcs[j]->transform(&in, &out[j]);
5181 for (j = 0; j < gfxColorMaxComps; ++j) {
5182 color2->c[j] = dblToCol(out[j]);
5184 } else {
5185 *color2 = vertices[v].color;
5189 void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) {
5190 double out[gfxColorMaxComps];
5192 for (int j = 0; j < nFuncs; ++j) {
5193 funcs[j]->transform(&t, &out[j]);
5195 for (int j = 0; j < gfxColorMaxComps; ++j) {
5196 color->c[j] = dblToCol(out[j]);
5200 void GfxGouraudTriangleShading::getTriangle(int i,
5201 double *x0, double *y0, double *color0,
5202 double *x1, double *y1, double *color1,
5203 double *x2, double *y2, double *color2) {
5204 int v;
5206 assert(isParameterized());
5208 v = triangles[i][0];
5209 if (likely(v >= 0 && v < nVertices)) {
5210 *x0 = vertices[v].x;
5211 *y0 = vertices[v].y;
5212 *color0 = colToDbl(vertices[v].color.c[0]);
5214 v = triangles[i][1];
5215 if (likely(v >= 0 && v < nVertices)) {
5216 *x1 = vertices[v].x;
5217 *y1 = vertices[v].y;
5218 *color1 = colToDbl(vertices[v].color.c[0]);
5220 v = triangles[i][2];
5221 if (likely(v >= 0 && v < nVertices)) {
5222 *x2 = vertices[v].x;
5223 *y2 = vertices[v].y;
5224 *color2 = colToDbl(vertices[v].color.c[0]);
5228 //------------------------------------------------------------------------
5229 // GfxPatchMeshShading
5230 //------------------------------------------------------------------------
5232 GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
5233 GfxPatch *patchesA, int nPatchesA,
5234 Function **funcsA, int nFuncsA):
5235 GfxShading(typeA)
5237 int i;
5239 patches = patchesA;
5240 nPatches = nPatchesA;
5241 nFuncs = nFuncsA;
5242 for (i = 0; i < nFuncs; ++i) {
5243 funcs[i] = funcsA[i];
5247 GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
5248 GfxShading(shading)
5250 int i;
5252 nPatches = shading->nPatches;
5253 patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
5254 memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
5255 nFuncs = shading->nFuncs;
5256 for (i = 0; i < nFuncs; ++i) {
5257 funcs[i] = shading->funcs[i]->copy();
5261 GfxPatchMeshShading::~GfxPatchMeshShading() {
5262 int i;
5264 gfree(patches);
5265 for (i = 0; i < nFuncs; ++i) {
5266 delete funcs[i];
5270 GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Dict *dict,
5271 Stream *str, OutputDev *out, GfxState *state) {
5272 GfxPatchMeshShading *shading;
5273 Function *funcsA[gfxColorMaxComps];
5274 int nFuncsA;
5275 int coordBits, compBits, flagBits;
5276 double xMin, xMax, yMin, yMax;
5277 double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
5278 double xMul, yMul;
5279 double cMul[gfxColorMaxComps];
5280 GfxPatch *patchesA, *p;
5281 int nComps, nPatchesA, patchesSize, nPts, nColors;
5282 Guint flag;
5283 double x[16], y[16];
5284 Guint xi, yi;
5285 double c[4][gfxColorMaxComps];
5286 Guint ci;
5287 GfxShadingBitBuf *bitBuf;
5288 Object obj1, obj2;
5289 int i, j;
5291 if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
5292 coordBits = obj1.getInt();
5293 } else {
5294 error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
5295 goto err2;
5297 obj1.free();
5298 if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
5299 compBits = obj1.getInt();
5300 } else {
5301 error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
5302 goto err2;
5304 obj1.free();
5305 if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
5306 flagBits = obj1.getInt();
5307 } else {
5308 error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
5309 goto err2;
5311 obj1.free();
5312 if (dict->lookup("Decode", &obj1)->isArray() &&
5313 obj1.arrayGetLength() >= 6) {
5314 xMin = obj1.arrayGet(0, &obj2)->getNum();
5315 obj2.free();
5316 xMax = obj1.arrayGet(1, &obj2)->getNum();
5317 obj2.free();
5318 xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
5319 yMin = obj1.arrayGet(2, &obj2)->getNum();
5320 obj2.free();
5321 yMax = obj1.arrayGet(3, &obj2)->getNum();
5322 obj2.free();
5323 yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
5324 for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
5325 cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
5326 obj2.free();
5327 cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
5328 obj2.free();
5329 cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
5331 nComps = i;
5332 } else {
5333 error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
5334 goto err2;
5336 obj1.free();
5338 if (!dict->lookup("Function", &obj1)->isNull()) {
5339 if (obj1.isArray()) {
5340 nFuncsA = obj1.arrayGetLength();
5341 if (nFuncsA > gfxColorMaxComps) {
5342 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
5343 goto err1;
5345 for (i = 0; i < nFuncsA; ++i) {
5346 obj1.arrayGet(i, &obj2);
5347 if (!(funcsA[i] = Function::parse(&obj2))) {
5348 obj1.free();
5349 obj2.free();
5350 goto err1;
5352 obj2.free();
5354 } else {
5355 nFuncsA = 1;
5356 if (!(funcsA[0] = Function::parse(&obj1))) {
5357 obj1.free();
5358 goto err1;
5361 } else {
5362 nFuncsA = 0;
5364 obj1.free();
5366 nPatchesA = 0;
5367 patchesA = NULL;
5368 patchesSize = 0;
5369 bitBuf = new GfxShadingBitBuf(str);
5370 while (1) {
5371 if (!bitBuf->getBits(flagBits, &flag)) {
5372 break;
5374 if (typeA == 6) {
5375 switch (flag) {
5376 case 0: nPts = 12; nColors = 4; break;
5377 case 1:
5378 case 2:
5379 case 3:
5380 default: nPts = 8; nColors = 2; break;
5382 } else {
5383 switch (flag) {
5384 case 0: nPts = 16; nColors = 4; break;
5385 case 1:
5386 case 2:
5387 case 3:
5388 default: nPts = 12; nColors = 2; break;
5391 for (i = 0; i < nPts; ++i) {
5392 if (!bitBuf->getBits(coordBits, &xi) ||
5393 !bitBuf->getBits(coordBits, &yi)) {
5394 break;
5396 x[i] = xMin + xMul * (double)xi;
5397 y[i] = yMin + yMul * (double)yi;
5399 if (i < nPts) {
5400 break;
5402 for (i = 0; i < nColors; ++i) {
5403 for (j = 0; j < nComps; ++j) {
5404 if (!bitBuf->getBits(compBits, &ci)) {
5405 break;
5407 c[i][j] = cMin[j] + cMul[j] * (double)ci;
5408 if( nFuncsA == 0 ) {
5409 // ... and colorspace values can also be stored into doubles.
5410 // They will be casted later.
5411 c[i][j] = dblToCol(c[i][j]);
5414 if (j < nComps) {
5415 break;
5418 if (i < nColors) {
5419 break;
5421 if (nPatchesA == patchesSize) {
5422 int oldPatchesSize = patchesSize;
5423 patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
5424 patchesA = (GfxPatch *)greallocn(patchesA,
5425 patchesSize, sizeof(GfxPatch));
5426 memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch));
5428 p = &patchesA[nPatchesA];
5429 if (typeA == 6) {
5430 switch (flag) {
5431 case 0:
5432 p->x[0][0] = x[0];
5433 p->y[0][0] = y[0];
5434 p->x[0][1] = x[1];
5435 p->y[0][1] = y[1];
5436 p->x[0][2] = x[2];
5437 p->y[0][2] = y[2];
5438 p->x[0][3] = x[3];
5439 p->y[0][3] = y[3];
5440 p->x[1][3] = x[4];
5441 p->y[1][3] = y[4];
5442 p->x[2][3] = x[5];
5443 p->y[2][3] = y[5];
5444 p->x[3][3] = x[6];
5445 p->y[3][3] = y[6];
5446 p->x[3][2] = x[7];
5447 p->y[3][2] = y[7];
5448 p->x[3][1] = x[8];
5449 p->y[3][1] = y[8];
5450 p->x[3][0] = x[9];
5451 p->y[3][0] = y[9];
5452 p->x[2][0] = x[10];
5453 p->y[2][0] = y[10];
5454 p->x[1][0] = x[11];
5455 p->y[1][0] = y[11];
5456 for (j = 0; j < nComps; ++j) {
5457 p->color[0][0].c[j] = c[0][j];
5458 p->color[0][1].c[j] = c[1][j];
5459 p->color[1][1].c[j] = c[2][j];
5460 p->color[1][0].c[j] = c[3][j];
5462 break;
5463 case 1:
5464 if (nPatchesA == 0) {
5465 goto err1;
5467 p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
5468 p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
5469 p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
5470 p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
5471 p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
5472 p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
5473 p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
5474 p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
5475 p->x[1][3] = x[0];
5476 p->y[1][3] = y[0];
5477 p->x[2][3] = x[1];
5478 p->y[2][3] = y[1];
5479 p->x[3][3] = x[2];
5480 p->y[3][3] = y[2];
5481 p->x[3][2] = x[3];
5482 p->y[3][2] = y[3];
5483 p->x[3][1] = x[4];
5484 p->y[3][1] = y[4];
5485 p->x[3][0] = x[5];
5486 p->y[3][0] = y[5];
5487 p->x[2][0] = x[6];
5488 p->y[2][0] = y[6];
5489 p->x[1][0] = x[7];
5490 p->y[1][0] = y[7];
5491 for (j = 0; j < nComps; ++j) {
5492 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
5493 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5494 p->color[1][1].c[j] = c[0][j];
5495 p->color[1][0].c[j] = c[1][j];
5497 break;
5498 case 2:
5499 if (nPatchesA == 0) {
5500 goto err1;
5502 p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
5503 p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
5504 p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
5505 p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
5506 p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
5507 p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
5508 p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
5509 p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
5510 p->x[1][3] = x[0];
5511 p->y[1][3] = y[0];
5512 p->x[2][3] = x[1];
5513 p->y[2][3] = y[1];
5514 p->x[3][3] = x[2];
5515 p->y[3][3] = y[2];
5516 p->x[3][2] = x[3];
5517 p->y[3][2] = y[3];
5518 p->x[3][1] = x[4];
5519 p->y[3][1] = y[4];
5520 p->x[3][0] = x[5];
5521 p->y[3][0] = y[5];
5522 p->x[2][0] = x[6];
5523 p->y[2][0] = y[6];
5524 p->x[1][0] = x[7];
5525 p->y[1][0] = y[7];
5526 for (j = 0; j < nComps; ++j) {
5527 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5528 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5529 p->color[1][1].c[j] = c[0][j];
5530 p->color[1][0].c[j] = c[1][j];
5532 break;
5533 case 3:
5534 if (nPatchesA == 0) {
5535 goto err1;
5537 p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
5538 p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
5539 p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
5540 p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
5541 p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
5542 p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
5543 p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
5544 p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
5545 p->x[1][3] = x[0];
5546 p->y[1][3] = y[0];
5547 p->x[2][3] = x[1];
5548 p->y[2][3] = y[1];
5549 p->x[3][3] = x[2];
5550 p->y[3][3] = y[2];
5551 p->x[3][2] = x[3];
5552 p->y[3][2] = y[3];
5553 p->x[3][1] = x[4];
5554 p->y[3][1] = y[4];
5555 p->x[3][0] = x[5];
5556 p->y[3][0] = y[5];
5557 p->x[2][0] = x[6];
5558 p->y[2][0] = y[6];
5559 p->x[1][0] = x[7];
5560 p->y[1][0] = y[7];
5561 for (j = 0; j < nComps; ++j) {
5562 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5563 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
5564 p->color[1][1].c[j] = c[0][j];
5565 p->color[1][0].c[j] = c[1][j];
5567 break;
5569 } else {
5570 switch (flag) {
5571 case 0:
5572 p->x[0][0] = x[0];
5573 p->y[0][0] = y[0];
5574 p->x[0][1] = x[1];
5575 p->y[0][1] = y[1];
5576 p->x[0][2] = x[2];
5577 p->y[0][2] = y[2];
5578 p->x[0][3] = x[3];
5579 p->y[0][3] = y[3];
5580 p->x[1][3] = x[4];
5581 p->y[1][3] = y[4];
5582 p->x[2][3] = x[5];
5583 p->y[2][3] = y[5];
5584 p->x[3][3] = x[6];
5585 p->y[3][3] = y[6];
5586 p->x[3][2] = x[7];
5587 p->y[3][2] = y[7];
5588 p->x[3][1] = x[8];
5589 p->y[3][1] = y[8];
5590 p->x[3][0] = x[9];
5591 p->y[3][0] = y[9];
5592 p->x[2][0] = x[10];
5593 p->y[2][0] = y[10];
5594 p->x[1][0] = x[11];
5595 p->y[1][0] = y[11];
5596 p->x[1][1] = x[12];
5597 p->y[1][1] = y[12];
5598 p->x[1][2] = x[13];
5599 p->y[1][2] = y[13];
5600 p->x[2][2] = x[14];
5601 p->y[2][2] = y[14];
5602 p->x[2][1] = x[15];
5603 p->y[2][1] = y[15];
5604 for (j = 0; j < nComps; ++j) {
5605 p->color[0][0].c[j] = c[0][j];
5606 p->color[0][1].c[j] = c[1][j];
5607 p->color[1][1].c[j] = c[2][j];
5608 p->color[1][0].c[j] = c[3][j];
5610 break;
5611 case 1:
5612 if (nPatchesA == 0) {
5613 goto err1;
5615 p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
5616 p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
5617 p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
5618 p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
5619 p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
5620 p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
5621 p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
5622 p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
5623 p->x[1][3] = x[0];
5624 p->y[1][3] = y[0];
5625 p->x[2][3] = x[1];
5626 p->y[2][3] = y[1];
5627 p->x[3][3] = x[2];
5628 p->y[3][3] = y[2];
5629 p->x[3][2] = x[3];
5630 p->y[3][2] = y[3];
5631 p->x[3][1] = x[4];
5632 p->y[3][1] = y[4];
5633 p->x[3][0] = x[5];
5634 p->y[3][0] = y[5];
5635 p->x[2][0] = x[6];
5636 p->y[2][0] = y[6];
5637 p->x[1][0] = x[7];
5638 p->y[1][0] = y[7];
5639 p->x[1][1] = x[8];
5640 p->y[1][1] = y[8];
5641 p->x[1][2] = x[9];
5642 p->y[1][2] = y[9];
5643 p->x[2][2] = x[10];
5644 p->y[2][2] = y[10];
5645 p->x[2][1] = x[11];
5646 p->y[2][1] = y[11];
5647 for (j = 0; j < nComps; ++j) {
5648 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
5649 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5650 p->color[1][1].c[j] = c[0][j];
5651 p->color[1][0].c[j] = c[1][j];
5653 break;
5654 case 2:
5655 if (nPatchesA == 0) {
5656 goto err1;
5658 p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
5659 p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
5660 p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
5661 p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
5662 p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
5663 p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
5664 p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
5665 p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
5666 p->x[1][3] = x[0];
5667 p->y[1][3] = y[0];
5668 p->x[2][3] = x[1];
5669 p->y[2][3] = y[1];
5670 p->x[3][3] = x[2];
5671 p->y[3][3] = y[2];
5672 p->x[3][2] = x[3];
5673 p->y[3][2] = y[3];
5674 p->x[3][1] = x[4];
5675 p->y[3][1] = y[4];
5676 p->x[3][0] = x[5];
5677 p->y[3][0] = y[5];
5678 p->x[2][0] = x[6];
5679 p->y[2][0] = y[6];
5680 p->x[1][0] = x[7];
5681 p->y[1][0] = y[7];
5682 p->x[1][1] = x[8];
5683 p->y[1][1] = y[8];
5684 p->x[1][2] = x[9];
5685 p->y[1][2] = y[9];
5686 p->x[2][2] = x[10];
5687 p->y[2][2] = y[10];
5688 p->x[2][1] = x[11];
5689 p->y[2][1] = y[11];
5690 for (j = 0; j < nComps; ++j) {
5691 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5692 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5693 p->color[1][1].c[j] = c[0][j];
5694 p->color[1][0].c[j] = c[1][j];
5696 break;
5697 case 3:
5698 if (nPatchesA == 0) {
5699 goto err1;
5701 p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
5702 p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
5703 p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
5704 p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
5705 p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
5706 p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
5707 p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
5708 p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
5709 p->x[1][3] = x[0];
5710 p->y[1][3] = y[0];
5711 p->x[2][3] = x[1];
5712 p->y[2][3] = y[1];
5713 p->x[3][3] = x[2];
5714 p->y[3][3] = y[2];
5715 p->x[3][2] = x[3];
5716 p->y[3][2] = y[3];
5717 p->x[3][1] = x[4];
5718 p->y[3][1] = y[4];
5719 p->x[3][0] = x[5];
5720 p->y[3][0] = y[5];
5721 p->x[2][0] = x[6];
5722 p->y[2][0] = y[6];
5723 p->x[1][0] = x[7];
5724 p->y[1][0] = y[7];
5725 p->x[1][1] = x[8];
5726 p->y[1][1] = y[8];
5727 p->x[1][2] = x[9];
5728 p->y[1][2] = y[9];
5729 p->x[2][2] = x[10];
5730 p->y[2][2] = y[10];
5731 p->x[2][1] = x[11];
5732 p->y[2][1] = y[11];
5733 for (j = 0; j < nComps; ++j) {
5734 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5735 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
5736 p->color[1][1].c[j] = c[0][j];
5737 p->color[1][0].c[j] = c[1][j];
5739 break;
5742 ++nPatchesA;
5743 bitBuf->flushBits();
5745 delete bitBuf;
5747 if (typeA == 6) {
5748 for (i = 0; i < nPatchesA; ++i) {
5749 p = &patchesA[i];
5750 p->x[1][1] = (-4 * p->x[0][0]
5751 +6 * (p->x[0][1] + p->x[1][0])
5752 -2 * (p->x[0][3] + p->x[3][0])
5753 +3 * (p->x[3][1] + p->x[1][3])
5754 - p->x[3][3]) / 9;
5755 p->y[1][1] = (-4 * p->y[0][0]
5756 +6 * (p->y[0][1] + p->y[1][0])
5757 -2 * (p->y[0][3] + p->y[3][0])
5758 +3 * (p->y[3][1] + p->y[1][3])
5759 - p->y[3][3]) / 9;
5760 p->x[1][2] = (-4 * p->x[0][3]
5761 +6 * (p->x[0][2] + p->x[1][3])
5762 -2 * (p->x[0][0] + p->x[3][3])
5763 +3 * (p->x[3][2] + p->x[1][0])
5764 - p->x[3][0]) / 9;
5765 p->y[1][2] = (-4 * p->y[0][3]
5766 +6 * (p->y[0][2] + p->y[1][3])
5767 -2 * (p->y[0][0] + p->y[3][3])
5768 +3 * (p->y[3][2] + p->y[1][0])
5769 - p->y[3][0]) / 9;
5770 p->x[2][1] = (-4 * p->x[3][0]
5771 +6 * (p->x[3][1] + p->x[2][0])
5772 -2 * (p->x[3][3] + p->x[0][0])
5773 +3 * (p->x[0][1] + p->x[2][3])
5774 - p->x[0][3]) / 9;
5775 p->y[2][1] = (-4 * p->y[3][0]
5776 +6 * (p->y[3][1] + p->y[2][0])
5777 -2 * (p->y[3][3] + p->y[0][0])
5778 +3 * (p->y[0][1] + p->y[2][3])
5779 - p->y[0][3]) / 9;
5780 p->x[2][2] = (-4 * p->x[3][3]
5781 +6 * (p->x[3][2] + p->x[2][3])
5782 -2 * (p->x[3][0] + p->x[0][3])
5783 +3 * (p->x[0][2] + p->x[2][0])
5784 - p->x[0][0]) / 9;
5785 p->y[2][2] = (-4 * p->y[3][3]
5786 +6 * (p->y[3][2] + p->y[2][3])
5787 -2 * (p->y[3][0] + p->y[0][3])
5788 +3 * (p->y[0][2] + p->y[2][0])
5789 - p->y[0][0]) / 9;
5793 shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
5794 funcsA, nFuncsA);
5795 if (!shading->init(res, dict, out, state)) {
5796 delete shading;
5797 return NULL;
5799 return shading;
5801 err2:
5802 obj1.free();
5803 err1:
5804 return NULL;
5807 void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) {
5808 double out[gfxColorMaxComps];
5810 for (int j = 0; j < nFuncs; ++j) {
5811 funcs[j]->transform(&t, &out[j]);
5813 for (int j = 0; j < gfxColorMaxComps; ++j) {
5814 color->c[j] = dblToCol(out[j]);
5818 GfxShading *GfxPatchMeshShading::copy() {
5819 return new GfxPatchMeshShading(this);
5822 //------------------------------------------------------------------------
5823 // GfxImageColorMap
5824 //------------------------------------------------------------------------
5826 GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
5827 GfxColorSpace *colorSpaceA) {
5828 GfxIndexedColorSpace *indexedCS;
5829 GfxSeparationColorSpace *sepCS;
5830 int maxPixel, indexHigh;
5831 Guchar *indexedLookup;
5832 Function *sepFunc;
5833 Object obj;
5834 double x[gfxColorMaxComps];
5835 double y[gfxColorMaxComps];
5836 int i, j, k;
5837 double mapped;
5838 GBool useByteLookup;
5840 ok = gTrue;
5841 useMatte = gFalse;
5843 // bits per component and color space
5844 bits = bitsA;
5845 maxPixel = (1 << bits) - 1;
5846 colorSpace = colorSpaceA;
5848 // this is a hack to support 16 bits images, everywhere
5849 // we assume a component fits in 8 bits, with this hack
5850 // we treat 16 bit images as 8 bit ones until it's fixed correctly.
5851 // The hack has another part on ImageStream::getLine
5852 if (maxPixel > 255) maxPixel = 255;
5854 // initialize
5855 for (k = 0; k < gfxColorMaxComps; ++k) {
5856 lookup[k] = NULL;
5857 lookup2[k] = NULL;
5859 byte_lookup = NULL;
5861 // get decode map
5862 if (decode->isNull()) {
5863 nComps = colorSpace->getNComps();
5864 colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
5865 } else if (decode->isArray()) {
5866 nComps = decode->arrayGetLength() / 2;
5867 if (nComps < colorSpace->getNComps()) {
5868 goto err1;
5870 if (nComps > colorSpace->getNComps()) {
5871 error(errSyntaxWarning, -1, "Too many elements in Decode array");
5872 nComps = colorSpace->getNComps();
5874 for (i = 0; i < nComps; ++i) {
5875 decode->arrayGet(2*i, &obj);
5876 if (!obj.isNum()) {
5877 goto err2;
5879 decodeLow[i] = obj.getNum();
5880 obj.free();
5881 decode->arrayGet(2*i+1, &obj);
5882 if (!obj.isNum()) {
5883 goto err2;
5885 decodeRange[i] = obj.getNum() - decodeLow[i];
5886 obj.free();
5888 } else {
5889 goto err1;
5892 // Construct a lookup table -- this stores pre-computed decoded
5893 // values for each component, i.e., the result of applying the
5894 // decode mapping to each possible image pixel component value.
5895 for (k = 0; k < nComps; ++k) {
5896 lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5897 sizeof(GfxColorComp));
5898 for (i = 0; i <= maxPixel; ++i) {
5899 lookup[k][i] = dblToCol(decodeLow[k] +
5900 (i * decodeRange[k]) / maxPixel);
5904 // Optimization: for Indexed and Separation color spaces (which have
5905 // only one component), we pre-compute a second lookup table with
5906 // color values
5907 colorSpace2 = NULL;
5908 nComps2 = 0;
5909 useByteLookup = gFalse;
5910 switch (colorSpace->getMode()) {
5911 case csIndexed:
5912 // Note that indexHigh may not be the same as maxPixel --
5913 // Distiller will remove unused palette entries, resulting in
5914 // indexHigh < maxPixel.
5915 indexedCS = (GfxIndexedColorSpace *)colorSpace;
5916 colorSpace2 = indexedCS->getBase();
5917 indexHigh = indexedCS->getIndexHigh();
5918 nComps2 = colorSpace2->getNComps();
5919 indexedLookup = indexedCS->getLookup();
5920 colorSpace2->getDefaultRanges(x, y, indexHigh);
5921 if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5922 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
5923 useByteLookup = gTrue;
5925 for (k = 0; k < nComps2; ++k) {
5926 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5927 sizeof(GfxColorComp));
5928 for (i = 0; i <= maxPixel; ++i) {
5929 j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
5930 if (j < 0) {
5931 j = 0;
5932 } else if (j > indexHigh) {
5933 j = indexHigh;
5936 mapped = x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k];
5937 lookup2[k][i] = dblToCol(mapped);
5938 if (useByteLookup)
5939 byte_lookup[i * nComps2 + k] = (Guchar) (mapped * 255);
5942 break;
5943 case csSeparation:
5944 sepCS = (GfxSeparationColorSpace *)colorSpace;
5945 colorSpace2 = sepCS->getAlt();
5946 nComps2 = colorSpace2->getNComps();
5947 sepFunc = sepCS->getFunc();
5948 if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5949 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
5950 useByteLookup = gTrue;
5952 for (k = 0; k < nComps2; ++k) {
5953 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5954 sizeof(GfxColorComp));
5955 for (i = 0; i <= maxPixel; ++i) {
5956 x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
5957 sepFunc->transform(x, y);
5958 lookup2[k][i] = dblToCol(y[k]);
5959 if (useByteLookup)
5960 byte_lookup[i*nComps2 + k] = (Guchar) (y[k] * 255);
5963 break;
5964 default:
5965 if (colorSpace->useGetGrayLine() || colorSpace->useGetRGBLine() || colorSpace->useGetCMYKLine() || colorSpace->useGetDeviceNLine()) {
5966 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps);
5967 useByteLookup = gTrue;
5969 for (k = 0; k < nComps; ++k) {
5970 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5971 sizeof(GfxColorComp));
5972 for (i = 0; i <= maxPixel; ++i) {
5973 mapped = decodeLow[k] + (i * decodeRange[k]) / maxPixel;
5974 lookup2[k][i] = dblToCol(mapped);
5975 if (useByteLookup) {
5976 int byte;
5978 byte = (int) (mapped * 255.0 + 0.5);
5979 if (byte < 0)
5980 byte = 0;
5981 else if (byte > 255)
5982 byte = 255;
5983 byte_lookup[i * nComps + k] = byte;
5989 return;
5991 err2:
5992 obj.free();
5993 err1:
5994 ok = gFalse;
5997 GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
5998 int n, i, k;
6000 colorSpace = colorMap->colorSpace->copy();
6001 bits = colorMap->bits;
6002 nComps = colorMap->nComps;
6003 nComps2 = colorMap->nComps2;
6004 useMatte = colorMap->useMatte;
6005 matteColor = colorMap->matteColor;
6006 colorSpace2 = NULL;
6007 for (k = 0; k < gfxColorMaxComps; ++k) {
6008 lookup[k] = NULL;
6010 n = 1 << bits;
6011 if (colorSpace->getMode() == csIndexed) {
6012 colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
6013 for (k = 0; k < nComps2; ++k) {
6014 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
6015 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
6017 } else if (colorSpace->getMode() == csSeparation) {
6018 colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
6019 for (k = 0; k < nComps2; ++k) {
6020 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
6021 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
6023 } else {
6024 for (k = 0; k < nComps; ++k) {
6025 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
6026 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
6029 if (colorMap->byte_lookup) {
6030 int nc = colorSpace2 ? nComps2 : nComps;
6032 byte_lookup = (Guchar *)gmallocn (n, nc);
6033 memcpy(byte_lookup, colorMap->byte_lookup, n * nc);
6035 for (i = 0; i < nComps; ++i) {
6036 decodeLow[i] = colorMap->decodeLow[i];
6037 decodeRange[i] = colorMap->decodeRange[i];
6039 ok = gTrue;
6042 GfxImageColorMap::~GfxImageColorMap() {
6043 int i;
6045 delete colorSpace;
6046 for (i = 0; i < gfxColorMaxComps; ++i) {
6047 gfree(lookup[i]);
6048 gfree(lookup2[i]);
6050 gfree(byte_lookup);
6053 void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
6054 GfxColor color;
6055 int i;
6057 if (colorSpace2) {
6058 for (i = 0; i < nComps2; ++i) {
6059 color.c[i] = lookup2[i][x[0]];
6061 colorSpace2->getGray(&color, gray);
6062 } else {
6063 for (i = 0; i < nComps; ++i) {
6064 color.c[i] = lookup2[i][x[i]];
6066 colorSpace->getGray(&color, gray);
6070 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
6071 GfxColor color;
6072 int i;
6074 if (colorSpace2) {
6075 for (i = 0; i < nComps2; ++i) {
6076 color.c[i] = lookup2[i][x[0]];
6078 colorSpace2->getRGB(&color, rgb);
6079 } else {
6080 for (i = 0; i < nComps; ++i) {
6081 color.c[i] = lookup2[i][x[i]];
6083 colorSpace->getRGB(&color, rgb);
6087 void GfxImageColorMap::getGrayLine(Guchar *in, Guchar *out, int length) {
6088 int i, j;
6089 Guchar *inp, *tmp_line;
6091 if ((colorSpace2 && !colorSpace2->useGetGrayLine ()) ||
6092 (!colorSpace2 && !colorSpace->useGetGrayLine ())) {
6093 GfxGray gray;
6095 inp = in;
6096 for (i = 0; i < length; i++) {
6097 getGray (inp, &gray);
6098 out[i] = colToByte(gray);
6099 inp += nComps;
6101 return;
6104 switch (colorSpace->getMode()) {
6105 case csIndexed:
6106 case csSeparation:
6107 tmp_line = (Guchar *) gmallocn (length, nComps2);
6108 for (i = 0; i < length; i++) {
6109 for (j = 0; j < nComps2; j++) {
6110 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6113 colorSpace2->getGrayLine(tmp_line, out, length);
6114 gfree (tmp_line);
6115 break;
6117 default:
6118 inp = in;
6119 for (j = 0; j < length; j++)
6120 for (i = 0; i < nComps; i++) {
6121 *inp = byte_lookup[*inp * nComps + i];
6122 inp++;
6124 colorSpace->getGrayLine(in, out, length);
6125 break;
6130 void GfxImageColorMap::getRGBLine(Guchar *in, unsigned int *out, int length) {
6131 int i, j;
6132 Guchar *inp, *tmp_line;
6134 if (!useRGBLine()) {
6135 GfxRGB rgb;
6137 inp = in;
6138 for (i = 0; i < length; i++) {
6139 getRGB (inp, &rgb);
6140 out[i] =
6141 ((int) colToByte(rgb.r) << 16) |
6142 ((int) colToByte(rgb.g) << 8) |
6143 ((int) colToByte(rgb.b) << 0);
6144 inp += nComps;
6146 return;
6149 switch (colorSpace->getMode()) {
6150 case csIndexed:
6151 case csSeparation:
6152 tmp_line = (Guchar *) gmallocn (length, nComps2);
6153 for (i = 0; i < length; i++) {
6154 for (j = 0; j < nComps2; j++) {
6155 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6158 colorSpace2->getRGBLine(tmp_line, out, length);
6159 gfree (tmp_line);
6160 break;
6162 default:
6163 inp = in;
6164 for (j = 0; j < length; j++)
6165 for (i = 0; i < nComps; i++) {
6166 *inp = byte_lookup[*inp * nComps + i];
6167 inp++;
6169 colorSpace->getRGBLine(in, out, length);
6170 break;
6175 void GfxImageColorMap::getRGBLine(Guchar *in, Guchar *out, int length) {
6176 int i, j;
6177 Guchar *inp, *tmp_line;
6179 if (!useRGBLine()) {
6180 GfxRGB rgb;
6182 inp = in;
6183 for (i = 0; i < length; i++) {
6184 getRGB (inp, &rgb);
6185 *out++ = colToByte(rgb.r);
6186 *out++ = colToByte(rgb.g);
6187 *out++ = colToByte(rgb.b);
6188 inp += nComps;
6190 return;
6193 switch (colorSpace->getMode()) {
6194 case csIndexed:
6195 case csSeparation:
6196 tmp_line = (Guchar *) gmallocn (length, nComps2);
6197 for (i = 0; i < length; i++) {
6198 for (j = 0; j < nComps2; j++) {
6199 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6202 colorSpace2->getRGBLine(tmp_line, out, length);
6203 gfree (tmp_line);
6204 break;
6206 default:
6207 inp = in;
6208 for (j = 0; j < length; j++)
6209 for (i = 0; i < nComps; i++) {
6210 *inp = byte_lookup[*inp * nComps + i];
6211 inp++;
6213 colorSpace->getRGBLine(in, out, length);
6214 break;
6219 void GfxImageColorMap::getRGBXLine(Guchar *in, Guchar *out, int length) {
6220 int i, j;
6221 Guchar *inp, *tmp_line;
6223 if (!useRGBLine()) {
6224 GfxRGB rgb;
6226 inp = in;
6227 for (i = 0; i < length; i++) {
6228 getRGB (inp, &rgb);
6229 *out++ = colToByte(rgb.r);
6230 *out++ = colToByte(rgb.g);
6231 *out++ = colToByte(rgb.b);
6232 *out++ = 255;
6233 inp += nComps;
6235 return;
6238 switch (colorSpace->getMode()) {
6239 case csIndexed:
6240 case csSeparation:
6241 tmp_line = (Guchar *) gmallocn (length, nComps2);
6242 for (i = 0; i < length; i++) {
6243 for (j = 0; j < nComps2; j++) {
6244 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6247 colorSpace2->getRGBXLine(tmp_line, out, length);
6248 gfree (tmp_line);
6249 break;
6251 default:
6252 inp = in;
6253 for (j = 0; j < length; j++)
6254 for (i = 0; i < nComps; i++) {
6255 *inp = byte_lookup[*inp * nComps + i];
6256 inp++;
6258 colorSpace->getRGBXLine(in, out, length);
6259 break;
6264 void GfxImageColorMap::getCMYKLine(Guchar *in, Guchar *out, int length) {
6265 int i, j;
6266 Guchar *inp, *tmp_line;
6268 if (!useCMYKLine()) {
6269 GfxCMYK cmyk;
6271 inp = in;
6272 for (i = 0; i < length; i++) {
6273 getCMYK (inp, &cmyk);
6274 *out++ = colToByte(cmyk.c);
6275 *out++ = colToByte(cmyk.m);
6276 *out++ = colToByte(cmyk.y);
6277 *out++ = colToByte(cmyk.k);
6278 inp += nComps;
6280 return;
6283 switch (colorSpace->getMode()) {
6284 case csIndexed:
6285 case csSeparation:
6286 tmp_line = (Guchar *) gmallocn (length, nComps2);
6287 for (i = 0; i < length; i++) {
6288 for (j = 0; j < nComps2; j++) {
6289 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6292 colorSpace2->getCMYKLine(tmp_line, out, length);
6293 gfree (tmp_line);
6294 break;
6296 default:
6297 inp = in;
6298 for (j = 0; j < length; j++)
6299 for (i = 0; i < nComps; i++) {
6300 *inp = byte_lookup[*inp * nComps + i];
6301 inp++;
6303 colorSpace->getCMYKLine(in, out, length);
6304 break;
6309 void GfxImageColorMap::getDeviceNLine(Guchar *in, Guchar *out, int length) {
6310 int i, j;
6311 Guchar *inp, *tmp_line;
6313 if (!useDeviceNLine()) {
6314 GfxColor deviceN;
6316 inp = in;
6317 for (i = 0; i < length; i++) {
6318 getDeviceN (inp, &deviceN);
6319 for (int j = 0; j < SPOT_NCOMPS+4; j++)
6320 *out++ = deviceN.c[j];
6321 inp += nComps;
6323 return;
6326 switch (colorSpace->getMode()) {
6327 case csIndexed:
6328 case csSeparation:
6329 tmp_line = (Guchar *) gmallocn (length, nComps2);
6330 for (i = 0; i < length; i++) {
6331 for (j = 0; j < nComps2; j++) {
6332 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6335 colorSpace2->getDeviceNLine(tmp_line, out, length);
6336 gfree (tmp_line);
6337 break;
6339 default:
6340 inp = in;
6341 for (j = 0; j < length; j++)
6342 for (i = 0; i < nComps; i++) {
6343 *inp = byte_lookup[*inp * nComps + i];
6344 inp++;
6346 colorSpace->getDeviceNLine(in, out, length);
6347 break;
6352 void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
6353 GfxColor color;
6354 int i;
6356 if (colorSpace2) {
6357 for (i = 0; i < nComps2; ++i) {
6358 color.c[i] = lookup2[i][x[0]];
6360 colorSpace2->getCMYK(&color, cmyk);
6361 } else {
6362 for (i = 0; i < nComps; ++i) {
6363 color.c[i] = lookup[i][x[i]];
6365 colorSpace->getCMYK(&color, cmyk);
6369 void GfxImageColorMap::getDeviceN(Guchar *x, GfxColor *deviceN) {
6370 GfxColor color;
6371 int i;
6373 if (colorSpace2) {
6374 for (i = 0; i < nComps2; ++i) {
6375 color.c[i] = lookup2[i][x[0]];
6377 colorSpace2->getDeviceN(&color, deviceN);
6378 } else {
6379 for (i = 0; i < nComps; ++i) {
6380 color.c[i] = lookup[i][x[i]];
6382 colorSpace->getDeviceN(&color, deviceN);
6386 void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
6387 int maxPixel, i;
6389 maxPixel = (1 << bits) - 1;
6390 for (i = 0; i < nComps; ++i) {
6391 color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
6395 //------------------------------------------------------------------------
6396 // GfxSubpath and GfxPath
6397 //------------------------------------------------------------------------
6399 GfxSubpath::GfxSubpath(double x1, double y1) {
6400 size = 16;
6401 x = (double *)gmallocn(size, sizeof(double));
6402 y = (double *)gmallocn(size, sizeof(double));
6403 curve = (GBool *)gmallocn(size, sizeof(GBool));
6404 n = 1;
6405 x[0] = x1;
6406 y[0] = y1;
6407 curve[0] = gFalse;
6408 closed = gFalse;
6411 GfxSubpath::~GfxSubpath() {
6412 gfree(x);
6413 gfree(y);
6414 gfree(curve);
6417 // Used for copy().
6418 GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
6419 size = subpath->size;
6420 n = subpath->n;
6421 x = (double *)gmallocn(size, sizeof(double));
6422 y = (double *)gmallocn(size, sizeof(double));
6423 curve = (GBool *)gmallocn(size, sizeof(GBool));
6424 memcpy(x, subpath->x, n * sizeof(double));
6425 memcpy(y, subpath->y, n * sizeof(double));
6426 memcpy(curve, subpath->curve, n * sizeof(GBool));
6427 closed = subpath->closed;
6430 void GfxSubpath::lineTo(double x1, double y1) {
6431 if (n >= size) {
6432 size *= 2;
6433 x = (double *)greallocn(x, size, sizeof(double));
6434 y = (double *)greallocn(y, size, sizeof(double));
6435 curve = (GBool *)greallocn(curve, size, sizeof(GBool));
6437 x[n] = x1;
6438 y[n] = y1;
6439 curve[n] = gFalse;
6440 ++n;
6443 void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
6444 double x3, double y3) {
6445 if (n+3 > size) {
6446 size *= 2;
6447 x = (double *)greallocn(x, size, sizeof(double));
6448 y = (double *)greallocn(y, size, sizeof(double));
6449 curve = (GBool *)greallocn(curve, size, sizeof(GBool));
6451 x[n] = x1;
6452 y[n] = y1;
6453 x[n+1] = x2;
6454 y[n+1] = y2;
6455 x[n+2] = x3;
6456 y[n+2] = y3;
6457 curve[n] = curve[n+1] = gTrue;
6458 curve[n+2] = gFalse;
6459 n += 3;
6462 void GfxSubpath::close() {
6463 if (x[n-1] != x[0] || y[n-1] != y[0]) {
6464 lineTo(x[0], y[0]);
6466 closed = gTrue;
6469 void GfxSubpath::offset(double dx, double dy) {
6470 int i;
6472 for (i = 0; i < n; ++i) {
6473 x[i] += dx;
6474 y[i] += dy;
6478 GfxPath::GfxPath() {
6479 justMoved = gFalse;
6480 size = 16;
6481 n = 0;
6482 firstX = firstY = 0;
6483 subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
6486 GfxPath::~GfxPath() {
6487 int i;
6489 for (i = 0; i < n; ++i)
6490 delete subpaths[i];
6491 gfree(subpaths);
6494 // Used for copy().
6495 GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
6496 GfxSubpath **subpaths1, int n1, int size1) {
6497 int i;
6499 justMoved = justMoved1;
6500 firstX = firstX1;
6501 firstY = firstY1;
6502 size = size1;
6503 n = n1;
6504 subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
6505 for (i = 0; i < n; ++i)
6506 subpaths[i] = subpaths1[i]->copy();
6509 void GfxPath::moveTo(double x, double y) {
6510 justMoved = gTrue;
6511 firstX = x;
6512 firstY = y;
6515 void GfxPath::lineTo(double x, double y) {
6516 if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) {
6517 if (n >= size) {
6518 size *= 2;
6519 subpaths = (GfxSubpath **)
6520 greallocn(subpaths, size, sizeof(GfxSubpath *));
6522 if (justMoved) {
6523 subpaths[n] = new GfxSubpath(firstX, firstY);
6524 } else {
6525 subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(),
6526 subpaths[n-1]->getLastY());
6528 ++n;
6529 justMoved = gFalse;
6531 subpaths[n-1]->lineTo(x, y);
6534 void GfxPath::curveTo(double x1, double y1, double x2, double y2,
6535 double x3, double y3) {
6536 if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) {
6537 if (n >= size) {
6538 size *= 2;
6539 subpaths = (GfxSubpath **)
6540 greallocn(subpaths, size, sizeof(GfxSubpath *));
6542 if (justMoved) {
6543 subpaths[n] = new GfxSubpath(firstX, firstY);
6544 } else {
6545 subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(),
6546 subpaths[n-1]->getLastY());
6548 ++n;
6549 justMoved = gFalse;
6551 subpaths[n-1]->curveTo(x1, y1, x2, y2, x3, y3);
6554 void GfxPath::close() {
6555 // this is necessary to handle the pathological case of
6556 // moveto/closepath/clip, which defines an empty clipping region
6557 if (justMoved) {
6558 if (n >= size) {
6559 size *= 2;
6560 subpaths = (GfxSubpath **)
6561 greallocn(subpaths, size, sizeof(GfxSubpath *));
6563 subpaths[n] = new GfxSubpath(firstX, firstY);
6564 ++n;
6565 justMoved = gFalse;
6567 subpaths[n-1]->close();
6570 void GfxPath::append(GfxPath *path) {
6571 int i;
6573 if (n + path->n > size) {
6574 size = n + path->n;
6575 subpaths = (GfxSubpath **)
6576 greallocn(subpaths, size, sizeof(GfxSubpath *));
6578 for (i = 0; i < path->n; ++i) {
6579 subpaths[n++] = path->subpaths[i]->copy();
6581 justMoved = gFalse;
6584 void GfxPath::offset(double dx, double dy) {
6585 int i;
6587 for (i = 0; i < n; ++i) {
6588 subpaths[i]->offset(dx, dy);
6592 //------------------------------------------------------------------------
6594 //------------------------------------------------------------------------
6595 GfxState::ReusablePathIterator::ReusablePathIterator(GfxPath *path)
6596 : path(path),
6597 subPathOff(0),
6598 coordOff(0),
6599 numCoords(0),
6600 curSubPath(NULL)
6602 if( path->getNumSubpaths() ) {
6603 curSubPath = path->getSubpath(subPathOff);
6604 numCoords = curSubPath->getNumPoints();
6608 bool GfxState::ReusablePathIterator::isEnd() const {
6609 return coordOff >= numCoords;
6612 void GfxState::ReusablePathIterator::next() {
6613 ++coordOff;
6614 if (coordOff == numCoords) {
6615 ++subPathOff;
6616 if (subPathOff < path->getNumSubpaths()) {
6617 coordOff = 0;
6618 curSubPath = path->getSubpath(subPathOff);
6619 numCoords = curSubPath->getNumPoints();
6624 void GfxState::ReusablePathIterator::setCoord(double x, double y) {
6625 curSubPath->setX(coordOff, x);
6626 curSubPath->setY(coordOff, y);
6629 void GfxState::ReusablePathIterator::reset() {
6630 coordOff = 0;
6631 subPathOff = 0;
6632 curSubPath = path->getSubpath(0);
6633 numCoords = curSubPath->getNumPoints();
6636 GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
6637 int rotateA, GBool upsideDown) {
6638 double kx, ky;
6640 hDPI = hDPIA;
6641 vDPI = vDPIA;
6642 rotate = rotateA;
6643 px1 = pageBox->x1;
6644 py1 = pageBox->y1;
6645 px2 = pageBox->x2;
6646 py2 = pageBox->y2;
6647 kx = hDPI / 72.0;
6648 ky = vDPI / 72.0;
6649 if (rotate == 90) {
6650 ctm[0] = 0;
6651 ctm[1] = upsideDown ? ky : -ky;
6652 ctm[2] = kx;
6653 ctm[3] = 0;
6654 ctm[4] = -kx * py1;
6655 ctm[5] = ky * (upsideDown ? -px1 : px2);
6656 pageWidth = kx * (py2 - py1);
6657 pageHeight = ky * (px2 - px1);
6658 } else if (rotate == 180) {
6659 ctm[0] = -kx;
6660 ctm[1] = 0;
6661 ctm[2] = 0;
6662 ctm[3] = upsideDown ? ky : -ky;
6663 ctm[4] = kx * px2;
6664 ctm[5] = ky * (upsideDown ? -py1 : py2);
6665 pageWidth = kx * (px2 - px1);
6666 pageHeight = ky * (py2 - py1);
6667 } else if (rotate == 270) {
6668 ctm[0] = 0;
6669 ctm[1] = upsideDown ? -ky : ky;
6670 ctm[2] = -kx;
6671 ctm[3] = 0;
6672 ctm[4] = kx * py2;
6673 ctm[5] = ky * (upsideDown ? px2 : -px1);
6674 pageWidth = kx * (py2 - py1);
6675 pageHeight = ky * (px2 - px1);
6676 } else {
6677 ctm[0] = kx;
6678 ctm[1] = 0;
6679 ctm[2] = 0;
6680 ctm[3] = upsideDown ? -ky : ky;
6681 ctm[4] = -kx * px1;
6682 ctm[5] = ky * (upsideDown ? py2 : -py1);
6683 pageWidth = kx * (px2 - px1);
6684 pageHeight = ky * (py2 - py1);
6687 fillColorSpace = new GfxDeviceGrayColorSpace();
6688 strokeColorSpace = new GfxDeviceGrayColorSpace();
6689 fillColor.c[0] = 0;
6690 strokeColor.c[0] = 0;
6691 fillPattern = NULL;
6692 strokePattern = NULL;
6693 blendMode = gfxBlendNormal;
6694 fillOpacity = 1;
6695 strokeOpacity = 1;
6696 fillOverprint = gFalse;
6697 strokeOverprint = gFalse;
6698 overprintMode = 0;
6699 transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL;
6701 lineWidth = 1;
6702 lineDash = NULL;
6703 lineDashLength = 0;
6704 lineDashStart = 0;
6705 flatness = 1;
6706 lineJoin = 0;
6707 lineCap = 0;
6708 miterLimit = 10;
6709 strokeAdjust = gFalse;
6710 alphaIsShape = gFalse;
6711 textKnockout = gFalse;
6713 font = NULL;
6714 fontSize = 0;
6715 textMat[0] = 1; textMat[1] = 0;
6716 textMat[2] = 0; textMat[3] = 1;
6717 textMat[4] = 0; textMat[5] = 0;
6718 charSpace = 0;
6719 wordSpace = 0;
6720 horizScaling = 1;
6721 leading = 0;
6722 rise = 0;
6723 render = 0;
6725 path = new GfxPath();
6726 curX = curY = 0;
6727 lineX = lineY = 0;
6729 clipXMin = 0;
6730 clipYMin = 0;
6731 clipXMax = pageWidth;
6732 clipYMax = pageHeight;
6734 renderingIntent[0] = 0;
6736 saved = NULL;
6737 #ifdef USE_CMS
6738 GfxColorSpace::setupColorProfiles();
6739 XYZ2DisplayTransformRelCol = NULL;
6740 XYZ2DisplayTransformAbsCol = NULL;
6741 XYZ2DisplayTransformSat = NULL;
6742 XYZ2DisplayTransformPerc = NULL;
6743 localDisplayProfile = NULL;
6744 displayProfileRef = 0;
6745 #endif
6748 GfxState::~GfxState() {
6749 int i;
6751 if (fillColorSpace) {
6752 delete fillColorSpace;
6754 if (strokeColorSpace) {
6755 delete strokeColorSpace;
6757 if (fillPattern) {
6758 delete fillPattern;
6760 if (strokePattern) {
6761 delete strokePattern;
6763 for (i = 0; i < 4; ++i) {
6764 if (transfer[i]) {
6765 delete transfer[i];
6768 gfree(lineDash);
6769 if (path) {
6770 // this gets set to NULL by restore()
6771 delete path;
6773 if (font) {
6774 font->decRefCnt();
6776 #ifdef USE_CMS
6777 if (XYZ2DisplayTransformRelCol) {
6778 if (XYZ2DisplayTransformRelCol->unref() == 0)
6779 delete XYZ2DisplayTransformRelCol;
6781 if (XYZ2DisplayTransformAbsCol) {
6782 if (XYZ2DisplayTransformAbsCol->unref() == 0)
6783 delete XYZ2DisplayTransformAbsCol;
6785 if (XYZ2DisplayTransformSat) {
6786 if (XYZ2DisplayTransformSat->unref() == 0)
6787 delete XYZ2DisplayTransformSat;
6789 if (XYZ2DisplayTransformPerc) {
6790 if (XYZ2DisplayTransformPerc->unref() == 0)
6791 delete XYZ2DisplayTransformPerc;
6793 if (--displayProfileRef == 0 && localDisplayProfile != NULL) {
6794 cmsCloseProfile(localDisplayProfile);
6796 #endif
6799 // Used for copy();
6800 GfxState::GfxState(GfxState *state, GBool copyPath) {
6801 int i;
6803 memcpy(this, state, sizeof(GfxState));
6804 if (fillColorSpace) {
6805 fillColorSpace = state->fillColorSpace->copy();
6807 if (strokeColorSpace) {
6808 strokeColorSpace = state->strokeColorSpace->copy();
6810 if (fillPattern) {
6811 fillPattern = state->fillPattern->copy();
6813 if (strokePattern) {
6814 strokePattern = state->strokePattern->copy();
6816 for (i = 0; i < 4; ++i) {
6817 if (transfer[i]) {
6818 transfer[i] = state->transfer[i]->copy();
6821 if (lineDashLength > 0) {
6822 lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
6823 memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
6825 if (font)
6826 font->incRefCnt();
6828 if (copyPath) {
6829 path = state->path->copy();
6831 saved = NULL;
6832 #ifdef USE_CMS
6833 if (XYZ2DisplayTransformRelCol) {
6834 XYZ2DisplayTransformRelCol->ref();
6836 if (XYZ2DisplayTransformAbsCol) {
6837 XYZ2DisplayTransformAbsCol->ref();
6839 if (XYZ2DisplayTransformSat) {
6840 XYZ2DisplayTransformSat->ref();
6842 if (XYZ2DisplayTransformPerc) {
6843 XYZ2DisplayTransformPerc->ref();
6845 if (localDisplayProfile) {
6846 displayProfileRef++;
6848 #endif
6851 #ifdef USE_CMS
6852 void GfxState::setDisplayProfile(cmsHPROFILE localDisplayProfileA) {
6853 if (localDisplayProfile != NULL) {
6854 cmsCloseProfile(localDisplayProfile);
6856 localDisplayProfile = localDisplayProfileA;
6857 if (localDisplayProfileA != NULL) {
6858 cmsHTRANSFORM transform;
6859 unsigned int nChannels;
6860 unsigned int localDisplayPixelType;
6862 localDisplayPixelType = getCMSColorSpaceType(cmsGetColorSpace(localDisplayProfile));
6863 nChannels = getCMSNChannels(cmsGetColorSpace(localDisplayProfile));
6864 displayProfileRef = 1;
6865 // create transform from XYZ
6866 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
6867 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6868 localDisplayProfile,
6869 COLORSPACE_SH(localDisplayPixelType) |
6870 CHANNELS_SH(nChannels) | BYTES_SH(1),
6871 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
6872 error(errSyntaxWarning, -1, "Can't create Lab transform");
6873 } else {
6874 XYZ2DisplayTransformRelCol = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6876 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6877 localDisplayProfile,
6878 COLORSPACE_SH(localDisplayPixelType) |
6879 CHANNELS_SH(nChannels) | BYTES_SH(1),
6880 INTENT_ABSOLUTE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
6881 error(errSyntaxWarning, -1, "Can't create Lab transform");
6882 } else {
6883 XYZ2DisplayTransformAbsCol = new GfxColorTransform(transform, INTENT_ABSOLUTE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6885 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6886 localDisplayProfile,
6887 COLORSPACE_SH(localDisplayPixelType) |
6888 CHANNELS_SH(nChannels) | BYTES_SH(1),
6889 INTENT_SATURATION,LCMS_FLAGS)) == 0) {
6890 error(errSyntaxWarning, -1, "Can't create Lab transform");
6891 } else {
6892 XYZ2DisplayTransformSat = new GfxColorTransform(transform, INTENT_SATURATION, PT_XYZ, localDisplayPixelType);
6894 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6895 localDisplayProfile,
6896 COLORSPACE_SH(localDisplayPixelType) |
6897 CHANNELS_SH(nChannels) | BYTES_SH(1),
6898 INTENT_PERCEPTUAL,LCMS_FLAGS)) == 0) {
6899 error(errSyntaxWarning, -1, "Can't create Lab transform");
6900 } else {
6901 XYZ2DisplayTransformPerc = new GfxColorTransform(transform, INTENT_PERCEPTUAL, PT_XYZ, localDisplayPixelType);
6903 cmsCloseProfile(XYZProfile);
6907 GfxColorTransform *GfxState::getXYZ2DisplayTransform() {
6908 GfxColorTransform *transform;
6910 transform = XYZ2DisplayTransformRelCol;
6911 if (strcmp(renderingIntent, "AbsoluteColorimetric") == 0) {
6912 transform = XYZ2DisplayTransformAbsCol;
6913 } else if (strcmp(renderingIntent, "Saturation") == 0) {
6914 transform = XYZ2DisplayTransformSat;
6915 } else if (strcmp(renderingIntent, "Perceptual") == 0) {
6916 transform = XYZ2DisplayTransformPerc;
6918 if (transform == NULL) {
6919 transform = XYZ2DisplayTransform;
6921 return transform;
6924 #endif
6926 void GfxState::setPath(GfxPath *pathA) {
6927 delete path;
6928 path = pathA;
6931 void GfxState::getUserClipBBox(double *xMin, double *yMin,
6932 double *xMax, double *yMax) {
6933 double ictm[6];
6934 double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
6936 // invert the CTM
6937 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
6938 ictm[0] = ctm[3] * det;
6939 ictm[1] = -ctm[1] * det;
6940 ictm[2] = -ctm[2] * det;
6941 ictm[3] = ctm[0] * det;
6942 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
6943 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
6945 // transform all four corners of the clip bbox; find the min and max
6946 // x and y values
6947 xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
6948 yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
6949 tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
6950 ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
6951 if (tx < xMin1) {
6952 xMin1 = tx;
6953 } else if (tx > xMax1) {
6954 xMax1 = tx;
6956 if (ty < yMin1) {
6957 yMin1 = ty;
6958 } else if (ty > yMax1) {
6959 yMax1 = ty;
6961 tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
6962 ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
6963 if (tx < xMin1) {
6964 xMin1 = tx;
6965 } else if (tx > xMax1) {
6966 xMax1 = tx;
6968 if (ty < yMin1) {
6969 yMin1 = ty;
6970 } else if (ty > yMax1) {
6971 yMax1 = ty;
6973 tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
6974 ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
6975 if (tx < xMin1) {
6976 xMin1 = tx;
6977 } else if (tx > xMax1) {
6978 xMax1 = tx;
6980 if (ty < yMin1) {
6981 yMin1 = ty;
6982 } else if (ty > yMax1) {
6983 yMax1 = ty;
6986 *xMin = xMin1;
6987 *yMin = yMin1;
6988 *xMax = xMax1;
6989 *yMax = yMax1;
6992 double GfxState::transformWidth(double w) {
6993 double x, y;
6995 x = ctm[0] + ctm[2];
6996 y = ctm[1] + ctm[3];
6997 return w * sqrt(0.5 * (x * x + y * y));
7000 double GfxState::getTransformedFontSize() {
7001 double x1, y1, x2, y2;
7003 x1 = textMat[2] * fontSize;
7004 y1 = textMat[3] * fontSize;
7005 x2 = ctm[0] * x1 + ctm[2] * y1;
7006 y2 = ctm[1] * x1 + ctm[3] * y1;
7007 return sqrt(x2 * x2 + y2 * y2);
7010 void GfxState::getFontTransMat(double *m11, double *m12,
7011 double *m21, double *m22) {
7012 *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
7013 *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
7014 *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
7015 *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
7018 void GfxState::setCTM(double a, double b, double c,
7019 double d, double e, double f) {
7020 ctm[0] = a;
7021 ctm[1] = b;
7022 ctm[2] = c;
7023 ctm[3] = d;
7024 ctm[4] = e;
7025 ctm[5] = f;
7028 void GfxState::concatCTM(double a, double b, double c,
7029 double d, double e, double f) {
7030 double a1 = ctm[0];
7031 double b1 = ctm[1];
7032 double c1 = ctm[2];
7033 double d1 = ctm[3];
7035 ctm[0] = a * a1 + b * c1;
7036 ctm[1] = a * b1 + b * d1;
7037 ctm[2] = c * a1 + d * c1;
7038 ctm[3] = c * b1 + d * d1;
7039 ctm[4] = e * a1 + f * c1 + ctm[4];
7040 ctm[5] = e * b1 + f * d1 + ctm[5];
7043 void GfxState::shiftCTMAndClip(double tx, double ty) {
7044 ctm[4] += tx;
7045 ctm[5] += ty;
7046 clipXMin += tx;
7047 clipYMin += ty;
7048 clipXMax += tx;
7049 clipYMax += ty;
7052 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
7053 if (fillColorSpace) {
7054 delete fillColorSpace;
7056 fillColorSpace = colorSpace;
7059 void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
7060 if (strokeColorSpace) {
7061 delete strokeColorSpace;
7063 strokeColorSpace = colorSpace;
7066 void GfxState::setFillPattern(GfxPattern *pattern) {
7067 if (fillPattern) {
7068 delete fillPattern;
7070 fillPattern = pattern;
7073 void GfxState::setStrokePattern(GfxPattern *pattern) {
7074 if (strokePattern) {
7075 delete strokePattern;
7077 strokePattern = pattern;
7080 void GfxState::setFont(GfxFont *fontA, double fontSizeA) {
7081 if (font)
7082 font->decRefCnt();
7084 font = fontA;
7085 fontSize = fontSizeA;
7088 void GfxState::setTransfer(Function **funcs) {
7089 int i;
7091 for (i = 0; i < 4; ++i) {
7092 if (transfer[i]) {
7093 delete transfer[i];
7095 transfer[i] = funcs[i];
7099 void GfxState::setLineDash(double *dash, int length, double start) {
7100 if (lineDash)
7101 gfree(lineDash);
7102 lineDash = dash;
7103 lineDashLength = length;
7104 lineDashStart = start;
7107 void GfxState::clearPath() {
7108 delete path;
7109 path = new GfxPath();
7112 void GfxState::clip() {
7113 double xMin, yMin, xMax, yMax, x, y;
7114 GfxSubpath *subpath;
7115 int i, j;
7117 xMin = xMax = yMin = yMax = 0; // make gcc happy
7118 for (i = 0; i < path->getNumSubpaths(); ++i) {
7119 subpath = path->getSubpath(i);
7120 for (j = 0; j < subpath->getNumPoints(); ++j) {
7121 transform(subpath->getX(j), subpath->getY(j), &x, &y);
7122 if (i == 0 && j == 0) {
7123 xMin = xMax = x;
7124 yMin = yMax = y;
7125 } else {
7126 if (x < xMin) {
7127 xMin = x;
7128 } else if (x > xMax) {
7129 xMax = x;
7131 if (y < yMin) {
7132 yMin = y;
7133 } else if (y > yMax) {
7134 yMax = y;
7139 if (xMin > clipXMin) {
7140 clipXMin = xMin;
7142 if (yMin > clipYMin) {
7143 clipYMin = yMin;
7145 if (xMax < clipXMax) {
7146 clipXMax = xMax;
7148 if (yMax < clipYMax) {
7149 clipYMax = yMax;
7153 void GfxState::clipToStrokePath() {
7154 double xMin, yMin, xMax, yMax, x, y, t0, t1;
7155 GfxSubpath *subpath;
7156 int i, j;
7158 xMin = xMax = yMin = yMax = 0; // make gcc happy
7159 for (i = 0; i < path->getNumSubpaths(); ++i) {
7160 subpath = path->getSubpath(i);
7161 for (j = 0; j < subpath->getNumPoints(); ++j) {
7162 transform(subpath->getX(j), subpath->getY(j), &x, &y);
7163 if (i == 0 && j == 0) {
7164 xMin = xMax = x;
7165 yMin = yMax = y;
7166 } else {
7167 if (x < xMin) {
7168 xMin = x;
7169 } else if (x > xMax) {
7170 xMax = x;
7172 if (y < yMin) {
7173 yMin = y;
7174 } else if (y > yMax) {
7175 yMax = y;
7181 // allow for the line width
7182 //~ miter joins can extend farther than this
7183 t0 = fabs(ctm[0]);
7184 t1 = fabs(ctm[2]);
7185 if (t0 > t1) {
7186 xMin -= 0.5 * lineWidth * t0;
7187 xMax += 0.5 * lineWidth * t0;
7188 } else {
7189 xMin -= 0.5 * lineWidth * t1;
7190 xMax += 0.5 * lineWidth * t1;
7192 t0 = fabs(ctm[0]);
7193 t1 = fabs(ctm[3]);
7194 if (t0 > t1) {
7195 yMin -= 0.5 * lineWidth * t0;
7196 yMax += 0.5 * lineWidth * t0;
7197 } else {
7198 yMin -= 0.5 * lineWidth * t1;
7199 yMax += 0.5 * lineWidth * t1;
7202 if (xMin > clipXMin) {
7203 clipXMin = xMin;
7205 if (yMin > clipYMin) {
7206 clipYMin = yMin;
7208 if (xMax < clipXMax) {
7209 clipXMax = xMax;
7211 if (yMax < clipYMax) {
7212 clipYMax = yMax;
7216 void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) {
7217 double x, y, xMin1, yMin1, xMax1, yMax1;
7219 transform(xMin, yMin, &x, &y);
7220 xMin1 = xMax1 = x;
7221 yMin1 = yMax1 = y;
7222 transform(xMax, yMin, &x, &y);
7223 if (x < xMin1) {
7224 xMin1 = x;
7225 } else if (x > xMax1) {
7226 xMax1 = x;
7228 if (y < yMin1) {
7229 yMin1 = y;
7230 } else if (y > yMax1) {
7231 yMax1 = y;
7233 transform(xMax, yMax, &x, &y);
7234 if (x < xMin1) {
7235 xMin1 = x;
7236 } else if (x > xMax1) {
7237 xMax1 = x;
7239 if (y < yMin1) {
7240 yMin1 = y;
7241 } else if (y > yMax1) {
7242 yMax1 = y;
7244 transform(xMin, yMax, &x, &y);
7245 if (x < xMin1) {
7246 xMin1 = x;
7247 } else if (x > xMax1) {
7248 xMax1 = x;
7250 if (y < yMin1) {
7251 yMin1 = y;
7252 } else if (y > yMax1) {
7253 yMax1 = y;
7256 if (xMin1 > clipXMin) {
7257 clipXMin = xMin1;
7259 if (yMin1 > clipYMin) {
7260 clipYMin = yMin1;
7262 if (xMax1 < clipXMax) {
7263 clipXMax = xMax1;
7265 if (yMax1 < clipYMax) {
7266 clipYMax = yMax1;
7270 void GfxState::textShift(double tx, double ty) {
7271 double dx, dy;
7273 textTransformDelta(tx, ty, &dx, &dy);
7274 curX += dx;
7275 curY += dy;
7278 void GfxState::shift(double dx, double dy) {
7279 curX += dx;
7280 curY += dy;
7283 GfxState *GfxState::save() {
7284 GfxState *newState;
7286 newState = copy();
7287 newState->saved = this;
7288 return newState;
7291 GfxState *GfxState::restore() {
7292 GfxState *oldState;
7294 if (saved) {
7295 oldState = saved;
7297 // these attributes aren't saved/restored by the q/Q operators
7298 oldState->path = path;
7299 oldState->curX = curX;
7300 oldState->curY = curY;
7301 oldState->lineX = lineX;
7302 oldState->lineY = lineY;
7304 path = NULL;
7305 saved = NULL;
7306 delete this;
7308 } else {
7309 oldState = this;
7312 return oldState;
7315 GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
7316 Object obj2;
7317 int i, j;
7319 if (obj->isName()) {
7320 for (i = 0; i < nGfxBlendModeNames; ++i) {
7321 if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
7322 *mode = gfxBlendModeNames[i].mode;
7323 return gTrue;
7326 return gFalse;
7327 } else if (obj->isArray()) {
7328 for (i = 0; i < obj->arrayGetLength(); ++i) {
7329 obj->arrayGet(i, &obj2);
7330 if (!obj2.isName()) {
7331 obj2.free();
7332 return gFalse;
7334 for (j = 0; j < nGfxBlendModeNames; ++j) {
7335 if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
7336 obj2.free();
7337 *mode = gfxBlendModeNames[j].mode;
7338 return gTrue;
7341 obj2.free();
7343 *mode = gfxBlendNormal;
7344 return gTrue;
7345 } else {
7346 return gFalse;