beta-0.89.2
[luatex.git] / source / libs / poppler / poppler-src / poppler / GfxState.cc
blobbd3b42d51530483d93d3a08eb7655bc41724011d
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-2015 Albert Astals Cid <aacid@kde.org>
20 // Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp>
21 // Copyright (C) 2009, 2011-2015 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>
32 // To see a description of the changes please see the Changelog file that
33 // came with your tarball or type make ChangeLog if you are building from git
35 //========================================================================
37 #include <config.h>
39 #ifdef USE_GCC_PRAGMAS
40 #pragma implementation
41 #endif
43 #include <algorithm>
44 #include <stddef.h>
45 #include <math.h>
46 #include <string.h>
47 #include "goo/gmem.h"
48 #include "Error.h"
49 #include "Object.h"
50 #include "Array.h"
51 #include "Page.h"
52 #include "Gfx.h"
53 #include "GfxState.h"
54 #include "GfxState_helpers.h"
55 #include "GfxFont.h"
56 #include "GlobalParams.h"
57 #include "PopplerCache.h"
58 #include "OutputDev.h"
59 #include "splash/SplashTypes.h"
61 //------------------------------------------------------------------------
63 // Max depth of nested color spaces. This is used to catch infinite
64 // loops in the color space object structure.
65 #define colorSpaceRecursionLimit 8
67 //------------------------------------------------------------------------
69 GBool Matrix::invertTo(Matrix *other) const
71 double det;
73 det = 1 / determinant();
74 other->m[0] = m[3] * det;
75 other->m[1] = -m[1] * det;
76 other->m[2] = -m[2] * det;
77 other->m[3] = m[0] * det;
78 other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det;
79 other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det;
81 return gTrue;
84 void Matrix::translate(double tx, double ty)
86 double x0 = tx*m[0] + ty*m[2] + m[4];
87 double y0 = tx*m[1] + ty*m[3] + m[5];
88 m[4] = x0;
89 m[5] = y0;
92 void Matrix::scale(double sx, double sy)
94 m[0] *= sx;
95 m[1] *= sx;
96 m[2] *= sy;
97 m[3] *= sy;
100 void Matrix::transform(double x, double y, double *tx, double *ty) const
102 double temp_x, temp_y;
104 temp_x = m[0] * x + m[2] * y + m[4];
105 temp_y = m[1] * x + m[3] * y + m[5];
107 *tx = temp_x;
108 *ty = temp_y;
111 // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
112 double Matrix::norm() const
114 double f, g, h, i, j;
116 i = m[0]*m[0] + m[1]*m[1];
117 j = m[2]*m[2] + m[3]*m[3];
119 f = 0.5 * (i + j);
120 g = 0.5 * (i - j);
121 h = m[0]*m[2] + m[1]*m[3];
123 return sqrt (f + hypot (g, h));
126 //------------------------------------------------------------------------
128 struct GfxBlendModeInfo {
129 const char *name;
130 GfxBlendMode mode;
133 static const GfxBlendModeInfo gfxBlendModeNames[] = {
134 { "Normal", gfxBlendNormal },
135 { "Compatible", gfxBlendNormal },
136 { "Multiply", gfxBlendMultiply },
137 { "Screen", gfxBlendScreen },
138 { "Overlay", gfxBlendOverlay },
139 { "Darken", gfxBlendDarken },
140 { "Lighten", gfxBlendLighten },
141 { "ColorDodge", gfxBlendColorDodge },
142 { "ColorBurn", gfxBlendColorBurn },
143 { "HardLight", gfxBlendHardLight },
144 { "SoftLight", gfxBlendSoftLight },
145 { "Difference", gfxBlendDifference },
146 { "Exclusion", gfxBlendExclusion },
147 { "Hue", gfxBlendHue },
148 { "Saturation", gfxBlendSaturation },
149 { "Color", gfxBlendColor },
150 { "Luminosity", gfxBlendLuminosity }
153 #define nGfxBlendModeNames \
154 ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
156 //------------------------------------------------------------------------
158 // NB: This must match the GfxColorSpaceMode enum defined in
159 // GfxState.h
160 static const char *gfxColorSpaceModeNames[] = {
161 "DeviceGray",
162 "CalGray",
163 "DeviceRGB",
164 "CalRGB",
165 "DeviceCMYK",
166 "Lab",
167 "ICCBased",
168 "Indexed",
169 "Separation",
170 "DeviceN",
171 "Pattern"
174 #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
176 #ifdef USE_CMS
178 static const std::map<unsigned int, unsigned int>::size_type CMSCACHE_LIMIT = 2048;
180 #ifdef USE_LCMS1
181 #include <lcms.h>
182 #define cmsColorSpaceSignature icColorSpaceSignature
183 #define cmsSetLogErrorHandler cmsSetErrorHandler
184 #define cmsSigXYZData icSigXYZData
185 #define cmsSigLuvData icSigLuvData
186 #define cmsSigLabData icSigLabData
187 #define cmsSigYCbCrData icSigYCbCrData
188 #define cmsSigYxyData icSigYxyData
189 #define cmsSigRgbData icSigRgbData
190 #define cmsSigHsvData icSigHsvData
191 #define cmsSigHlsData icSigHlsData
192 #define cmsSigCmyData icSigCmyData
193 #define cmsSig3colorData icSig3colorData
194 #define cmsSigGrayData icSigGrayData
195 #define cmsSigCmykData icSigCmykData
196 #define cmsSig4colorData icSig4colorData
197 #define cmsSig2colorData icSig2colorData
198 #define cmsSig5colorData icSig5colorData
199 #define cmsSig6colorData icSig6colorData
200 #define cmsSig7colorData icSig7colorData
201 #define cmsSig8colorData icSig8colorData
202 #define cmsSig9colorData icSig9colorData
203 #define cmsSig10colorData icSig10colorData
204 #define cmsSig11colorData icSig11colorData
205 #define cmsSig12colorData icSig12colorData
206 #define cmsSig13colorData icSig13colorData
207 #define cmsSig14colorData icSig14colorData
208 #define cmsSig15colorData icSig15colorData
209 #define LCMS_FLAGS 0
210 #else
211 #include <lcms2.h>
212 #define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION
213 #endif
215 #define COLOR_PROFILE_DIR "/ColorProfiles/"
216 #define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR
218 void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) {
219 cmsDoTransform(transform, in, out, size);
222 // transformA should be a cmsHTRANSFORM
223 GfxColorTransform::GfxColorTransform(void *transformA, int cmsIntentA, unsigned int inputPixelTypeA, unsigned int transformPixelTypeA) {
224 transform = transformA;
225 refCount = 1;
226 cmsIntent = cmsIntentA;
227 inputPixelType = inputPixelTypeA;
228 transformPixelType = transformPixelTypeA;
231 GfxColorTransform::~GfxColorTransform() {
232 cmsDeleteTransform(transform);
235 void GfxColorTransform::ref() {
236 refCount++;
239 unsigned int GfxColorTransform::unref() {
240 return --refCount;
243 static cmsHPROFILE RGBProfile = NULL;
244 static GooString *displayProfileName = NULL; // display profile file Name
245 static cmsHPROFILE displayProfile = NULL; // display profile
246 static unsigned int displayPixelType = 0;
247 static GfxColorTransform *XYZ2DisplayTransform = NULL;
249 // convert color space signature to cmsColor type
250 static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs);
251 static unsigned int getCMSNChannels(cmsColorSpaceSignature cs);
252 static cmsHPROFILE loadColorProfile(const char *fileName);
254 void GfxColorSpace::setDisplayProfile(void *displayProfileA) {
255 displayProfile = displayProfileA;
256 if (displayProfile != NULL) {
257 cmsHTRANSFORM transform;
258 unsigned int nChannels;
260 displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
261 nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
262 // create transform from XYZ
263 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
264 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
265 displayProfile,
266 COLORSPACE_SH(displayPixelType) |
267 CHANNELS_SH(nChannels) | BYTES_SH(1),
268 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
269 error(errSyntaxWarning, -1, "Can't create Lab transform");
270 } else {
271 XYZ2DisplayTransform = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, displayPixelType);
273 cmsCloseProfile(XYZProfile);
277 void GfxColorSpace::setDisplayProfileName(GooString *name) {
278 displayProfileName = name->copy();
281 cmsHPROFILE GfxColorSpace::getRGBProfile() {
282 return RGBProfile;
285 cmsHPROFILE GfxColorSpace::getDisplayProfile() {
286 return displayProfile;
289 #endif
291 //------------------------------------------------------------------------
292 // GfxColorSpace
293 //------------------------------------------------------------------------
295 GfxColorSpace::GfxColorSpace() {
296 overprintMask = 0x0f;
297 mapping = NULL;
300 GfxColorSpace::~GfxColorSpace() {
303 GfxColorSpace *GfxColorSpace::parse(GfxResources *res, Object *csObj, OutputDev *out, GfxState *state, int recursion) {
304 GfxColorSpace *cs;
305 Object obj1;
307 if (recursion > colorSpaceRecursionLimit) {
308 error(errSyntaxError, -1, "Loop detected in color space objects");
309 return NULL;
312 cs = NULL;
313 if (csObj->isName()) {
314 if (csObj->isName("DeviceGray") || csObj->isName("G")) {
315 if (res != NULL) {
316 Object objCS;
317 res->lookupColorSpace("DefaultGray", &objCS);
318 if (objCS.isNull()) {
319 cs = new GfxDeviceGrayColorSpace();
320 } else {
321 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
323 objCS.free();
324 } else {
325 cs = new GfxDeviceGrayColorSpace();
327 } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) {
328 if (res != NULL) {
329 Object objCS;
330 res->lookupColorSpace("DefaultRGB", &objCS);
331 if (objCS.isNull()) {
332 cs = new GfxDeviceRGBColorSpace();
333 } else {
334 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
336 objCS.free();
337 } else {
338 cs = new GfxDeviceRGBColorSpace();
340 } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) {
341 if (res != NULL) {
342 Object objCS;
343 res->lookupColorSpace("DefaultCMYK", &objCS);
344 if (objCS.isNull()) {
345 cs = new GfxDeviceCMYKColorSpace();
346 } else {
347 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
349 objCS.free();
350 } else {
351 cs = new GfxDeviceCMYKColorSpace();
353 } else if (csObj->isName("Pattern")) {
354 cs = new GfxPatternColorSpace(NULL);
355 } else {
356 error(errSyntaxWarning, -1, "Bad color space '{0:s}'", csObj->getName());
358 } else if (csObj->isArray() && csObj->arrayGetLength() > 0) {
359 csObj->arrayGet(0, &obj1);
360 if (obj1.isName("DeviceGray") || obj1.isName("G")) {
361 if (res != NULL) {
362 Object objCS;
363 res->lookupColorSpace("DefaultGray", &objCS);
364 if (objCS.isNull()) {
365 cs = new GfxDeviceGrayColorSpace();
366 } else {
367 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
369 objCS.free();
370 } else {
371 cs = new GfxDeviceGrayColorSpace();
373 } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) {
374 if (res != NULL) {
375 Object objCS;
376 res->lookupColorSpace("DefaultRGB", &objCS);
377 if (objCS.isNull()) {
378 cs = new GfxDeviceRGBColorSpace();
379 } else {
380 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
382 objCS.free();
383 } else {
384 cs = new GfxDeviceRGBColorSpace();
386 } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) {
387 if (res != NULL) {
388 Object objCS;
389 res->lookupColorSpace("DefaultCMYK", &objCS);
390 if (objCS.isNull()) {
391 cs = new GfxDeviceCMYKColorSpace();
392 } else {
393 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
395 objCS.free();
396 } else {
397 cs = new GfxDeviceCMYKColorSpace();
399 } else if (obj1.isName("CalGray")) {
400 cs = GfxCalGrayColorSpace::parse(csObj->getArray(), state);
401 } else if (obj1.isName("CalRGB")) {
402 cs = GfxCalRGBColorSpace::parse(csObj->getArray(), state);
403 } else if (obj1.isName("Lab")) {
404 cs = GfxLabColorSpace::parse(csObj->getArray(), state);
405 } else if (obj1.isName("ICCBased")) {
406 cs = GfxICCBasedColorSpace::parse(csObj->getArray(), out, state, recursion);
407 } else if (obj1.isName("Indexed") || obj1.isName("I")) {
408 cs = GfxIndexedColorSpace::parse(res, csObj->getArray(), out, state, recursion);
409 } else if (obj1.isName("Separation")) {
410 cs = GfxSeparationColorSpace::parse(res, csObj->getArray(), out, state, recursion);
411 } else if (obj1.isName("DeviceN")) {
412 cs = GfxDeviceNColorSpace::parse(res, csObj->getArray(), out, state, recursion);
413 } else if (obj1.isName("Pattern")) {
414 cs = GfxPatternColorSpace::parse(res, csObj->getArray(), out, state, recursion);
415 } else {
416 error(errSyntaxWarning, -1, "Bad color space");
418 obj1.free();
419 } else if (csObj->isDict()) {
420 csObj->dictLookup("ColorSpace", &obj1);
421 if (obj1.isName("DeviceGray")) {
422 if (res != NULL) {
423 Object objCS;
424 res->lookupColorSpace("DefaultGray", &objCS);
425 if (objCS.isNull()) {
426 cs = new GfxDeviceGrayColorSpace();
427 } else {
428 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
430 objCS.free();
431 } else {
432 cs = new GfxDeviceGrayColorSpace();
434 } else if (obj1.isName("DeviceRGB")) {
435 if (res != NULL) {
436 Object objCS;
437 res->lookupColorSpace("DefaultRGB", &objCS);
438 if (objCS.isNull()) {
439 cs = new GfxDeviceRGBColorSpace();
440 } else {
441 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
443 objCS.free();
444 } else {
445 cs = new GfxDeviceRGBColorSpace();
447 } else if (obj1.isName("DeviceCMYK")) {
448 if (res != NULL) {
449 Object objCS;
450 res->lookupColorSpace("DefaultCMYK", &objCS);
451 if (objCS.isNull()) {
452 cs = new GfxDeviceCMYKColorSpace();
453 } else {
454 cs = GfxColorSpace::parse(NULL, &objCS, out, state);
456 objCS.free();
457 } else {
458 cs = new GfxDeviceCMYKColorSpace();
460 } else {
461 error(errSyntaxWarning, -1, "Bad color space dict'");
463 obj1.free();
464 } else {
465 error(errSyntaxWarning, -1, "Bad color space - expected name or array or dict");
467 return cs;
470 void GfxColorSpace::createMapping(GooList *separationList, int maxSepComps) {
471 return;
474 void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
475 int maxImgPixel) {
476 int i;
478 for (i = 0; i < getNComps(); ++i) {
479 decodeLow[i] = 0;
480 decodeRange[i] = 1;
484 int GfxColorSpace::getNumColorSpaceModes() {
485 return nGfxColorSpaceModes;
488 const char *GfxColorSpace::getColorSpaceModeName(int idx) {
489 return gfxColorSpaceModeNames[idx];
492 #ifdef USE_CMS
493 cmsHPROFILE loadColorProfile(const char *fileName)
495 cmsHPROFILE hp = NULL;
496 FILE *fp;
498 if (fileName[0] == '/') {
499 // full path
500 // check if open the file
501 if ((fp = fopen(fileName,"r")) != NULL) {
502 fclose(fp);
503 hp = cmsOpenProfileFromFile(fileName,"r");
505 return hp;
507 // try to load from global directory
508 GooString *path = new GooString(GLOBAL_COLOR_PROFILE_DIR);
509 path->append(fileName);
510 // check if open the file
511 if ((fp = fopen(path->getCString(),"r")) != NULL) {
512 fclose(fp);
513 hp = cmsOpenProfileFromFile(path->getCString(),"r");
515 delete path;
516 return hp;
519 #ifdef USE_LCMS1
520 static int CMSError(int ecode, const char *msg)
522 error(errSyntaxWarning, -1, "{0:s}", msg);
523 return 1;
525 #else
526 static void CMSError(cmsContext /*contextId*/, cmsUInt32Number /*ecode*/, const char *text)
528 error(errSyntaxWarning, -1, "{0:s}", text);
530 #endif
532 int GfxColorSpace::setupColorProfiles()
534 static GBool initialized = gFalse;
535 cmsHTRANSFORM transform;
536 unsigned int nChannels;
538 // do only once
539 if (initialized) return 0;
540 initialized = gTrue;
542 // set error handlor
543 cmsSetLogErrorHandler(CMSError);
545 if (displayProfile == NULL) {
546 // load display profile if it was not already loaded.
547 if (displayProfileName == NULL) {
548 displayProfile = loadColorProfile("display.icc");
549 } else if (displayProfileName->getLength() > 0) {
550 displayProfile = loadColorProfile(displayProfileName->getCString());
553 // load RGB profile
554 RGBProfile = loadColorProfile("RGB.icc");
555 if (RGBProfile == NULL) {
556 /* use built in sRGB profile */
557 RGBProfile = cmsCreate_sRGBProfile();
559 // create transforms
560 if (displayProfile != NULL) {
561 displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile));
562 nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile));
563 // create transform from XYZ
564 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
565 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
566 displayProfile,
567 COLORSPACE_SH(displayPixelType) |
568 CHANNELS_SH(nChannels) | BYTES_SH(1),
569 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
570 error(errSyntaxWarning, -1, "Can't create Lab transform");
571 } else {
572 XYZ2DisplayTransform = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, displayPixelType);
574 cmsCloseProfile(XYZProfile);
576 return 0;
579 unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs)
581 switch (cs) {
582 case cmsSigXYZData:
583 return PT_XYZ;
584 break;
585 case cmsSigLabData:
586 return PT_Lab;
587 break;
588 case cmsSigLuvData:
589 return PT_YUV;
590 break;
591 case cmsSigYCbCrData:
592 return PT_YCbCr;
593 break;
594 case cmsSigYxyData:
595 return PT_Yxy;
596 break;
597 case cmsSigRgbData:
598 return PT_RGB;
599 break;
600 case cmsSigGrayData:
601 return PT_GRAY;
602 break;
603 case cmsSigHsvData:
604 return PT_HSV;
605 break;
606 case cmsSigHlsData:
607 return PT_HLS;
608 break;
609 case cmsSigCmykData:
610 return PT_CMYK;
611 break;
612 case cmsSigCmyData:
613 return PT_CMY;
614 break;
615 case cmsSig2colorData:
616 case cmsSig3colorData:
617 case cmsSig4colorData:
618 case cmsSig5colorData:
619 case cmsSig6colorData:
620 case cmsSig7colorData:
621 case cmsSig8colorData:
622 case cmsSig9colorData:
623 case cmsSig10colorData:
624 case cmsSig11colorData:
625 case cmsSig12colorData:
626 case cmsSig13colorData:
627 case cmsSig14colorData:
628 case cmsSig15colorData:
629 default:
630 break;
632 return PT_RGB;
635 unsigned int getCMSNChannels(cmsColorSpaceSignature cs)
637 switch (cs) {
638 case cmsSigXYZData:
639 case cmsSigLuvData:
640 case cmsSigLabData:
641 case cmsSigYCbCrData:
642 case cmsSigYxyData:
643 case cmsSigRgbData:
644 case cmsSigHsvData:
645 case cmsSigHlsData:
646 case cmsSigCmyData:
647 case cmsSig3colorData:
648 return 3;
649 break;
650 case cmsSigGrayData:
651 return 1;
652 break;
653 case cmsSigCmykData:
654 case cmsSig4colorData:
655 return 4;
656 break;
657 case cmsSig2colorData:
658 return 2;
659 break;
660 case cmsSig5colorData:
661 return 5;
662 break;
663 case cmsSig6colorData:
664 return 6;
665 break;
666 case cmsSig7colorData:
667 return 7;
668 break;
669 case cmsSig8colorData:
670 return 8;
671 break;
672 case cmsSig9colorData:
673 return 9;
674 break;
675 case cmsSig10colorData:
676 return 10;
677 break;
678 case cmsSig11colorData:
679 return 11;
680 break;
681 case cmsSig12colorData:
682 return 12;
683 break;
684 case cmsSig13colorData:
685 return 13;
686 break;
687 case cmsSig14colorData:
688 return 14;
689 break;
690 case cmsSig15colorData:
691 return 15;
692 default:
693 break;
695 return 3;
697 #endif
699 //------------------------------------------------------------------------
700 // GfxDeviceGrayColorSpace
701 //------------------------------------------------------------------------
703 GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
706 GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
709 GfxColorSpace *GfxDeviceGrayColorSpace::copy() {
710 return new GfxDeviceGrayColorSpace();
713 void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
714 *gray = clip01(color->c[0]);
717 void GfxDeviceGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
718 memcpy (out, in, length);
721 void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
722 rgb->r = rgb->g = rgb->b = clip01(color->c[0]);
725 void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, unsigned int *out,
726 int length) {
727 int i;
729 for (i = 0; i < length; i++)
730 out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0);
733 void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
734 for (int i = 0; i < length; i++) {
735 *out++ = in[i];
736 *out++ = in[i];
737 *out++ = in[i];
741 void GfxDeviceGrayColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
742 for (int i = 0; i < length; i++) {
743 *out++ = in[i];
744 *out++ = in[i];
745 *out++ = in[i];
746 *out++ = 255;
750 void GfxDeviceGrayColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
751 for (int i = 0; i < length; i++) {
752 *out++ = 0;
753 *out++ = 0;
754 *out++ = 0;
755 *out++ = in[i];
759 void GfxDeviceGrayColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
760 for (int i = 0; i < length; i++) {
761 for (int j = 0; j < SPOT_NCOMPS+4; j++)
762 out[j] = 0;
763 out[4] = in[i];
764 out += (SPOT_NCOMPS+4);
768 void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
769 cmyk->c = cmyk->m = cmyk->y = 0;
770 cmyk->k = clip01(gfxColorComp1 - color->c[0]);
773 void GfxDeviceGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
774 for (int i = 0; i < gfxColorMaxComps; i++)
775 deviceN->c[i] = 0;
776 deviceN->c[3] = clip01(gfxColorComp1 - color->c[0]);
779 void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) {
780 color->c[0] = 0;
783 //------------------------------------------------------------------------
784 // GfxCalGrayColorSpace
785 //------------------------------------------------------------------------
787 GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
788 whiteX = whiteY = whiteZ = 1;
789 blackX = blackY = blackZ = 0;
790 gamma = 1;
793 GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
794 #ifdef USE_CMS
795 if (transform != NULL) {
796 if (transform->unref() == 0) delete transform;
798 #endif
801 GfxColorSpace *GfxCalGrayColorSpace::copy() {
802 GfxCalGrayColorSpace *cs;
804 cs = new GfxCalGrayColorSpace();
805 cs->whiteX = whiteX;
806 cs->whiteY = whiteY;
807 cs->whiteZ = whiteZ;
808 cs->blackX = blackX;
809 cs->blackY = blackY;
810 cs->blackZ = blackZ;
811 cs->gamma = gamma;
812 #ifdef USE_CMS
813 cs->transform = transform;
814 if (transform != NULL) transform->ref();
815 #endif
816 return cs;
819 // This is the inverse of MatrixLMN in Example 4.10 from the PostScript
820 // Language Reference, Third Edition.
821 static const double xyzrgb[3][3] = {
822 { 3.240449, -1.537136, -0.498531 },
823 { -0.969265, 1.876011, 0.041556 },
824 { 0.055643, -0.204026, 1.057229 }
827 GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr, GfxState *state) {
828 GfxCalGrayColorSpace *cs;
829 Object obj1, obj2, obj3;
831 arr->get(1, &obj1);
832 if (!obj1.isDict()) {
833 error(errSyntaxWarning, -1, "Bad CalGray color space");
834 obj1.free();
835 return NULL;
837 cs = new GfxCalGrayColorSpace();
838 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
839 obj2.arrayGetLength() == 3) {
840 obj2.arrayGet(0, &obj3);
841 if (likely(obj3.isNum()))
842 cs->whiteX = obj3.getNum();
843 obj3.free();
844 obj2.arrayGet(1, &obj3);
845 if (likely(obj3.isNum()))
846 cs->whiteY = obj3.getNum();
847 obj3.free();
848 obj2.arrayGet(2, &obj3);
849 if (likely(obj3.isNum()))
850 cs->whiteZ = obj3.getNum();
851 obj3.free();
853 obj2.free();
854 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
855 obj2.arrayGetLength() == 3) {
856 obj2.arrayGet(0, &obj3);
857 if (likely(obj3.isNum()))
858 cs->blackX = obj3.getNum();
859 obj3.free();
860 obj2.arrayGet(1, &obj3);
861 if (likely(obj3.isNum()))
862 cs->blackY = obj3.getNum();
863 obj3.free();
864 obj2.arrayGet(2, &obj3);
865 if (likely(obj3.isNum()))
866 cs->blackZ = obj3.getNum();
867 obj3.free();
869 obj2.free();
870 if (obj1.dictLookup("Gamma", &obj2)->isNum()) {
871 cs->gamma = obj2.getNum();
873 obj2.free();
874 obj1.free();
876 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
877 xyzrgb[0][1] * cs->whiteY +
878 xyzrgb[0][2] * cs->whiteZ);
879 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
880 xyzrgb[1][1] * cs->whiteY +
881 xyzrgb[1][2] * cs->whiteZ);
882 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
883 xyzrgb[2][1] * cs->whiteY +
884 xyzrgb[2][2] * cs->whiteZ);
885 #ifdef USE_CMS
886 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
887 if (cs->transform != NULL) cs->transform->ref();
888 #endif
889 return cs;
892 // convert CalGray to media XYZ color space
893 // (not multiply by the white point)
894 void GfxCalGrayColorSpace::getXYZ(GfxColor *color,
895 double *pX, double *pY, double *pZ) {
896 const double A = colToDbl(color->c[0]);
897 const double xyzColor = pow(A,gamma);
898 *pX = xyzColor;
899 *pY = xyzColor;
900 *pZ = xyzColor;
903 void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) {
904 GfxRGB rgb;
906 #ifdef USE_CMS
907 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
908 Guchar out[gfxColorMaxComps];
909 double in[gfxColorMaxComps];
910 double X, Y, Z;
912 getXYZ(color,&X,&Y,&Z);
913 in[0] = clip01(X);
914 in[1] = clip01(Y);
915 in[2] = clip01(Z);
916 transform->doTransform(in,out,1);
917 *gray = byteToCol(out[0]);
918 return;
920 #endif
921 getRGB(color, &rgb);
922 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
923 0.587 * rgb.g +
924 0.114 * rgb.b + 0.5));
927 void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
928 double X, Y, Z;
929 double r, g, b;
931 getXYZ(color,&X,&Y,&Z);
932 #ifdef USE_CMS
933 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
934 Guchar out[gfxColorMaxComps];
935 double in[gfxColorMaxComps];
937 in[0] = clip01(X);
938 in[1] = clip01(Y);
939 in[2] = clip01(Z);
940 transform->doTransform(in,out,1);
941 rgb->r = byteToCol(out[0]);
942 rgb->g = byteToCol(out[1]);
943 rgb->b = byteToCol(out[2]);
944 return;
946 #endif
947 X *= whiteX;
948 Y *= whiteY;
949 Z *= whiteZ;
950 // convert XYZ to RGB, including gamut mapping and gamma correction
951 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
952 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
953 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
954 rgb->r = dblToCol(sqrt(clip01(r * kr)));
955 rgb->g = dblToCol(sqrt(clip01(g * kg)));
956 rgb->b = dblToCol(sqrt(clip01(b * kb)));
959 void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
960 GfxRGB rgb;
961 GfxColorComp c, m, y, k;
963 #ifdef USE_CMS
964 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
965 double in[gfxColorMaxComps];
966 Guchar out[gfxColorMaxComps];
967 double X, Y, Z;
969 getXYZ(color,&X,&Y,&Z);
970 in[0] = clip01(X);
971 in[1] = clip01(Y);
972 in[2] = clip01(Z);
974 transform->doTransform(in,out,1);
975 cmyk->c = byteToCol(out[0]);
976 cmyk->m = byteToCol(out[1]);
977 cmyk->y = byteToCol(out[2]);
978 cmyk->k = byteToCol(out[3]);
979 return;
981 #endif
982 getRGB(color, &rgb);
983 c = clip01(gfxColorComp1 - rgb.r);
984 m = clip01(gfxColorComp1 - rgb.g);
985 y = clip01(gfxColorComp1 - rgb.b);
986 k = c;
987 if (m < k) {
988 k = m;
990 if (y < k) {
991 k = y;
993 cmyk->c = c - k;
994 cmyk->m = m - k;
995 cmyk->y = y - k;
996 cmyk->k = k;
999 void GfxCalGrayColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1000 GfxCMYK cmyk;
1001 for (int i = 0; i < gfxColorMaxComps; i++)
1002 deviceN->c[i] = 0;
1003 getCMYK(color, &cmyk);
1004 deviceN->c[0] = cmyk.c;
1005 deviceN->c[1] = cmyk.m;
1006 deviceN->c[2] = cmyk.y;
1007 deviceN->c[3] = cmyk.k;
1010 void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) {
1011 color->c[0] = 0;
1014 //------------------------------------------------------------------------
1015 // GfxDeviceRGBColorSpace
1016 //------------------------------------------------------------------------
1018 GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
1021 GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
1024 GfxColorSpace *GfxDeviceRGBColorSpace::copy() {
1025 return new GfxDeviceRGBColorSpace();
1028 void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1029 *gray = clip01((GfxColorComp)(0.3 * color->c[0] +
1030 0.59 * color->c[1] +
1031 0.11 * color->c[2] + 0.5));
1034 void GfxDeviceRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) {
1035 int i;
1037 for (i = 0; i < length; i++) {
1038 out[i] =
1039 (in[i * 3 + 0] * 19595 +
1040 in[i * 3 + 1] * 38469 +
1041 in[i * 3 + 2] * 7472) / 65536;
1045 void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1046 rgb->r = clip01(color->c[0]);
1047 rgb->g = clip01(color->c[1]);
1048 rgb->b = clip01(color->c[2]);
1051 void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out,
1052 int length) {
1053 Guchar *p;
1054 int i;
1056 for (i = 0, p = in; i < length; i++, p += 3)
1057 out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0);
1060 void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
1061 for (int i = 0; i < length; i++) {
1062 *out++ = *in++;
1063 *out++ = *in++;
1064 *out++ = *in++;
1068 void GfxDeviceRGBColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
1069 for (int i = 0; i < length; i++) {
1070 *out++ = *in++;
1071 *out++ = *in++;
1072 *out++ = *in++;
1073 *out++ = 255;
1077 void GfxDeviceRGBColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
1078 GfxColorComp c, m, y, k;
1080 for (int i = 0; i < length; i++) {
1081 c = byteToCol(255 - *in++);
1082 m = byteToCol(255 - *in++);
1083 y = byteToCol(255 - *in++);
1084 k = c;
1085 if (m < k) {
1086 k = m;
1088 if (y < k) {
1089 k = y;
1091 *out++ = colToByte(c - k);
1092 *out++ = colToByte(m - k);
1093 *out++ = colToByte(y - k);
1094 *out++ = colToByte(k);
1098 void GfxDeviceRGBColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
1099 GfxColorComp c, m, y, k;
1101 for (int i = 0; i < length; i++) {
1102 for (int j = 0; j < SPOT_NCOMPS+4; j++)
1103 out[j] = 0;
1104 c = byteToCol(255 - *in++);
1105 m = byteToCol(255 - *in++);
1106 y = byteToCol(255 - *in++);
1107 k = c;
1108 if (m < k) {
1109 k = m;
1111 if (y < k) {
1112 k = y;
1114 out[0] = colToByte(c - k);
1115 out[1] = colToByte(m - k);
1116 out[2] = colToByte(y - k);
1117 out[3] = colToByte(k);
1118 out += (SPOT_NCOMPS+4);
1122 void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1123 GfxColorComp c, m, y, k;
1125 c = clip01(gfxColorComp1 - color->c[0]);
1126 m = clip01(gfxColorComp1 - color->c[1]);
1127 y = clip01(gfxColorComp1 - color->c[2]);
1128 k = c;
1129 if (m < k) {
1130 k = m;
1132 if (y < k) {
1133 k = y;
1135 cmyk->c = c - k;
1136 cmyk->m = m - k;
1137 cmyk->y = y - k;
1138 cmyk->k = k;
1141 void GfxDeviceRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1142 GfxCMYK cmyk;
1143 for (int i = 0; i < gfxColorMaxComps; i++)
1144 deviceN->c[i] = 0;
1145 getCMYK(color, &cmyk);
1146 deviceN->c[0] = cmyk.c;
1147 deviceN->c[1] = cmyk.m;
1148 deviceN->c[2] = cmyk.y;
1149 deviceN->c[3] = cmyk.k;
1152 void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) {
1153 color->c[0] = 0;
1154 color->c[1] = 0;
1155 color->c[2] = 0;
1158 //------------------------------------------------------------------------
1159 // GfxCalRGBColorSpace
1160 //------------------------------------------------------------------------
1162 GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
1163 whiteX = whiteY = whiteZ = 1;
1164 blackX = blackY = blackZ = 0;
1165 gammaR = gammaG = gammaB = 1;
1166 mat[0] = 1; mat[1] = 0; mat[2] = 0;
1167 mat[3] = 0; mat[4] = 1; mat[5] = 0;
1168 mat[6] = 0; mat[7] = 0; mat[8] = 1;
1171 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
1172 #ifdef USE_CMS
1173 if (transform != NULL) {
1174 if (transform->unref() == 0) delete transform;
1176 #endif
1179 GfxColorSpace *GfxCalRGBColorSpace::copy() {
1180 GfxCalRGBColorSpace *cs;
1181 int i;
1183 cs = new GfxCalRGBColorSpace();
1184 cs->whiteX = whiteX;
1185 cs->whiteY = whiteY;
1186 cs->whiteZ = whiteZ;
1187 cs->blackX = blackX;
1188 cs->blackY = blackY;
1189 cs->blackZ = blackZ;
1190 cs->gammaR = gammaR;
1191 cs->gammaG = gammaG;
1192 cs->gammaB = gammaB;
1193 for (i = 0; i < 9; ++i) {
1194 cs->mat[i] = mat[i];
1196 #ifdef USE_CMS
1197 cs->transform = transform;
1198 if (transform != NULL) transform->ref();
1199 #endif
1200 return cs;
1203 GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr, GfxState *state) {
1204 GfxCalRGBColorSpace *cs;
1205 Object obj1, obj2, obj3;
1206 int i;
1208 arr->get(1, &obj1);
1209 if (!obj1.isDict()) {
1210 error(errSyntaxWarning, -1, "Bad CalRGB color space");
1211 obj1.free();
1212 return NULL;
1214 cs = new GfxCalRGBColorSpace();
1215 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
1216 obj2.arrayGetLength() == 3) {
1217 obj2.arrayGet(0, &obj3);
1218 if (likely(obj3.isNum()))
1219 cs->whiteX = obj3.getNum();
1220 obj3.free();
1221 obj2.arrayGet(1, &obj3);
1222 if (likely(obj3.isNum()))
1223 cs->whiteY = obj3.getNum();
1224 obj3.free();
1225 obj2.arrayGet(2, &obj3);
1226 if (likely(obj3.isNum()))
1227 cs->whiteZ = obj3.getNum();
1228 obj3.free();
1230 obj2.free();
1231 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
1232 obj2.arrayGetLength() == 3) {
1233 obj2.arrayGet(0, &obj3);
1234 if (likely(obj3.isNum()))
1235 cs->blackX = obj3.getNum();
1236 obj3.free();
1237 obj2.arrayGet(1, &obj3);
1238 if (likely(obj3.isNum()))
1239 cs->blackY = obj3.getNum();
1240 obj3.free();
1241 obj2.arrayGet(2, &obj3);
1242 if (likely(obj3.isNum()))
1243 cs->blackZ = obj3.getNum();
1244 obj3.free();
1246 obj2.free();
1247 if (obj1.dictLookup("Gamma", &obj2)->isArray() &&
1248 obj2.arrayGetLength() == 3) {
1249 obj2.arrayGet(0, &obj3);
1250 if (likely(obj3.isNum()))
1251 cs->gammaR = obj3.getNum();
1252 obj3.free();
1253 obj2.arrayGet(1, &obj3);
1254 if (likely(obj3.isNum()))
1255 cs->gammaG = obj3.getNum();
1256 obj3.free();
1257 obj2.arrayGet(2, &obj3);
1258 if (likely(obj3.isNum()))
1259 cs->gammaB = obj3.getNum();
1260 obj3.free();
1262 obj2.free();
1263 if (obj1.dictLookup("Matrix", &obj2)->isArray() &&
1264 obj2.arrayGetLength() == 9) {
1265 for (i = 0; i < 9; ++i) {
1266 obj2.arrayGet(i, &obj3);
1267 if (likely(obj3.isNum()))
1268 cs->mat[i] = obj3.getNum();
1269 obj3.free();
1272 obj2.free();
1273 obj1.free();
1275 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1276 xyzrgb[0][1] * cs->whiteY +
1277 xyzrgb[0][2] * cs->whiteZ);
1278 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1279 xyzrgb[1][1] * cs->whiteY +
1280 xyzrgb[1][2] * cs->whiteZ);
1281 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1282 xyzrgb[2][1] * cs->whiteY +
1283 xyzrgb[2][2] * cs->whiteZ);
1285 #ifdef USE_CMS
1286 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
1287 if (cs->transform != NULL) cs->transform->ref();
1288 #endif
1289 return cs;
1292 // convert CalRGB to XYZ color space
1293 void GfxCalRGBColorSpace::getXYZ(GfxColor *color,
1294 double *pX, double *pY, double *pZ) {
1295 double A, B, C;
1297 A = pow(colToDbl(color->c[0]), gammaR);
1298 B = pow(colToDbl(color->c[1]), gammaG);
1299 C = pow(colToDbl(color->c[2]), gammaB);
1300 *pX = mat[0] * A + mat[3] * B + mat[6] * C;
1301 *pY = mat[1] * A + mat[4] * B + mat[7] * C;
1302 *pZ = mat[2] * A + mat[5] * B + mat[8] * C;
1305 void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1306 GfxRGB rgb;
1308 #ifdef USE_CMS
1309 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
1310 Guchar out[gfxColorMaxComps];
1311 double in[gfxColorMaxComps];
1312 double X, Y, Z;
1314 getXYZ(color,&X,&Y,&Z);
1315 in[0] = clip01(X);
1316 in[1] = clip01(Y);
1317 in[2] = clip01(Z);
1318 transform->doTransform(in,out,1);
1319 *gray = byteToCol(out[0]);
1320 return;
1322 #endif
1323 getRGB(color, &rgb);
1324 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1325 0.587 * rgb.g +
1326 0.114 * rgb.b + 0.5));
1329 void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1330 double X, Y, Z;
1331 double r, g, b;
1333 getXYZ(color,&X,&Y,&Z);
1334 #ifdef USE_CMS
1335 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
1336 Guchar out[gfxColorMaxComps];
1337 double in[gfxColorMaxComps];
1339 in[0] = clip01(X/whiteX);
1340 in[1] = clip01(Y/whiteY);
1341 in[2] = clip01(Z/whiteZ);
1342 transform->doTransform(in,out,1);
1343 rgb->r = byteToCol(out[0]);
1344 rgb->g = byteToCol(out[1]);
1345 rgb->b = byteToCol(out[2]);
1346 return;
1348 #endif
1349 // convert XYZ to RGB, including gamut mapping and gamma correction
1350 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1351 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1352 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1353 rgb->r = dblToCol(sqrt(clip01(r)));
1354 rgb->g = dblToCol(sqrt(clip01(g)));
1355 rgb->b = dblToCol(sqrt(clip01(b)));
1358 void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1359 GfxRGB rgb;
1360 GfxColorComp c, m, y, k;
1362 #ifdef USE_CMS
1363 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1364 double in[gfxColorMaxComps];
1365 Guchar out[gfxColorMaxComps];
1366 double X, Y, Z;
1368 getXYZ(color,&X,&Y,&Z);
1369 in[0] = clip01(X);
1370 in[1] = clip01(Y);
1371 in[2] = clip01(Z);
1372 transform->doTransform(in,out,1);
1373 cmyk->c = byteToCol(out[0]);
1374 cmyk->m = byteToCol(out[1]);
1375 cmyk->y = byteToCol(out[2]);
1376 cmyk->k = byteToCol(out[3]);
1377 return;
1379 #endif
1380 getRGB(color, &rgb);
1381 c = clip01(gfxColorComp1 - rgb.r);
1382 m = clip01(gfxColorComp1 - rgb.g);
1383 y = clip01(gfxColorComp1 - rgb.b);
1384 k = c;
1385 if (m < k) {
1386 k = m;
1388 if (y < k) {
1389 k = y;
1391 cmyk->c = c - k;
1392 cmyk->m = m - k;
1393 cmyk->y = y - k;
1394 cmyk->k = k;
1397 void GfxCalRGBColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1398 GfxCMYK cmyk;
1399 for (int i = 0; i < gfxColorMaxComps; i++)
1400 deviceN->c[i] = 0;
1401 getCMYK(color, &cmyk);
1402 deviceN->c[0] = cmyk.c;
1403 deviceN->c[1] = cmyk.m;
1404 deviceN->c[2] = cmyk.y;
1405 deviceN->c[3] = cmyk.k;
1408 void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) {
1409 color->c[0] = 0;
1410 color->c[1] = 0;
1411 color->c[2] = 0;
1414 //------------------------------------------------------------------------
1415 // GfxDeviceCMYKColorSpace
1416 //------------------------------------------------------------------------
1418 GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
1421 GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
1424 GfxColorSpace *GfxDeviceCMYKColorSpace::copy() {
1425 return new GfxDeviceCMYKColorSpace();
1428 void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1429 *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3]
1430 - 0.3 * color->c[0]
1431 - 0.59 * color->c[1]
1432 - 0.11 * color->c[2] + 0.5));
1435 void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1436 double c, m, y, k, c1, m1, y1, k1, r, g, b;
1438 c = colToDbl(color->c[0]);
1439 m = colToDbl(color->c[1]);
1440 y = colToDbl(color->c[2]);
1441 k = colToDbl(color->c[3]);
1442 c1 = 1 - c;
1443 m1 = 1 - m;
1444 y1 = 1 - y;
1445 k1 = 1 - k;
1446 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1447 rgb->r = clip01(dblToCol(r));
1448 rgb->g = clip01(dblToCol(g));
1449 rgb->b = clip01(dblToCol(b));
1452 static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(Guchar *&in, double &r, double &g, double &b)
1454 double c, m, y, k, c1, m1, y1, k1;
1456 c = byteToDbl(*in++);
1457 m = byteToDbl(*in++);
1458 y = byteToDbl(*in++);
1459 k = byteToDbl(*in++);
1460 c1 = 1 - c;
1461 m1 = 1 - m;
1462 y1 = 1 - y;
1463 k1 = 1 - k;
1464 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1467 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length)
1469 double r, g, b;
1470 for (int i = 0; i < length; i++) {
1471 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1472 *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b));
1476 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
1478 double r, g, b;
1480 for (int i = 0; i < length; i++) {
1481 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1482 *out++ = dblToByte(clip01(r));
1483 *out++ = dblToByte(clip01(g));
1484 *out++ = dblToByte(clip01(b));
1488 void GfxDeviceCMYKColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
1490 double r, g, b;
1492 for (int i = 0; i < length; i++) {
1493 GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b);
1494 *out++ = dblToByte(clip01(r));
1495 *out++ = dblToByte(clip01(g));
1496 *out++ = dblToByte(clip01(b));
1497 *out++ = 255;
1501 void GfxDeviceCMYKColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length)
1503 for (int i = 0; i < length; i++) {
1504 *out++ = *in++;
1505 *out++ = *in++;
1506 *out++ = *in++;
1507 *out++ = *in++;
1511 void GfxDeviceCMYKColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length)
1513 for (int i = 0; i < length; i++) {
1514 for (int j = 0; j < SPOT_NCOMPS+4; j++)
1515 out[j] = 0;
1516 out[0] = *in++;
1517 out[1] = *in++;
1518 out[2] = *in++;
1519 out[3] = *in++;
1520 out += (SPOT_NCOMPS+4);
1524 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1525 cmyk->c = clip01(color->c[0]);
1526 cmyk->m = clip01(color->c[1]);
1527 cmyk->y = clip01(color->c[2]);
1528 cmyk->k = clip01(color->c[3]);
1531 void GfxDeviceCMYKColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1532 for (int i = 0; i < gfxColorMaxComps; i++)
1533 deviceN->c[i] = 0;
1534 deviceN->c[0] = clip01(color->c[0]);
1535 deviceN->c[1] = clip01(color->c[1]);
1536 deviceN->c[2] = clip01(color->c[2]);
1537 deviceN->c[3] = clip01(color->c[3]);
1540 void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) {
1541 color->c[0] = 0;
1542 color->c[1] = 0;
1543 color->c[2] = 0;
1544 color->c[3] = gfxColorComp1;
1547 //------------------------------------------------------------------------
1548 // GfxLabColorSpace
1549 //------------------------------------------------------------------------
1551 GfxLabColorSpace::GfxLabColorSpace() {
1552 whiteX = whiteY = whiteZ = 1;
1553 blackX = blackY = blackZ = 0;
1554 aMin = bMin = -100;
1555 aMax = bMax = 100;
1558 GfxLabColorSpace::~GfxLabColorSpace() {
1559 #ifdef USE_CMS
1560 if (transform != NULL) {
1561 if (transform->unref() == 0) delete transform;
1563 #endif
1566 GfxColorSpace *GfxLabColorSpace::copy() {
1567 GfxLabColorSpace *cs;
1569 cs = new GfxLabColorSpace();
1570 cs->whiteX = whiteX;
1571 cs->whiteY = whiteY;
1572 cs->whiteZ = whiteZ;
1573 cs->blackX = blackX;
1574 cs->blackY = blackY;
1575 cs->blackZ = blackZ;
1576 cs->aMin = aMin;
1577 cs->aMax = aMax;
1578 cs->bMin = bMin;
1579 cs->bMax = bMax;
1580 cs->kr = kr;
1581 cs->kg = kg;
1582 cs->kb = kb;
1583 #ifdef USE_CMS
1584 cs->transform = transform;
1585 if (transform != NULL) transform->ref();
1586 #endif
1587 return cs;
1590 GfxColorSpace *GfxLabColorSpace::parse(Array *arr, GfxState *state) {
1591 GfxLabColorSpace *cs;
1592 Object obj1, obj2, obj3;
1594 arr->get(1, &obj1);
1595 if (!obj1.isDict()) {
1596 error(errSyntaxWarning, -1, "Bad Lab color space");
1597 obj1.free();
1598 return NULL;
1600 cs = new GfxLabColorSpace();
1601 if (obj1.dictLookup("WhitePoint", &obj2)->isArray() &&
1602 obj2.arrayGetLength() == 3) {
1603 obj2.arrayGet(0, &obj3);
1604 cs->whiteX = obj3.getNum();
1605 obj3.free();
1606 obj2.arrayGet(1, &obj3);
1607 cs->whiteY = obj3.getNum();
1608 obj3.free();
1609 obj2.arrayGet(2, &obj3);
1610 cs->whiteZ = obj3.getNum();
1611 obj3.free();
1613 obj2.free();
1614 if (obj1.dictLookup("BlackPoint", &obj2)->isArray() &&
1615 obj2.arrayGetLength() == 3) {
1616 obj2.arrayGet(0, &obj3);
1617 cs->blackX = obj3.getNum();
1618 obj3.free();
1619 obj2.arrayGet(1, &obj3);
1620 cs->blackY = obj3.getNum();
1621 obj3.free();
1622 obj2.arrayGet(2, &obj3);
1623 cs->blackZ = obj3.getNum();
1624 obj3.free();
1626 obj2.free();
1627 if (obj1.dictLookup("Range", &obj2)->isArray() &&
1628 obj2.arrayGetLength() == 4) {
1629 obj2.arrayGet(0, &obj3);
1630 cs->aMin = obj3.getNum();
1631 obj3.free();
1632 obj2.arrayGet(1, &obj3);
1633 cs->aMax = obj3.getNum();
1634 obj3.free();
1635 obj2.arrayGet(2, &obj3);
1636 cs->bMin = obj3.getNum();
1637 obj3.free();
1638 obj2.arrayGet(3, &obj3);
1639 cs->bMax = obj3.getNum();
1640 obj3.free();
1642 obj2.free();
1643 obj1.free();
1645 cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX +
1646 xyzrgb[0][1] * cs->whiteY +
1647 xyzrgb[0][2] * cs->whiteZ);
1648 cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX +
1649 xyzrgb[1][1] * cs->whiteY +
1650 xyzrgb[1][2] * cs->whiteZ);
1651 cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX +
1652 xyzrgb[2][1] * cs->whiteY +
1653 xyzrgb[2][2] * cs->whiteZ);
1655 #ifdef USE_CMS
1656 cs->transform = (state != NULL) ? state->getXYZ2DisplayTransform() : XYZ2DisplayTransform;
1657 if (cs->transform != NULL) cs->transform->ref();
1658 #endif
1659 return cs;
1662 void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) {
1663 GfxRGB rgb;
1665 #ifdef USE_CMS
1666 if (transform != NULL && transform->getTransformPixelType() == PT_GRAY) {
1667 Guchar out[gfxColorMaxComps];
1668 double in[gfxColorMaxComps];
1670 getXYZ(color, &in[0], &in[1], &in[2]);
1671 transform->doTransform(in,out,1);
1672 *gray = byteToCol(out[0]);
1673 return;
1675 #endif
1676 getRGB(color, &rgb);
1677 *gray = clip01((GfxColorComp)(0.299 * rgb.r +
1678 0.587 * rgb.g +
1679 0.114 * rgb.b + 0.5));
1682 // convert L*a*b* to media XYZ color space
1683 // (not multiply by the white point)
1684 void GfxLabColorSpace::getXYZ(GfxColor *color,
1685 double *pX, double *pY, double *pZ) {
1686 double X, Y, Z;
1687 double t1, t2;
1689 t1 = (colToDbl(color->c[0]) + 16) / 116;
1690 t2 = t1 + colToDbl(color->c[1]) / 500;
1691 if (t2 >= (6.0 / 29.0)) {
1692 X = t2 * t2 * t2;
1693 } else {
1694 X = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1696 if (t1 >= (6.0 / 29.0)) {
1697 Y = t1 * t1 * t1;
1698 } else {
1699 Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0));
1701 t2 = t1 - colToDbl(color->c[2]) / 200;
1702 if (t2 >= (6.0 / 29.0)) {
1703 Z = t2 * t2 * t2;
1704 } else {
1705 Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0));
1707 *pX = X;
1708 *pY = Y;
1709 *pZ = Z;
1712 void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
1713 double X, Y, Z;
1714 double r, g, b;
1716 getXYZ(color, &X, &Y, &Z);
1717 #ifdef USE_CMS
1718 if (transform != NULL && transform->getTransformPixelType() == PT_RGB) {
1719 Guchar out[gfxColorMaxComps];
1720 double in[gfxColorMaxComps];
1722 in[0] = clip01(X);
1723 in[1] = clip01(Y);
1724 in[2] = clip01(Z);
1725 transform->doTransform(in,out,1);
1726 rgb->r = byteToCol(out[0]);
1727 rgb->g = byteToCol(out[1]);
1728 rgb->b = byteToCol(out[2]);
1729 return;
1730 } else if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1731 Guchar out[gfxColorMaxComps];
1732 double in[gfxColorMaxComps];
1733 double c, m, y, k, c1, m1, y1, k1, r, g, b;
1735 in[0] = clip01(X);
1736 in[1] = clip01(Y);
1737 in[2] = clip01(Z);
1738 transform->doTransform(in,out,1);
1739 c = byteToDbl(out[0]);
1740 m = byteToDbl(out[1]);
1741 y = byteToDbl(out[2]);
1742 k = byteToDbl(out[3]);
1743 c1 = 1 - c;
1744 m1 = 1 - m;
1745 y1 = 1 - y;
1746 k1 = 1 - k;
1747 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
1748 rgb->r = clip01(dblToCol(r));
1749 rgb->g = clip01(dblToCol(g));
1750 rgb->b = clip01(dblToCol(b));
1751 return;
1753 #endif
1754 X *= whiteX;
1755 Y *= whiteY;
1756 Z *= whiteZ;
1757 // convert XYZ to RGB, including gamut mapping and gamma correction
1758 r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z;
1759 g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z;
1760 b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z;
1761 rgb->r = dblToCol(sqrt(clip01(r * kr)));
1762 rgb->g = dblToCol(sqrt(clip01(g * kg)));
1763 rgb->b = dblToCol(sqrt(clip01(b * kb)));
1766 void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
1767 GfxRGB rgb;
1768 GfxColorComp c, m, y, k;
1770 #ifdef USE_CMS
1771 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
1772 double in[gfxColorMaxComps];
1773 Guchar out[gfxColorMaxComps];
1775 getXYZ(color, &in[0], &in[1], &in[2]);
1776 transform->doTransform(in,out,1);
1777 cmyk->c = byteToCol(out[0]);
1778 cmyk->m = byteToCol(out[1]);
1779 cmyk->y = byteToCol(out[2]);
1780 cmyk->k = byteToCol(out[3]);
1781 return;
1783 #endif
1784 getRGB(color, &rgb);
1785 c = clip01(gfxColorComp1 - rgb.r);
1786 m = clip01(gfxColorComp1 - rgb.g);
1787 y = clip01(gfxColorComp1 - rgb.b);
1788 k = c;
1789 if (m < k) {
1790 k = m;
1792 if (y < k) {
1793 k = y;
1795 cmyk->c = c - k;
1796 cmyk->m = m - k;
1797 cmyk->y = y - k;
1798 cmyk->k = k;
1801 void GfxLabColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
1802 GfxCMYK cmyk;
1803 for (int i = 0; i < gfxColorMaxComps; i++)
1804 deviceN->c[i] = 0;
1805 getCMYK(color, &cmyk);
1806 deviceN->c[0] = cmyk.c;
1807 deviceN->c[1] = cmyk.m;
1808 deviceN->c[2] = cmyk.y;
1809 deviceN->c[3] = cmyk.k;
1812 void GfxLabColorSpace::getDefaultColor(GfxColor *color) {
1813 color->c[0] = 0;
1814 if (aMin > 0) {
1815 color->c[1] = dblToCol(aMin);
1816 } else if (aMax < 0) {
1817 color->c[1] = dblToCol(aMax);
1818 } else {
1819 color->c[1] = 0;
1821 if (bMin > 0) {
1822 color->c[2] = dblToCol(bMin);
1823 } else if (bMax < 0) {
1824 color->c[2] = dblToCol(bMax);
1825 } else {
1826 color->c[2] = 0;
1830 void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange,
1831 int maxImgPixel) {
1832 decodeLow[0] = 0;
1833 decodeRange[0] = 100;
1834 decodeLow[1] = aMin;
1835 decodeRange[1] = aMax - aMin;
1836 decodeLow[2] = bMin;
1837 decodeRange[2] = bMax - bMin;
1840 //------------------------------------------------------------------------
1841 // GfxICCBasedColorSpace
1842 //------------------------------------------------------------------------
1844 class GfxICCBasedColorSpaceKey : public PopplerCacheKey
1846 public:
1847 GfxICCBasedColorSpaceKey(int numA, int genA) : num(numA), gen(genA)
1851 bool operator==(const PopplerCacheKey &key) const
1853 const GfxICCBasedColorSpaceKey *k = static_cast<const GfxICCBasedColorSpaceKey*>(&key);
1854 return k->num == num && k->gen == gen;
1857 int num, gen;
1860 class GfxICCBasedColorSpaceItem : public PopplerCacheItem
1862 public:
1863 GfxICCBasedColorSpaceItem(GfxICCBasedColorSpace *csA)
1865 cs = static_cast<GfxICCBasedColorSpace*>(csA->copy());
1868 ~GfxICCBasedColorSpaceItem()
1870 delete cs;
1873 GfxICCBasedColorSpace *cs;
1876 GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA,
1877 Ref *iccProfileStreamA) {
1878 nComps = nCompsA;
1879 alt = altA;
1880 iccProfileStream = *iccProfileStreamA;
1881 rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0;
1882 rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1;
1883 #ifdef USE_CMS
1884 transform = NULL;
1885 lineTransform = NULL;
1886 #endif
1889 GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
1890 delete alt;
1891 #ifdef USE_CMS
1892 if (transform != NULL) {
1893 if (transform->unref() == 0) delete transform;
1895 if (lineTransform != NULL) {
1896 if (lineTransform->unref() == 0) delete lineTransform;
1898 #endif
1901 GfxColorSpace *GfxICCBasedColorSpace::copy() {
1902 GfxICCBasedColorSpace *cs;
1903 int i;
1905 cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream);
1906 for (i = 0; i < 4; ++i) {
1907 cs->rangeMin[i] = rangeMin[i];
1908 cs->rangeMax[i] = rangeMax[i];
1910 #ifdef USE_CMS
1911 cs->transform = transform;
1912 if (transform != NULL) transform->ref();
1913 cs->lineTransform = lineTransform;
1914 if (lineTransform != NULL) lineTransform->ref();
1915 #endif
1916 return cs;
1919 GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, OutputDev *out, GfxState *state, int recursion) {
1920 GfxICCBasedColorSpace *cs;
1921 Ref iccProfileStreamA;
1922 int nCompsA;
1923 GfxColorSpace *altA;
1924 Dict *dict;
1925 Object obj1, obj2, obj3;
1926 int i;
1928 if (arr->getLength() < 2) {
1929 error(errSyntaxError, -1, "Bad ICCBased color space");
1930 return NULL;
1932 arr->getNF(1, &obj1);
1933 if (obj1.isRef()) {
1934 iccProfileStreamA = obj1.getRef();
1935 } else {
1936 iccProfileStreamA.num = 0;
1937 iccProfileStreamA.gen = 0;
1939 obj1.free();
1940 #ifdef USE_CMS
1941 // check cache
1942 if (out && iccProfileStreamA.num > 0) {
1943 GfxICCBasedColorSpaceKey k(iccProfileStreamA.num, iccProfileStreamA.gen);
1944 GfxICCBasedColorSpaceItem *item = static_cast<GfxICCBasedColorSpaceItem *>(out->getIccColorSpaceCache()->lookup(k));
1945 if (item != NULL)
1947 cs = static_cast<GfxICCBasedColorSpace*>(item->cs->copy());
1948 int transformIntent = cs->getIntent();
1949 int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
1950 if (state != NULL) {
1951 const char *intent = state->getRenderingIntent();
1952 if (intent != NULL) {
1953 if (strcmp(intent, "AbsoluteColorimetric") == 0) {
1954 cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC;
1955 } else if (strcmp(intent, "Saturation") == 0) {
1956 cmsIntent = INTENT_SATURATION;
1957 } else if (strcmp(intent, "Perceptual") == 0) {
1958 cmsIntent = INTENT_PERCEPTUAL;
1962 if (transformIntent == cmsIntent) {
1963 return cs;
1965 delete cs;
1968 #endif
1969 arr->get(1, &obj1);
1970 if (!obj1.isStream()) {
1971 error(errSyntaxWarning, -1, "Bad ICCBased color space (stream)");
1972 obj1.free();
1973 return NULL;
1975 dict = obj1.streamGetDict();
1976 if (!dict->lookup("N", &obj2)->isInt()) {
1977 error(errSyntaxWarning, -1, "Bad ICCBased color space (N)");
1978 obj2.free();
1979 obj1.free();
1980 return NULL;
1982 nCompsA = obj2.getInt();
1983 obj2.free();
1984 if (nCompsA > 4) {
1985 error(errSyntaxError, -1,
1986 "ICCBased color space with too many ({0:d} > 4) components",
1987 nCompsA);
1988 nCompsA = 4;
1990 if (dict->lookup("Alternate", &obj2)->isNull() ||
1991 !(altA = GfxColorSpace::parse(NULL, &obj2, out, state, recursion + 1))) {
1992 switch (nCompsA) {
1993 case 1:
1994 altA = new GfxDeviceGrayColorSpace();
1995 break;
1996 case 3:
1997 altA = new GfxDeviceRGBColorSpace();
1998 break;
1999 case 4:
2000 altA = new GfxDeviceCMYKColorSpace();
2001 break;
2002 default:
2003 error(errSyntaxWarning, -1, "Bad ICCBased color space - invalid N");
2004 obj2.free();
2005 obj1.free();
2006 return NULL;
2009 obj2.free();
2010 cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA);
2011 if (dict->lookup("Range", &obj2)->isArray() &&
2012 obj2.arrayGetLength() == 2 * nCompsA) {
2013 Object obj4;
2014 for (i = 0; i < nCompsA; ++i) {
2015 obj2.arrayGet(2*i, &obj3);
2016 obj2.arrayGet(2*i+1, &obj4);
2017 if (obj3.isNum() && obj4.isNum()) {
2018 cs->rangeMin[i] = obj3.getNum();
2019 cs->rangeMax[i] = obj4.getNum();
2021 obj3.free();
2022 obj4.free();
2025 obj2.free();
2026 obj1.free();
2028 #ifdef USE_CMS
2029 arr->get(1, &obj1);
2030 dict = obj1.streamGetDict();
2031 Guchar *profBuf;
2032 Stream *iccStream = obj1.getStream();
2033 int length = 0;
2035 profBuf = iccStream->toUnsignedChars(&length, 65536, 65536);
2036 cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,length);
2037 gfree(profBuf);
2038 if (hp == 0) {
2039 error(errSyntaxWarning, -1, "read ICCBased color space profile error");
2040 } else {
2041 cmsHPROFILE dhp = (state != NULL && state->getDisplayProfile() != NULL) ? state->getDisplayProfile() : displayProfile;
2042 if (dhp == NULL) dhp = RGBProfile;
2043 unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(hp));
2044 unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp));
2045 unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp));
2046 cmsHTRANSFORM transform;
2048 int cmsIntent = INTENT_RELATIVE_COLORIMETRIC;
2049 if (state != NULL) {
2050 const char *intent = state->getRenderingIntent();
2051 if (intent != NULL) {
2052 if (strcmp(intent, "AbsoluteColorimetric") == 0) {
2053 cmsIntent = INTENT_ABSOLUTE_COLORIMETRIC;
2054 } else if (strcmp(intent, "Saturation") == 0) {
2055 cmsIntent = INTENT_SATURATION;
2056 } else if (strcmp(intent, "Perceptual") == 0) {
2057 cmsIntent = INTENT_PERCEPTUAL;
2061 if ((transform = cmsCreateTransform(hp,
2062 COLORSPACE_SH(cst) |CHANNELS_SH(nCompsA) | BYTES_SH(1),
2063 dhp,
2064 COLORSPACE_SH(dcst) |
2065 CHANNELS_SH(dNChannels) | BYTES_SH(1),
2066 cmsIntent, LCMS_FLAGS)) == 0) {
2067 error(errSyntaxWarning, -1, "Can't create transform");
2068 cs->transform = NULL;
2069 } else {
2070 cs->transform = new GfxColorTransform(transform, cmsIntent, cst, dcst);
2072 if (dcst == PT_RGB || dcst == PT_CMYK) {
2073 // create line transform only when the display is RGB type color space
2074 if ((transform = cmsCreateTransform(hp,
2075 CHANNELS_SH(nCompsA) | BYTES_SH(1),dhp,
2076 (dcst == PT_RGB) ? TYPE_RGB_8 : TYPE_CMYK_8, cmsIntent, LCMS_FLAGS)) == 0) {
2077 error(errSyntaxWarning, -1, "Can't create transform");
2078 cs->lineTransform = NULL;
2079 } else {
2080 cs->lineTransform = new GfxColorTransform(transform, cmsIntent, cst, dcst);
2083 cmsCloseProfile(hp);
2085 obj1.free();
2086 // put this colorSpace into cache
2087 if (out && iccProfileStreamA.num > 0) {
2088 GfxICCBasedColorSpaceKey *k = new GfxICCBasedColorSpaceKey(iccProfileStreamA.num, iccProfileStreamA.gen);
2089 GfxICCBasedColorSpaceItem *item = new GfxICCBasedColorSpaceItem(cs);
2090 out->getIccColorSpaceCache()->put(k, item);
2092 #endif
2093 return cs;
2096 void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2097 #ifdef USE_CMS
2098 if (transform != 0 && transform->getTransformPixelType() == PT_GRAY) {
2099 Guchar in[gfxColorMaxComps];
2100 Guchar out[gfxColorMaxComps];
2102 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2103 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2104 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2105 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2106 } else {
2107 for (int i = 0;i < nComps;i++) {
2108 in[i] = colToByte(color->c[i]);
2111 if (nComps <= 4) {
2112 unsigned int key = 0;
2113 for (int j = 0; j < nComps; j++) {
2114 key = (key << 8) + in[j];
2116 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2117 if (it != cmsCache.end()) {
2118 unsigned int value = it->second;
2119 *gray = byteToCol(value & 0xff);
2120 return;
2123 transform->doTransform(in,out,1);
2124 *gray = byteToCol(out[0]);
2125 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2126 unsigned int key = 0;
2127 for (int j = 0; j < nComps; j++) {
2128 key = (key << 8) + in[j];
2130 unsigned int value = out[0];
2131 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2133 } else {
2134 GfxRGB rgb;
2135 getRGB(color,&rgb);
2136 *gray = clip01((GfxColorComp)(0.3 * rgb.r +
2137 0.59 * rgb.g +
2138 0.11 * rgb.b + 0.5));
2140 #else
2141 alt->getGray(color, gray);
2142 #endif
2145 void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2146 #ifdef USE_CMS
2147 if (transform != 0 && transform->getTransformPixelType() == PT_RGB) {
2148 Guchar in[gfxColorMaxComps];
2149 Guchar out[gfxColorMaxComps];
2151 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2152 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2153 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2154 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2155 } else {
2156 for (int i = 0;i < nComps;i++) {
2157 in[i] = colToByte(color->c[i]);
2160 if (nComps <= 4) {
2161 unsigned int key = 0;
2162 for (int j = 0; j < nComps; j++) {
2163 key = (key << 8) + in[j];
2165 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2166 if (it != cmsCache.end()) {
2167 unsigned int value = it->second;
2168 rgb->r = byteToCol(value >> 16);
2169 rgb->g = byteToCol((value >> 8) & 0xff);
2170 rgb->b = byteToCol(value & 0xff);
2171 return;
2174 transform->doTransform(in,out,1);
2175 rgb->r = byteToCol(out[0]);
2176 rgb->g = byteToCol(out[1]);
2177 rgb->b = byteToCol(out[2]);
2178 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2179 unsigned int key = 0;
2180 for (int j = 0; j < nComps; j++) {
2181 key = (key << 8) + in[j];
2183 unsigned int value = (out[0] << 16) + (out[1] << 8) + out[2];
2184 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2186 } else if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
2187 Guchar in[gfxColorMaxComps];
2188 Guchar out[gfxColorMaxComps];
2189 double c, m, y, k, c1, m1, y1, k1, r, g, b;
2191 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2192 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2193 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2194 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2195 } else {
2196 for (int i = 0;i < nComps;i++) {
2197 in[i] = colToByte(color->c[i]);
2200 if (nComps <= 4) {
2201 unsigned int key = 0;
2202 for (int j = 0; j < nComps; j++) {
2203 key = (key << 8) + in[j];
2205 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2206 if (it != cmsCache.end()) {
2207 unsigned int value = it->second;
2208 rgb->r = byteToCol(value >> 16);
2209 rgb->g = byteToCol((value >> 8) & 0xff);
2210 rgb->b = byteToCol(value & 0xff);
2211 return;
2214 transform->doTransform(in,out,1);
2215 c = byteToDbl(out[0]);
2216 m = byteToDbl(out[1]);
2217 y = byteToDbl(out[2]);
2218 k = byteToDbl(out[3]);
2219 c1 = 1 - c;
2220 m1 = 1 - m;
2221 y1 = 1 - y;
2222 k1 = 1 - k;
2223 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
2224 rgb->r = clip01(dblToCol(r));
2225 rgb->g = clip01(dblToCol(g));
2226 rgb->b = clip01(dblToCol(b));
2227 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2228 unsigned int key = 0;
2229 for (int j = 0; j < nComps; j++) {
2230 key = (key << 8) + in[j];
2232 unsigned int value = (dblToByte(r) << 16) + (dblToByte(g) << 8) + dblToByte(b);
2233 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2235 } else {
2236 alt->getRGB(color, rgb);
2238 #else
2239 alt->getRGB(color, rgb);
2240 #endif
2243 void GfxICCBasedColorSpace::getRGBLine(Guchar *in, unsigned int *out,
2244 int length) {
2245 #ifdef USE_CMS
2246 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2247 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2248 lineTransform->doTransform(in, tmp, length);
2249 for (int i = 0; i < length; ++i) {
2250 Guchar *current = tmp + (i * 3);
2251 out[i] = (current[0] << 16) | (current[1] << 8) | current[2];
2253 gfree(tmp);
2254 } else {
2255 alt->getRGBLine(in, out, length);
2257 #else
2258 alt->getRGBLine(in, out, length);
2259 #endif
2262 void GfxICCBasedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) {
2263 #ifdef USE_CMS
2264 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2265 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2266 lineTransform->doTransform(in, tmp, length);
2267 Guchar *current = tmp;
2268 for (int i = 0; i < length; ++i) {
2269 *out++ = *current++;
2270 *out++ = *current++;
2271 *out++ = *current++;
2273 gfree(tmp);
2274 } else if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2275 Guchar* tmp = (Guchar *)gmallocn(4 * length, sizeof(Guchar));
2276 lineTransform->doTransform(in, tmp, length);
2277 Guchar *current = tmp;
2278 double c, m, y, k, c1, m1, y1, k1, r, g, b;
2279 for (int i = 0; i < length; ++i) {
2280 c = byteToDbl(*current++);
2281 m = byteToDbl(*current++);
2282 y = byteToDbl(*current++);
2283 k = byteToDbl(*current++);
2284 c1 = 1 - c;
2285 m1 = 1 - m;
2286 y1 = 1 - y;
2287 k1 = 1 - k;
2288 cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b);
2289 *out++ = dblToByte(r);
2290 *out++ = dblToByte(g);
2291 *out++ = dblToByte(b);
2293 gfree(tmp);
2294 } else {
2295 alt->getRGBLine(in, out, length);
2297 #else
2298 alt->getRGBLine(in, out, length);
2299 #endif
2302 void GfxICCBasedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) {
2303 #ifdef USE_CMS
2304 if (lineTransform != 0 && lineTransform->getTransformPixelType() == PT_RGB) {
2305 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2306 lineTransform->doTransform(in, tmp, length);
2307 Guchar *current = tmp;
2308 for (int i = 0; i < length; ++i) {
2309 *out++ = *current++;
2310 *out++ = *current++;
2311 *out++ = *current++;
2312 *out++ = 255;
2314 gfree(tmp);
2315 } else {
2316 alt->getRGBXLine(in, out, length);
2318 #else
2319 alt->getRGBXLine(in, out, length);
2320 #endif
2323 void GfxICCBasedColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
2324 #ifdef USE_CMS
2325 if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2326 transform->doTransform(in,out,length);
2327 } else if (lineTransform != NULL && nComps != 4) {
2328 GfxColorComp c, m, y, k;
2329 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2330 getRGBLine(in, tmp, length);
2331 Guchar *p = tmp;
2332 for (int i = 0; i < length; i++) {
2333 c = byteToCol(255 - *p++);
2334 m = byteToCol(255 - *p++);
2335 y = byteToCol(255 - *p++);
2336 k = c;
2337 if (m < k) {
2338 k = m;
2340 if (y < k) {
2341 k = y;
2343 *out++ = colToByte(c - k);
2344 *out++ = colToByte(m - k);
2345 *out++ = colToByte(y - k);
2346 *out++ = colToByte(k);
2348 gfree(tmp);
2349 } else {
2350 alt->getCMYKLine(in, out, length);
2352 #else
2353 alt->getCMYKLine(in, out, length);
2354 #endif
2357 void GfxICCBasedColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
2358 #ifdef USE_CMS
2359 if (lineTransform != NULL && lineTransform->getTransformPixelType() == PT_CMYK) {
2360 Guchar* tmp = (Guchar *)gmallocn(4 * length, sizeof(Guchar));
2361 transform->doTransform(in,tmp,length);
2362 Guchar *p = tmp;
2363 for (int i = 0; i < length; i++) {
2364 for (int j = 0; j < 4; j++)
2365 *out++ = *p++;
2366 for (int j = 4; j < SPOT_NCOMPS+4; j++)
2367 *out++ = 0;
2369 gfree(tmp);
2370 } else if (lineTransform != NULL && nComps != 4) {
2371 GfxColorComp c, m, y, k;
2372 Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar));
2373 getRGBLine(in, tmp, length);
2374 Guchar *p = tmp;
2375 for (int i = 0; i < length; i++) {
2376 for (int j = 0; j < SPOT_NCOMPS+4; j++)
2377 out[j] = 0;
2378 c = byteToCol(255 - *p++);
2379 m = byteToCol(255 - *p++);
2380 y = byteToCol(255 - *p++);
2381 k = c;
2382 if (m < k) {
2383 k = m;
2385 if (y < k) {
2386 k = y;
2388 out[0] = colToByte(c - k);
2389 out[1] = colToByte(m - k);
2390 out[2] = colToByte(y - k);
2391 out[3] = colToByte(k);
2392 out += (SPOT_NCOMPS+4);
2394 gfree(tmp);
2395 } else {
2396 alt->getDeviceNLine(in, out, length);
2398 #else
2399 alt->getDeviceNLine(in, out, length);
2400 #endif
2403 void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2404 #ifdef USE_CMS
2405 if (transform != NULL && transform->getTransformPixelType() == PT_CMYK) {
2406 Guchar in[gfxColorMaxComps];
2407 Guchar out[gfxColorMaxComps];
2409 if (nComps == 3 && transform->getInputPixelType() == PT_Lab) {
2410 in[0] = colToByte(dblToCol(colToDbl(color->c[0]) / 100.0));
2411 in[1] = colToByte(dblToCol((colToDbl(color->c[1]) + 128.0) / 255.0));
2412 in[2] = colToByte(dblToCol((colToDbl(color->c[2]) + 128.0) / 255.0));
2413 } else {
2414 for (int i = 0;i < nComps;i++) {
2415 in[i] = colToByte(color->c[i]);
2418 if (nComps <= 4) {
2419 unsigned int key = 0;
2420 for (int j = 0; j < nComps; j++) {
2421 key = (key << 8) + in[j];
2423 std::map<unsigned int, unsigned int>::iterator it = cmsCache.find(key);
2424 if (it != cmsCache.end()) {
2425 unsigned int value = it->second;
2426 cmyk->c = byteToCol(value >> 24);
2427 cmyk->m = byteToCol((value >> 16) & 0xff);
2428 cmyk->y = byteToCol((value >> 8) & 0xff);
2429 cmyk->k = byteToCol(value & 0xff);
2430 return;
2433 transform->doTransform(in,out,1);
2434 cmyk->c = byteToCol(out[0]);
2435 cmyk->m = byteToCol(out[1]);
2436 cmyk->y = byteToCol(out[2]);
2437 cmyk->k = byteToCol(out[3]);
2438 if (nComps <= 4 && cmsCache.size() <= CMSCACHE_LIMIT) {
2439 unsigned int key = 0;
2440 for (int j = 0; j < nComps; j++) {
2441 key = (key << 8) + in[j];
2443 unsigned int value = (out[0] << 24) + (out[1] << 16) + (out[2] << 8) + out[3];
2444 cmsCache.insert(std::pair<unsigned int, unsigned int>(key, value));
2446 } else if (nComps != 4 && transform != NULL && transform->getTransformPixelType() == PT_RGB) {
2447 GfxRGB rgb;
2448 GfxColorComp c, m, y, k;
2450 getRGB(color,&rgb);
2451 c = clip01(gfxColorComp1 - rgb.r);
2452 m = clip01(gfxColorComp1 - rgb.g);
2453 y = clip01(gfxColorComp1 - rgb.b);
2454 k = c;
2455 if (m < k) {
2456 k = m;
2458 if (y < k) {
2459 k = y;
2461 cmyk->c = c - k;
2462 cmyk->m = m - k;
2463 cmyk->y = y - k;
2464 cmyk->k = k;
2465 } else {
2466 alt->getCMYK(color, cmyk);
2468 #else
2469 alt->getCMYK(color, cmyk);
2470 #endif
2473 GBool GfxICCBasedColorSpace::useGetRGBLine() {
2474 #ifdef USE_CMS
2475 return lineTransform != NULL || alt->useGetRGBLine();
2476 #else
2477 return alt->useGetRGBLine();
2478 #endif
2481 GBool GfxICCBasedColorSpace::useGetCMYKLine() {
2482 #ifdef USE_CMS
2483 return lineTransform != NULL || alt->useGetCMYKLine();
2484 #else
2485 return alt->useGetCMYKLine();
2486 #endif
2489 GBool GfxICCBasedColorSpace::useGetDeviceNLine() {
2490 #ifdef USE_CMS
2491 return lineTransform != NULL || alt->useGetDeviceNLine();
2492 #else
2493 return alt->useGetDeviceNLine();
2494 #endif
2497 void GfxICCBasedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2498 GfxCMYK cmyk;
2499 for (int i = 0; i < gfxColorMaxComps; i++)
2500 deviceN->c[i] = 0;
2501 getCMYK(color, &cmyk);
2502 deviceN->c[0] = cmyk.c;
2503 deviceN->c[1] = cmyk.m;
2504 deviceN->c[2] = cmyk.y;
2505 deviceN->c[3] = cmyk.k;
2508 void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) {
2509 int i;
2511 for (i = 0; i < nComps; ++i) {
2512 if (rangeMin[i] > 0) {
2513 color->c[i] = dblToCol(rangeMin[i]);
2514 } else if (rangeMax[i] < 0) {
2515 color->c[i] = dblToCol(rangeMax[i]);
2516 } else {
2517 color->c[i] = 0;
2522 void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow,
2523 double *decodeRange,
2524 int maxImgPixel) {
2525 alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel);
2527 #if 0
2528 // this is nominally correct, but some PDF files don't set the
2529 // correct ranges in the ICCBased dict
2530 int i;
2532 for (i = 0; i < nComps; ++i) {
2533 decodeLow[i] = rangeMin[i];
2534 decodeRange[i] = rangeMax[i] - rangeMin[i];
2536 #endif
2539 //------------------------------------------------------------------------
2540 // GfxIndexedColorSpace
2541 //------------------------------------------------------------------------
2543 GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA,
2544 int indexHighA) {
2545 base = baseA;
2546 indexHigh = indexHighA;
2547 lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(),
2548 sizeof(Guchar));
2549 overprintMask = base->getOverprintMask();
2552 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
2553 delete base;
2554 gfree(lookup);
2557 GfxColorSpace *GfxIndexedColorSpace::copy() {
2558 GfxIndexedColorSpace *cs;
2560 cs = new GfxIndexedColorSpace(base->copy(), indexHigh);
2561 memcpy(cs->lookup, lookup,
2562 (indexHigh + 1) * base->getNComps() * sizeof(Guchar));
2563 return cs;
2566 GfxColorSpace *GfxIndexedColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
2567 GfxIndexedColorSpace *cs;
2568 GfxColorSpace *baseA;
2569 int indexHighA;
2570 Object obj1;
2571 char *s;
2572 int n, i, j;
2574 if (arr->getLength() != 4) {
2575 error(errSyntaxWarning, -1, "Bad Indexed color space");
2576 goto err1;
2578 arr->get(1, &obj1);
2579 if (!(baseA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2580 error(errSyntaxWarning, -1, "Bad Indexed color space (base color space)");
2581 goto err2;
2583 obj1.free();
2584 if (!arr->get(2, &obj1)->isInt()) {
2585 error(errSyntaxWarning, -1, "Bad Indexed color space (hival)");
2586 delete baseA;
2587 goto err2;
2589 indexHighA = obj1.getInt();
2590 if (indexHighA < 0 || indexHighA > 255) {
2591 // the PDF spec requires indexHigh to be in [0,255] -- allowing
2592 // values larger than 255 creates a security hole: if nComps *
2593 // indexHigh is greater than 2^31, the loop below may overwrite
2594 // past the end of the array
2595 int previousValue = indexHighA;
2596 if (indexHighA < 0) indexHighA = 0;
2597 else indexHighA = 255;
2598 error(errSyntaxWarning, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue, indexHighA);
2600 obj1.free();
2601 cs = new GfxIndexedColorSpace(baseA, indexHighA);
2602 arr->get(3, &obj1);
2603 n = baseA->getNComps();
2604 if (obj1.isStream()) {
2605 obj1.streamReset();
2606 for (i = 0; i <= indexHighA; ++i) {
2607 const int readChars = obj1.streamGetChars(n, &cs->lookup[i*n]);
2608 for (j = readChars; j < n; ++j) {
2609 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes");
2610 cs->lookup[i*n + j] = 0;
2613 obj1.streamClose();
2614 } else if (obj1.isString()) {
2615 if (obj1.getString()->getLength() < (indexHighA + 1) * n) {
2616 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table string too short)");
2617 goto err3;
2619 s = obj1.getString()->getCString();
2620 for (i = 0; i <= indexHighA; ++i) {
2621 for (j = 0; j < n; ++j) {
2622 cs->lookup[i*n + j] = (Guchar)*s++;
2625 } else {
2626 error(errSyntaxWarning, -1, "Bad Indexed color space (lookup table)");
2627 goto err3;
2629 obj1.free();
2630 return cs;
2632 err3:
2633 delete cs;
2634 err2:
2635 obj1.free();
2636 err1:
2637 return NULL;
2640 GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color,
2641 GfxColor *baseColor) {
2642 Guchar *p;
2643 double low[gfxColorMaxComps], range[gfxColorMaxComps];
2644 int n, i;
2646 n = base->getNComps();
2647 base->getDefaultRanges(low, range, indexHigh);
2648 const int idx = (int)(colToDbl(color->c[0]) + 0.5) * n;
2649 if (likely((idx + n < (indexHigh + 1) * base->getNComps()) && idx >= 0)) {
2650 p = &lookup[idx];
2651 for (i = 0; i < n; ++i) {
2652 baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]);
2654 } else {
2655 for (i = 0; i < n; ++i) {
2656 baseColor->c[i] = 0;
2659 return baseColor;
2662 void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2663 GfxColor color2;
2665 base->getGray(mapColorToBase(color, &color2), gray);
2668 void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2669 GfxColor color2;
2671 base->getRGB(mapColorToBase(color, &color2), rgb);
2674 void GfxIndexedColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) {
2675 Guchar *line;
2676 int i, j, n;
2678 n = base->getNComps();
2679 line = (Guchar *) gmallocn (length, n);
2680 for (i = 0; i < length; i++)
2681 for (j = 0; j < n; j++)
2682 line[i * n + j] = lookup[in[i] * n + j];
2684 base->getRGBLine(line, out, length);
2686 gfree (line);
2689 void GfxIndexedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length)
2691 Guchar *line;
2692 int i, j, n;
2694 n = base->getNComps();
2695 line = (Guchar *) gmallocn (length, n);
2696 for (i = 0; i < length; i++)
2697 for (j = 0; j < n; j++)
2698 line[i * n + j] = lookup[in[i] * n + j];
2700 base->getRGBLine(line, out, length);
2702 gfree (line);
2705 void GfxIndexedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length)
2707 Guchar *line;
2708 int i, j, n;
2710 n = base->getNComps();
2711 line = (Guchar *) gmallocn (length, n);
2712 for (i = 0; i < length; i++)
2713 for (j = 0; j < n; j++)
2714 line[i * n + j] = lookup[in[i] * n + j];
2716 base->getRGBXLine(line, out, length);
2718 gfree (line);
2721 void GfxIndexedColorSpace::getCMYKLine(Guchar *in, Guchar *out, int length) {
2722 Guchar *line;
2723 int i, j, n;
2725 n = base->getNComps();
2726 line = (Guchar *) gmallocn (length, n);
2727 for (i = 0; i < length; i++)
2728 for (j = 0; j < n; j++)
2729 line[i * n + j] = lookup[in[i] * n + j];
2731 base->getCMYKLine(line, out, length);
2733 gfree (line);
2736 void GfxIndexedColorSpace::getDeviceNLine(Guchar *in, Guchar *out, int length) {
2737 Guchar *line;
2738 int i, j, n;
2740 n = base->getNComps();
2741 line = (Guchar *) gmallocn (length, n);
2742 for (i = 0; i < length; i++)
2743 for (j = 0; j < n; j++)
2744 line[i * n + j] = lookup[in[i] * n + j];
2746 base->getDeviceNLine(line, out, length);
2748 gfree (line);
2751 void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2752 GfxColor color2;
2754 base->getCMYK(mapColorToBase(color, &color2), cmyk);
2757 void GfxIndexedColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2758 GfxColor color2;
2760 base->getDeviceN(mapColorToBase(color, &color2), deviceN);
2763 void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) {
2764 color->c[0] = 0;
2767 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow,
2768 double *decodeRange,
2769 int maxImgPixel) {
2770 decodeLow[0] = 0;
2771 decodeRange[0] = maxImgPixel;
2774 //------------------------------------------------------------------------
2775 // GfxSeparationColorSpace
2776 //------------------------------------------------------------------------
2778 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2779 GfxColorSpace *altA,
2780 Function *funcA) {
2781 name = nameA;
2782 alt = altA;
2783 func = funcA;
2784 nonMarking = !name->cmp("None");
2785 if (!name->cmp("Cyan")) {
2786 overprintMask = 0x01;
2787 } else if (!name->cmp("Magenta")) {
2788 overprintMask = 0x02;
2789 } else if (!name->cmp("Yellow")) {
2790 overprintMask = 0x04;
2791 } else if (!name->cmp("Black")) {
2792 overprintMask = 0x08;
2793 } else if (!name->cmp("All")) {
2794 overprintMask = 0xffffffff;
2798 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA,
2799 GfxColorSpace *altA,
2800 Function *funcA,
2801 GBool nonMarkingA,
2802 Guint overprintMaskA,
2803 int *mappingA) {
2804 name = nameA;
2805 alt = altA;
2806 func = funcA;
2807 nonMarking = nonMarkingA;
2808 overprintMask = overprintMaskA;
2809 mapping = mappingA;
2812 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
2813 delete name;
2814 delete alt;
2815 delete func;
2816 if (mapping != NULL)
2817 gfree(mapping);
2820 GfxColorSpace *GfxSeparationColorSpace::copy() {
2821 int *mappingA = NULL;
2822 if (mapping != NULL) {
2823 mappingA = (int *) gmalloc(sizeof(int));
2824 *mappingA = *mapping;
2826 return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy(),
2827 nonMarking, overprintMask, mappingA);
2830 //~ handle the 'All' and 'None' colorants
2831 GfxColorSpace *GfxSeparationColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
2832 GfxSeparationColorSpace *cs;
2833 GooString *nameA;
2834 GfxColorSpace *altA;
2835 Function *funcA;
2836 Object obj1;
2838 if (arr->getLength() != 4) {
2839 error(errSyntaxWarning, -1, "Bad Separation color space");
2840 goto err1;
2842 if (!arr->get(1, &obj1)->isName()) {
2843 error(errSyntaxWarning, -1, "Bad Separation color space (name)");
2844 goto err2;
2846 nameA = new GooString(obj1.getName());
2847 obj1.free();
2848 arr->get(2, &obj1);
2849 if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
2850 error(errSyntaxWarning, -1, "Bad Separation color space (alternate color space)");
2851 goto err3;
2853 obj1.free();
2854 arr->get(3, &obj1);
2855 if (!(funcA = Function::parse(&obj1))) {
2856 goto err4;
2858 if (funcA->getInputSize() != 1) {
2859 error(errSyntaxWarning, -1, "Bad SeparationColorSpace function");
2860 goto err5;
2862 obj1.free();
2863 cs = new GfxSeparationColorSpace(nameA, altA, funcA);
2864 return cs;
2866 err5:
2867 delete funcA;
2868 err4:
2869 delete altA;
2870 err3:
2871 delete nameA;
2872 err2:
2873 obj1.free();
2874 err1:
2875 return NULL;
2878 void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) {
2879 double x;
2880 double c[gfxColorMaxComps];
2881 GfxColor color2;
2882 int i;
2884 if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) {
2885 *gray = clip01(gfxColorComp1 - color->c[0]);
2886 } else {
2887 x = colToDbl(color->c[0]);
2888 func->transform(&x, c);
2889 for (i = 0; i < alt->getNComps(); ++i) {
2890 color2.c[i] = dblToCol(c[i]);
2892 alt->getGray(&color2, gray);
2896 void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
2897 double x;
2898 double c[gfxColorMaxComps];
2899 GfxColor color2;
2900 int i;
2902 if (alt->getMode() == csDeviceGray && name->cmp("Black") == 0) {
2903 rgb->r = clip01(gfxColorComp1 - color->c[0]);
2904 rgb->g = clip01(gfxColorComp1 - color->c[0]);
2905 rgb->b = clip01(gfxColorComp1 - color->c[0]);
2906 } else {
2907 x = colToDbl(color->c[0]);
2908 func->transform(&x, c);
2909 for (i = 0; i < alt->getNComps(); ++i) {
2910 color2.c[i] = dblToCol(c[i]);
2912 alt->getRGB(&color2, rgb);
2916 void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
2917 double x;
2918 double c[gfxColorMaxComps];
2919 GfxColor color2;
2920 int i;
2922 if (name->cmp("Black") == 0) {
2923 cmyk->c = 0;
2924 cmyk->m = 0;
2925 cmyk->y = 0;
2926 cmyk->k = color->c[0];
2927 } else if (name->cmp("Cyan") == 0) {
2928 cmyk->c = color->c[0];
2929 cmyk->m = 0;
2930 cmyk->y = 0;
2931 cmyk->k = 0;
2932 } else if (name->cmp("Magenta") == 0) {
2933 cmyk->c = 0;
2934 cmyk->m = color->c[0];
2935 cmyk->y = 0;
2936 cmyk->k = 0;
2937 } else if (name->cmp("Yellow") == 0) {
2938 cmyk->c = 0;
2939 cmyk->m = 0;
2940 cmyk->y = color->c[0];
2941 cmyk->k = 0;
2942 } else {
2943 x = colToDbl(color->c[0]);
2944 func->transform(&x, c);
2945 for (i = 0; i < alt->getNComps(); ++i) {
2946 color2.c[i] = dblToCol(c[i]);
2948 alt->getCMYK(&color2, cmyk);
2952 void GfxSeparationColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
2953 for (int i = 0; i < gfxColorMaxComps; i++)
2954 deviceN->c[i] = 0;
2955 if (mapping == NULL || mapping[0] == -1) {
2956 GfxCMYK cmyk;
2958 getCMYK(color, &cmyk);
2959 deviceN->c[0] = cmyk.c;
2960 deviceN->c[1] = cmyk.m;
2961 deviceN->c[2] = cmyk.y;
2962 deviceN->c[3] = cmyk.k;
2963 } else {
2964 deviceN->c[mapping[0]] = color->c[0];
2968 void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) {
2969 color->c[0] = gfxColorComp1;
2972 void GfxSeparationColorSpace::createMapping(GooList *separationList, int maxSepComps) {
2973 if (nonMarking)
2974 return;
2975 mapping = (int *)gmalloc(sizeof(int));
2976 switch (overprintMask) {
2977 case 0x01:
2978 *mapping = 0;
2979 break;
2980 case 0x02:
2981 *mapping = 1;
2982 break;
2983 case 0x04:
2984 *mapping = 2;
2985 break;
2986 case 0x08:
2987 *mapping = 3;
2988 break;
2989 default:
2990 Guint newOverprintMask = 0x10;
2991 for (int i = 0; i < separationList->getLength(); i++) {
2992 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(i);
2993 if (!sepCS->getName()->cmp(name)) {
2994 if (sepCS->getFunc()->hasDifferentResultSet(func)) {
2995 error(errSyntaxWarning, -1,
2996 "Different functions found for '{0:t}', convert immediately", name);
2997 gfree(mapping);
2998 mapping = NULL;
2999 return;
3001 *mapping = i+4;
3002 overprintMask = newOverprintMask;
3003 return;
3005 newOverprintMask <<=1;
3007 if (separationList->getLength() == maxSepComps) {
3008 error(errSyntaxWarning, -1,
3009 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, name);
3010 gfree(mapping);
3011 mapping = NULL;
3012 return;
3014 *mapping = separationList->getLength() + 4;
3015 separationList->append(copy());
3016 overprintMask = newOverprintMask;
3017 break;
3021 //------------------------------------------------------------------------
3022 // GfxDeviceNColorSpace
3023 //------------------------------------------------------------------------
3025 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
3026 GooString **namesA,
3027 GfxColorSpace *altA,
3028 Function *funcA,
3029 GooList *sepsCSA) {
3030 int i;
3032 nComps = nCompsA;
3033 alt = altA;
3034 func = funcA;
3035 sepsCS = sepsCSA;
3036 nonMarking = gTrue;
3037 overprintMask = 0;
3038 mapping = NULL;
3039 for (i = 0; i < nComps; ++i) {
3040 names[i] = namesA[i];
3041 if (names[i]->cmp("None")) {
3042 nonMarking = gFalse;
3044 if (!names[i]->cmp("Cyan")) {
3045 overprintMask |= 0x01;
3046 } else if (!names[i]->cmp("Magenta")) {
3047 overprintMask |= 0x02;
3048 } else if (!names[i]->cmp("Yellow")) {
3049 overprintMask |= 0x04;
3050 } else if (!names[i]->cmp("Black")) {
3051 overprintMask |= 0x08;
3052 } else if (!names[i]->cmp("All")) {
3053 overprintMask = 0xffffffff;
3054 } else {
3055 overprintMask = 0x0f;
3060 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA,
3061 GooString **namesA,
3062 GfxColorSpace *altA,
3063 Function *funcA,
3064 GooList *sepsCSA,
3065 int *mappingA,
3066 GBool nonMarkingA,
3067 Guint overprintMaskA) {
3068 int i;
3070 nComps = nCompsA;
3071 alt = altA;
3072 func = funcA;
3073 sepsCS = sepsCSA;
3074 mapping = mappingA;
3075 nonMarking = nonMarkingA;
3076 overprintMask = overprintMaskA;
3077 for (i = 0; i < nComps; ++i) {
3078 names[i] = namesA[i]->copy();
3082 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
3083 int i;
3085 for (i = 0; i < nComps; ++i) {
3086 delete names[i];
3088 delete alt;
3089 delete func;
3090 deleteGooList(sepsCS, GfxSeparationColorSpace);
3091 if (mapping != NULL)
3092 gfree(mapping);
3095 GfxColorSpace *GfxDeviceNColorSpace::copy() {
3096 int i;
3097 int *mappingA = NULL;
3099 GooList *sepsCSA = new GooList(sepsCS->getLength());
3100 for (i = 0; i < sepsCS->getLength(); i++) {
3101 GfxSeparationColorSpace *scs = (GfxSeparationColorSpace *) sepsCS->get(i);
3102 if (likely(scs != NULL)) {
3103 sepsCSA->append(scs->copy());
3106 if (mapping != NULL) {
3107 mappingA = (int *)gmalloc(sizeof(int) * nComps);
3108 for (i = 0; i < nComps; i++)
3109 mappingA[i] = mapping[i];
3111 return new GfxDeviceNColorSpace(nComps, names, alt->copy(), func->copy(),
3112 sepsCSA, mappingA, nonMarking, overprintMask);
3115 //~ handle the 'None' colorant
3116 GfxColorSpace *GfxDeviceNColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
3117 GfxDeviceNColorSpace *cs;
3118 int nCompsA;
3119 GooString *namesA[gfxColorMaxComps];
3120 GfxColorSpace *altA;
3121 Function *funcA;
3122 Object obj1, obj2;
3123 int i;
3124 GooList *separationList = new GooList();
3126 if (arr->getLength() != 4 && arr->getLength() != 5) {
3127 error(errSyntaxWarning, -1, "Bad DeviceN color space");
3128 goto err1;
3130 if (!arr->get(1, &obj1)->isArray()) {
3131 error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
3132 goto err2;
3134 nCompsA = obj1.arrayGetLength();
3135 if (nCompsA > gfxColorMaxComps) {
3136 error(errSyntaxWarning, -1, "DeviceN color space with too many ({0:d} > {1:d}) components",
3137 nCompsA, gfxColorMaxComps);
3138 nCompsA = gfxColorMaxComps;
3140 for (i = 0; i < nCompsA; ++i) {
3141 if (!obj1.arrayGet(i, &obj2)->isName()) {
3142 error(errSyntaxWarning, -1, "Bad DeviceN color space (names)");
3143 obj2.free();
3144 goto err2;
3146 namesA[i] = new GooString(obj2.getName());
3147 obj2.free();
3149 obj1.free();
3150 arr->get(2, &obj1);
3151 if (!(altA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
3152 error(errSyntaxWarning, -1, "Bad DeviceN color space (alternate color space)");
3153 goto err3;
3155 obj1.free();
3156 arr->get(3, &obj1);
3157 if (!(funcA = Function::parse(&obj1))) {
3158 goto err4;
3160 obj1.free();
3161 if (arr->getLength() == 5) {
3162 if (!arr->get(4, &obj1)->isDict()) {
3163 error(errSyntaxWarning, -1, "Bad DeviceN color space (attributes)");
3164 goto err4;
3166 Dict *attribs = obj1.getDict();
3167 attribs->lookup("Colorants", &obj2);
3168 if (obj2.isDict()) {
3169 Dict *colorants = obj2.getDict();
3170 for (i = 0; i < colorants->getLength(); i++) {
3171 Object obj3;
3172 colorants->getVal(i, &obj3);
3173 if (obj3.isArray()) {
3174 separationList->append(GfxSeparationColorSpace::parse(res, obj3.getArray(), out, state, recursion));
3175 } else {
3176 obj3.free();
3177 obj2.free();
3178 error(errSyntaxWarning, -1, "Bad DeviceN color space (colorant value entry is not an Array)");
3179 goto err4;
3181 obj3.free();
3184 obj2.free();
3185 obj1.free();
3187 cs = new GfxDeviceNColorSpace(nCompsA, namesA, altA, funcA, separationList);
3188 return cs;
3190 err4:
3191 delete altA;
3192 err3:
3193 for (i = 0; i < nCompsA; ++i) {
3194 delete namesA[i];
3196 err2:
3197 obj1.free();
3198 err1:
3199 delete separationList;
3200 return NULL;
3203 void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) {
3204 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3205 GfxColor color2;
3206 int i;
3208 for (i = 0; i < nComps; ++i) {
3209 x[i] = colToDbl(color->c[i]);
3211 func->transform(x, c);
3212 for (i = 0; i < alt->getNComps(); ++i) {
3213 color2.c[i] = dblToCol(c[i]);
3215 alt->getGray(&color2, gray);
3218 void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
3219 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3220 GfxColor color2;
3221 int i;
3223 for (i = 0; i < nComps; ++i) {
3224 x[i] = colToDbl(color->c[i]);
3226 func->transform(x, c);
3227 for (i = 0; i < alt->getNComps(); ++i) {
3228 color2.c[i] = dblToCol(c[i]);
3230 alt->getRGB(&color2, rgb);
3233 void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
3234 double x[gfxColorMaxComps], c[gfxColorMaxComps];
3235 GfxColor color2;
3236 int i;
3238 for (i = 0; i < nComps; ++i) {
3239 x[i] = colToDbl(color->c[i]);
3241 func->transform(x, c);
3242 for (i = 0; i < alt->getNComps(); ++i) {
3243 color2.c[i] = dblToCol(c[i]);
3245 alt->getCMYK(&color2, cmyk);
3248 void GfxDeviceNColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
3249 for (int i = 0; i < gfxColorMaxComps; i++)
3250 deviceN->c[i] = 0;
3251 if (mapping == NULL) {
3252 GfxCMYK cmyk;
3254 getCMYK(color, &cmyk);
3255 deviceN->c[0] = cmyk.c;
3256 deviceN->c[1] = cmyk.m;
3257 deviceN->c[2] = cmyk.y;
3258 deviceN->c[3] = cmyk.k;
3259 } else {
3260 for (int j = 0; j < nComps; j++)
3261 if (mapping[j] != -1)
3262 deviceN->c[mapping[j]] = color->c[j];
3266 void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) {
3267 int i;
3269 for (i = 0; i < nComps; ++i) {
3270 color->c[i] = gfxColorComp1;
3274 void GfxDeviceNColorSpace::createMapping(GooList *separationList, int maxSepComps) {
3275 if (nonMarking) // None
3276 return;
3277 mapping = (int *)gmalloc(sizeof(int) * nComps);
3278 Guint newOverprintMask = 0;
3279 for (int i = 0; i < nComps; i++) {
3280 if (!names[i]->cmp("None")) {
3281 mapping[i] = -1;
3282 } else if (!names[i]->cmp("Cyan")) {
3283 newOverprintMask |= 0x01;
3284 mapping[i] = 0;
3285 } else if (!names[i]->cmp("Magenta")) {
3286 newOverprintMask |= 0x02;
3287 mapping[i] = 1;
3288 } else if (!names[i]->cmp("Yellow")) {
3289 newOverprintMask |= 0x04;
3290 mapping[i] = 2;
3291 } else if (!names[i]->cmp("Black")) {
3292 newOverprintMask |= 0x08;
3293 mapping[i] = 3;
3294 } else {
3295 Guint startOverprintMask = 0x10;
3296 GBool found = gFalse;
3297 Function *sepFunc = NULL;
3298 if (nComps == 1)
3299 sepFunc = func;
3300 else {
3301 for (int k = 0; k < sepsCS->getLength(); k++) {
3302 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
3303 if (!sepCS->getName()->cmp(names[i])) {
3304 sepFunc = sepCS->getFunc();
3305 break;
3309 for (int j = 0; j < separationList->getLength(); j++) {
3310 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)separationList->get(j);
3311 if (!sepCS->getName()->cmp(names[i])) {
3312 if (sepFunc != NULL && sepCS->getFunc()->hasDifferentResultSet(sepFunc)) {
3313 error(errSyntaxWarning, -1,
3314 "Different functions found for '{0:t}', convert immediately", names[i]);
3315 gfree(mapping);
3316 mapping = NULL;
3317 overprintMask = 0xffffffff;
3318 return;
3320 mapping[i] = j+4;
3321 newOverprintMask |= startOverprintMask;
3322 found = gTrue;
3323 break;
3325 startOverprintMask <<=1;
3327 if (!found) {
3328 if (separationList->getLength() == maxSepComps) {
3329 error(errSyntaxWarning, -1,
3330 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps, names[i]);
3331 gfree(mapping);
3332 mapping = NULL;
3333 overprintMask = 0xffffffff;
3334 return;
3336 mapping[i] = separationList->getLength() + 4;
3337 newOverprintMask |= startOverprintMask;
3338 if (nComps == 1)
3339 separationList->append(new GfxSeparationColorSpace(names[i]->copy(),alt->copy(), func->copy()));
3340 else {
3341 for (int k = 0; k < sepsCS->getLength(); k++) {
3342 GfxSeparationColorSpace *sepCS = (GfxSeparationColorSpace *)sepsCS->get(k);
3343 if (!sepCS->getName()->cmp(names[i])) {
3344 found = gTrue;
3345 separationList->append(sepCS->copy());
3346 break;
3349 if(!found) {
3350 error(errSyntaxWarning, -1, "DeviceN has no suitable colorant");
3351 gfree(mapping);
3352 mapping = NULL;
3353 overprintMask = 0xffffffff;
3354 return;
3360 overprintMask = newOverprintMask;
3363 //------------------------------------------------------------------------
3364 // GfxPatternColorSpace
3365 //------------------------------------------------------------------------
3367 GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) {
3368 under = underA;
3371 GfxPatternColorSpace::~GfxPatternColorSpace() {
3372 if (under) {
3373 delete under;
3377 GfxColorSpace *GfxPatternColorSpace::copy() {
3378 return new GfxPatternColorSpace(under ? under->copy() :
3379 (GfxColorSpace *)NULL);
3382 GfxColorSpace *GfxPatternColorSpace::parse(GfxResources *res, Array *arr, OutputDev *out, GfxState *state, int recursion) {
3383 GfxPatternColorSpace *cs;
3384 GfxColorSpace *underA;
3385 Object obj1;
3387 if (arr->getLength() != 1 && arr->getLength() != 2) {
3388 error(errSyntaxWarning, -1, "Bad Pattern color space");
3389 return NULL;
3391 underA = NULL;
3392 if (arr->getLength() == 2) {
3393 arr->get(1, &obj1);
3394 if (!(underA = GfxColorSpace::parse(res, &obj1, out, state, recursion + 1))) {
3395 error(errSyntaxWarning, -1, "Bad Pattern color space (underlying color space)");
3396 obj1.free();
3397 return NULL;
3399 obj1.free();
3401 cs = new GfxPatternColorSpace(underA);
3402 return cs;
3405 void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) {
3406 *gray = 0;
3409 void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) {
3410 rgb->r = rgb->g = rgb->b = 0;
3413 void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) {
3414 cmyk->c = cmyk->m = cmyk->y = 0;
3415 cmyk->k = 1;
3418 void GfxPatternColorSpace::getDeviceN(GfxColor *color, GfxColor *deviceN) {
3419 for (int i = 0; i < gfxColorMaxComps; i++)
3420 deviceN->c[i] = 0;
3421 deviceN->c[3] = 1;
3424 void GfxPatternColorSpace::getDefaultColor(GfxColor *color) {
3425 color->c[0]=0;
3428 //------------------------------------------------------------------------
3429 // Pattern
3430 //------------------------------------------------------------------------
3432 GfxPattern::GfxPattern(int typeA) {
3433 type = typeA;
3436 GfxPattern::~GfxPattern() {
3439 GfxPattern *GfxPattern::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) {
3440 GfxPattern *pattern;
3441 Object obj1;
3443 if (obj->isDict()) {
3444 obj->dictLookup("PatternType", &obj1);
3445 } else if (obj->isStream()) {
3446 obj->streamGetDict()->lookup("PatternType", &obj1);
3447 } else {
3448 return NULL;
3450 pattern = NULL;
3451 if (obj1.isInt() && obj1.getInt() == 1) {
3452 pattern = GfxTilingPattern::parse(obj);
3453 } else if (obj1.isInt() && obj1.getInt() == 2) {
3454 pattern = GfxShadingPattern::parse(res, obj, out, state);
3456 obj1.free();
3457 return pattern;
3460 //------------------------------------------------------------------------
3461 // GfxTilingPattern
3462 //------------------------------------------------------------------------
3464 GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) {
3465 GfxTilingPattern *pat;
3466 Dict *dict;
3467 int paintTypeA, tilingTypeA;
3468 double bboxA[4], matrixA[6];
3469 double xStepA, yStepA;
3470 Object resDictA;
3471 Object obj1, obj2;
3472 int i;
3474 if (!patObj->isStream()) {
3475 return NULL;
3477 dict = patObj->streamGetDict();
3479 if (dict->lookup("PaintType", &obj1)->isInt()) {
3480 paintTypeA = obj1.getInt();
3481 } else {
3482 paintTypeA = 1;
3483 error(errSyntaxWarning, -1, "Invalid or missing PaintType in pattern");
3485 obj1.free();
3486 if (dict->lookup("TilingType", &obj1)->isInt()) {
3487 tilingTypeA = obj1.getInt();
3488 } else {
3489 tilingTypeA = 1;
3490 error(errSyntaxWarning, -1, "Invalid or missing TilingType in pattern");
3492 obj1.free();
3493 bboxA[0] = bboxA[1] = 0;
3494 bboxA[2] = bboxA[3] = 1;
3495 if (dict->lookup("BBox", &obj1)->isArray() &&
3496 obj1.arrayGetLength() == 4) {
3497 for (i = 0; i < 4; ++i) {
3498 if (obj1.arrayGet(i, &obj2)->isNum()) {
3499 bboxA[i] = obj2.getNum();
3501 obj2.free();
3503 } else {
3504 error(errSyntaxWarning, -1, "Invalid or missing BBox in pattern");
3506 obj1.free();
3507 if (dict->lookup("XStep", &obj1)->isNum()) {
3508 xStepA = obj1.getNum();
3509 } else {
3510 xStepA = 1;
3511 error(errSyntaxWarning, -1, "Invalid or missing XStep in pattern");
3513 obj1.free();
3514 if (dict->lookup("YStep", &obj1)->isNum()) {
3515 yStepA = obj1.getNum();
3516 } else {
3517 yStepA = 1;
3518 error(errSyntaxWarning, -1, "Invalid or missing YStep in pattern");
3520 obj1.free();
3521 if (!dict->lookup("Resources", &resDictA)->isDict()) {
3522 resDictA.free();
3523 resDictA.initNull();
3524 error(errSyntaxWarning, -1, "Invalid or missing Resources in pattern");
3526 matrixA[0] = 1; matrixA[1] = 0;
3527 matrixA[2] = 0; matrixA[3] = 1;
3528 matrixA[4] = 0; matrixA[5] = 0;
3529 if (dict->lookup("Matrix", &obj1)->isArray() &&
3530 obj1.arrayGetLength() == 6) {
3531 for (i = 0; i < 6; ++i) {
3532 if (obj1.arrayGet(i, &obj2)->isNum()) {
3533 matrixA[i] = obj2.getNum();
3535 obj2.free();
3538 obj1.free();
3540 pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA,
3541 &resDictA, matrixA, patObj);
3542 resDictA.free();
3543 return pat;
3546 GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA,
3547 double *bboxA, double xStepA, double yStepA,
3548 Object *resDictA, double *matrixA,
3549 Object *contentStreamA):
3550 GfxPattern(1)
3552 int i;
3554 paintType = paintTypeA;
3555 tilingType = tilingTypeA;
3556 for (i = 0; i < 4; ++i) {
3557 bbox[i] = bboxA[i];
3559 xStep = xStepA;
3560 yStep = yStepA;
3561 resDictA->copy(&resDict);
3562 for (i = 0; i < 6; ++i) {
3563 matrix[i] = matrixA[i];
3565 contentStreamA->copy(&contentStream);
3568 GfxTilingPattern::~GfxTilingPattern() {
3569 resDict.free();
3570 contentStream.free();
3573 GfxPattern *GfxTilingPattern::copy() {
3574 return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep,
3575 &resDict, matrix, &contentStream);
3578 //------------------------------------------------------------------------
3579 // GfxShadingPattern
3580 //------------------------------------------------------------------------
3582 GfxShadingPattern *GfxShadingPattern::parse(GfxResources *res, Object *patObj, OutputDev *out, GfxState *state) {
3583 Dict *dict;
3584 GfxShading *shadingA;
3585 double matrixA[6];
3586 Object obj1, obj2;
3587 int i;
3589 if (!patObj->isDict()) {
3590 return NULL;
3592 dict = patObj->getDict();
3594 dict->lookup("Shading", &obj1);
3595 shadingA = GfxShading::parse(res, &obj1, out, state);
3596 obj1.free();
3597 if (!shadingA) {
3598 return NULL;
3601 matrixA[0] = 1; matrixA[1] = 0;
3602 matrixA[2] = 0; matrixA[3] = 1;
3603 matrixA[4] = 0; matrixA[5] = 0;
3604 if (dict->lookup("Matrix", &obj1)->isArray() &&
3605 obj1.arrayGetLength() == 6) {
3606 for (i = 0; i < 6; ++i) {
3607 if (obj1.arrayGet(i, &obj2)->isNum()) {
3608 matrixA[i] = obj2.getNum();
3610 obj2.free();
3613 obj1.free();
3615 return new GfxShadingPattern(shadingA, matrixA);
3618 GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA):
3619 GfxPattern(2)
3621 int i;
3623 shading = shadingA;
3624 for (i = 0; i < 6; ++i) {
3625 matrix[i] = matrixA[i];
3629 GfxShadingPattern::~GfxShadingPattern() {
3630 delete shading;
3633 GfxPattern *GfxShadingPattern::copy() {
3634 return new GfxShadingPattern(shading->copy(), matrix);
3637 //------------------------------------------------------------------------
3638 // GfxShading
3639 //------------------------------------------------------------------------
3641 GfxShading::GfxShading(int typeA) {
3642 type = typeA;
3643 colorSpace = NULL;
3646 GfxShading::GfxShading(GfxShading *shading) {
3647 int i;
3649 type = shading->type;
3650 colorSpace = shading->colorSpace->copy();
3651 for (i = 0; i < gfxColorMaxComps; ++i) {
3652 background.c[i] = shading->background.c[i];
3654 hasBackground = shading->hasBackground;
3655 xMin = shading->xMin;
3656 yMin = shading->yMin;
3657 xMax = shading->xMax;
3658 yMax = shading->yMax;
3659 hasBBox = shading->hasBBox;
3662 GfxShading::~GfxShading() {
3663 if (colorSpace) {
3664 delete colorSpace;
3668 GfxShading *GfxShading::parse(GfxResources *res, Object *obj, OutputDev *out, GfxState *state) {
3669 GfxShading *shading;
3670 Dict *dict;
3671 int typeA;
3672 Object obj1;
3674 if (obj->isDict()) {
3675 dict = obj->getDict();
3676 } else if (obj->isStream()) {
3677 dict = obj->streamGetDict();
3678 } else {
3679 return NULL;
3682 if (!dict->lookup("ShadingType", &obj1)->isInt()) {
3683 error(errSyntaxWarning, -1, "Invalid ShadingType in shading dictionary");
3684 obj1.free();
3685 return NULL;
3687 typeA = obj1.getInt();
3688 obj1.free();
3690 switch (typeA) {
3691 case 1:
3692 shading = GfxFunctionShading::parse(res, dict, out, state);
3693 break;
3694 case 2:
3695 shading = GfxAxialShading::parse(res, dict, out, state);
3696 break;
3697 case 3:
3698 shading = GfxRadialShading::parse(res, dict, out, state);
3699 break;
3700 case 4:
3701 if (obj->isStream()) {
3702 shading = GfxGouraudTriangleShading::parse(res, 4, dict, obj->getStream(), out, state);
3703 } else {
3704 error(errSyntaxWarning, -1, "Invalid Type 4 shading object");
3705 goto err1;
3707 break;
3708 case 5:
3709 if (obj->isStream()) {
3710 shading = GfxGouraudTriangleShading::parse(res, 5, dict, obj->getStream(), out, state);
3711 } else {
3712 error(errSyntaxWarning, -1, "Invalid Type 5 shading object");
3713 goto err1;
3715 break;
3716 case 6:
3717 if (obj->isStream()) {
3718 shading = GfxPatchMeshShading::parse(res, 6, dict, obj->getStream(), out, state);
3719 } else {
3720 error(errSyntaxWarning, -1, "Invalid Type 6 shading object");
3721 goto err1;
3723 break;
3724 case 7:
3725 if (obj->isStream()) {
3726 shading = GfxPatchMeshShading::parse(res, 7, dict, obj->getStream(), out, state);
3727 } else {
3728 error(errSyntaxWarning, -1, "Invalid Type 7 shading object");
3729 goto err1;
3731 break;
3732 default:
3733 error(errSyntaxWarning, -1, "Unimplemented shading type {0:d}", typeA);
3734 goto err1;
3737 return shading;
3739 err1:
3740 return NULL;
3743 GBool GfxShading::init(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
3744 Object obj1, obj2;
3745 int i;
3747 dict->lookup("ColorSpace", &obj1);
3748 if (!(colorSpace = GfxColorSpace::parse(res, &obj1, out, state))) {
3749 error(errSyntaxWarning, -1, "Bad color space in shading dictionary");
3750 obj1.free();
3751 return gFalse;
3753 obj1.free();
3755 for (i = 0; i < gfxColorMaxComps; ++i) {
3756 background.c[i] = 0;
3758 hasBackground = gFalse;
3759 if (dict->lookup("Background", &obj1)->isArray()) {
3760 if (obj1.arrayGetLength() == colorSpace->getNComps()) {
3761 hasBackground = gTrue;
3762 for (i = 0; i < colorSpace->getNComps(); ++i) {
3763 background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum());
3764 obj2.free();
3766 } else {
3767 error(errSyntaxWarning, -1, "Bad Background in shading dictionary");
3770 obj1.free();
3772 xMin = yMin = xMax = yMax = 0;
3773 hasBBox = gFalse;
3774 if (dict->lookup("BBox", &obj1)->isArray()) {
3775 if (obj1.arrayGetLength() == 4) {
3776 Object obj3, obj4, obj5;
3777 obj1.arrayGet(0, &obj2);
3778 obj1.arrayGet(1, &obj3);
3779 obj1.arrayGet(2, &obj4);
3780 obj1.arrayGet(3, &obj5);
3781 if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum())
3783 hasBBox = gTrue;
3784 xMin = obj2.getNum();
3785 yMin = obj3.getNum();
3786 xMax = obj4.getNum();
3787 yMax = obj5.getNum();
3788 } else {
3789 error(errSyntaxWarning, -1, "Bad BBox in shading dictionary (Values not numbers)");
3791 obj2.free();
3792 obj3.free();
3793 obj4.free();
3794 obj5.free();
3795 } else {
3796 error(errSyntaxWarning, -1, "Bad BBox in shading dictionary");
3799 obj1.free();
3801 return gTrue;
3804 //------------------------------------------------------------------------
3805 // GfxFunctionShading
3806 //------------------------------------------------------------------------
3808 GfxFunctionShading::GfxFunctionShading(double x0A, double y0A,
3809 double x1A, double y1A,
3810 double *matrixA,
3811 Function **funcsA, int nFuncsA):
3812 GfxShading(1)
3814 int i;
3816 x0 = x0A;
3817 y0 = y0A;
3818 x1 = x1A;
3819 y1 = y1A;
3820 for (i = 0; i < 6; ++i) {
3821 matrix[i] = matrixA[i];
3823 nFuncs = nFuncsA;
3824 for (i = 0; i < nFuncs; ++i) {
3825 funcs[i] = funcsA[i];
3829 GfxFunctionShading::GfxFunctionShading(GfxFunctionShading *shading):
3830 GfxShading(shading)
3832 int i;
3834 x0 = shading->x0;
3835 y0 = shading->y0;
3836 x1 = shading->x1;
3837 y1 = shading->y1;
3838 for (i = 0; i < 6; ++i) {
3839 matrix[i] = shading->matrix[i];
3841 nFuncs = shading->nFuncs;
3842 for (i = 0; i < nFuncs; ++i) {
3843 funcs[i] = shading->funcs[i]->copy();
3847 GfxFunctionShading::~GfxFunctionShading() {
3848 int i;
3850 for (i = 0; i < nFuncs; ++i) {
3851 delete funcs[i];
3855 GfxFunctionShading *GfxFunctionShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
3856 GfxFunctionShading *shading;
3857 double x0A, y0A, x1A, y1A;
3858 double matrixA[6];
3859 Function *funcsA[gfxColorMaxComps];
3860 int nFuncsA;
3861 Object obj1, obj2;
3862 int i;
3864 x0A = y0A = 0;
3865 x1A = y1A = 1;
3866 if (dict->lookup("Domain", &obj1)->isArray() &&
3867 obj1.arrayGetLength() == 4) {
3868 x0A = obj1.arrayGet(0, &obj2)->getNum();
3869 obj2.free();
3870 x1A = obj1.arrayGet(1, &obj2)->getNum();
3871 obj2.free();
3872 y0A = obj1.arrayGet(2, &obj2)->getNum();
3873 obj2.free();
3874 y1A = obj1.arrayGet(3, &obj2)->getNum();
3875 obj2.free();
3877 obj1.free();
3879 matrixA[0] = 1; matrixA[1] = 0;
3880 matrixA[2] = 0; matrixA[3] = 1;
3881 matrixA[4] = 0; matrixA[5] = 0;
3882 if (dict->lookup("Matrix", &obj1)->isArray() &&
3883 obj1.arrayGetLength() == 6) {
3884 matrixA[0] = obj1.arrayGet(0, &obj2)->getNum();
3885 obj2.free();
3886 matrixA[1] = obj1.arrayGet(1, &obj2)->getNum();
3887 obj2.free();
3888 matrixA[2] = obj1.arrayGet(2, &obj2)->getNum();
3889 obj2.free();
3890 matrixA[3] = obj1.arrayGet(3, &obj2)->getNum();
3891 obj2.free();
3892 matrixA[4] = obj1.arrayGet(4, &obj2)->getNum();
3893 obj2.free();
3894 matrixA[5] = obj1.arrayGet(5, &obj2)->getNum();
3895 obj2.free();
3897 obj1.free();
3899 dict->lookup("Function", &obj1);
3900 if (obj1.isArray()) {
3901 nFuncsA = obj1.arrayGetLength();
3902 if (nFuncsA > gfxColorMaxComps || nFuncsA <= 0) {
3903 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
3904 goto err1;
3906 for (i = 0; i < nFuncsA; ++i) {
3907 obj1.arrayGet(i, &obj2);
3908 if (!(funcsA[i] = Function::parse(&obj2))) {
3909 goto err2;
3911 obj2.free();
3913 } else {
3914 nFuncsA = 1;
3915 if (!(funcsA[0] = Function::parse(&obj1))) {
3916 goto err1;
3919 obj1.free();
3921 shading = new GfxFunctionShading(x0A, y0A, x1A, y1A, matrixA,
3922 funcsA, nFuncsA);
3923 if (!shading->init(res, dict, out, state)) {
3924 delete shading;
3925 return NULL;
3927 return shading;
3929 err2:
3930 obj2.free();
3931 err1:
3932 obj1.free();
3933 return NULL;
3936 GfxShading *GfxFunctionShading::copy() {
3937 return new GfxFunctionShading(this);
3940 void GfxFunctionShading::getColor(double x, double y, GfxColor *color) {
3941 double in[2], out[gfxColorMaxComps];
3942 int i;
3944 // NB: there can be one function with n outputs or n functions with
3945 // one output each (where n = number of color components)
3946 for (i = 0; i < gfxColorMaxComps; ++i) {
3947 out[i] = 0;
3949 in[0] = x;
3950 in[1] = y;
3951 for (i = 0; i < nFuncs; ++i) {
3952 funcs[i]->transform(in, &out[i]);
3954 for (i = 0; i < gfxColorMaxComps; ++i) {
3955 color->c[i] = dblToCol(out[i]);
3959 //------------------------------------------------------------------------
3960 // GfxUnivariateShading
3961 //------------------------------------------------------------------------
3963 GfxUnivariateShading::GfxUnivariateShading(int typeA,
3964 double t0A, double t1A,
3965 Function **funcsA, int nFuncsA,
3966 GBool extend0A, GBool extend1A):
3967 GfxShading(typeA)
3969 int i;
3971 t0 = t0A;
3972 t1 = t1A;
3973 nFuncs = nFuncsA;
3974 for (i = 0; i < nFuncs; ++i) {
3975 funcs[i] = funcsA[i];
3977 extend0 = extend0A;
3978 extend1 = extend1A;
3980 cacheSize = 0;
3981 lastMatch = 0;
3982 cacheBounds = NULL;
3983 cacheCoeff = NULL;
3984 cacheValues = NULL;
3987 GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading *shading):
3988 GfxShading(shading)
3990 int i;
3992 t0 = shading->t0;
3993 t1 = shading->t1;
3994 nFuncs = shading->nFuncs;
3995 for (i = 0; i < nFuncs; ++i) {
3996 funcs[i] = shading->funcs[i]->copy();
3998 extend0 = shading->extend0;
3999 extend1 = shading->extend1;
4001 cacheSize = 0;
4002 lastMatch = 0;
4003 cacheBounds = NULL;
4004 cacheCoeff = NULL;
4005 cacheValues = NULL;
4008 GfxUnivariateShading::~GfxUnivariateShading() {
4009 int i;
4011 for (i = 0; i < nFuncs; ++i) {
4012 delete funcs[i];
4015 gfree (cacheBounds);
4018 void GfxUnivariateShading::getColor(double t, GfxColor *color) {
4019 double out[gfxColorMaxComps];
4020 int i, nComps;
4022 // NB: there can be one function with n outputs or n functions with
4023 // one output each (where n = number of color components)
4024 nComps = nFuncs * funcs[0]->getOutputSize();
4026 if (cacheSize > 0) {
4027 double x, ix, *l, *u, *upper;
4029 if (cacheBounds[lastMatch - 1] >= t) {
4030 upper = std::lower_bound (cacheBounds, cacheBounds + lastMatch - 1, t);
4031 lastMatch = upper - cacheBounds;
4032 lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
4033 } else if (cacheBounds[lastMatch] < t) {
4034 upper = std::lower_bound (cacheBounds + lastMatch + 1, cacheBounds + cacheSize, t);
4035 lastMatch = upper - cacheBounds;
4036 lastMatch = std::min<int>(std::max<int>(1, lastMatch), cacheSize - 1);
4039 x = (t - cacheBounds[lastMatch-1]) * cacheCoeff[lastMatch];
4040 ix = 1.0 - x;
4041 u = cacheValues + lastMatch * nComps;
4042 l = u - nComps;
4044 for (i = 0; i < nComps; ++i) {
4045 out[i] = ix * l[i] + x * u[i];
4047 } else {
4048 for (i = 0; i < nComps; ++i) {
4049 out[i] = 0;
4051 for (i = 0; i < nFuncs; ++i) {
4052 if (funcs[i]->getInputSize() != 1) {
4053 error(errSyntaxWarning, -1, "Invalid shading function (input != 1)");
4054 break;
4056 funcs[i]->transform(&t, &out[i]);
4060 for (i = 0; i < nComps; ++i) {
4061 color->c[i] = dblToCol(out[i]);
4065 void GfxUnivariateShading::setupCache(const Matrix *ctm,
4066 double xMin, double yMin,
4067 double xMax, double yMax) {
4068 double sMin, sMax, tMin, tMax, upperBound;
4069 int i, j, nComps, maxSize;
4071 gfree (cacheBounds);
4072 cacheBounds = NULL;
4073 cacheSize = 0;
4075 // NB: there can be one function with n outputs or n functions with
4076 // one output each (where n = number of color components)
4077 nComps = nFuncs * funcs[0]->getOutputSize();
4079 getParameterRange(&sMin, &sMax, xMin, yMin, xMax, yMax);
4080 upperBound = ctm->norm() * getDistance(sMin, sMax);
4081 maxSize = ceil(upperBound);
4082 maxSize = std::max<int>(maxSize, 2);
4085 double x[4], y[4];
4087 ctm->transform(xMin, yMin, &x[0], &y[0]);
4088 ctm->transform(xMax, yMin, &x[1], &y[1]);
4089 ctm->transform(xMin, yMax, &x[2], &y[2]);
4090 ctm->transform(xMax, yMax, &x[3], &y[3]);
4092 xMin = xMax = x[0];
4093 yMin = yMax = y[0];
4094 for (i = 1; i < 4; i++) {
4095 xMin = std::min<double>(xMin, x[i]);
4096 yMin = std::min<double>(yMin, y[i]);
4097 xMax = std::max<double>(xMax, x[i]);
4098 yMax = std::max<double>(yMax, y[i]);
4102 if (maxSize > (xMax-xMin) * (yMax-yMin)) {
4103 return;
4106 if (t0 < t1) {
4107 tMin = t0 + sMin * (t1 - t0);
4108 tMax = t0 + sMax * (t1 - t0);
4109 } else {
4110 tMin = t0 + sMax * (t1 - t0);
4111 tMax = t0 + sMin * (t1 - t0);
4114 cacheBounds = (double *)gmallocn(maxSize, sizeof(double) * (nComps + 2));
4115 cacheCoeff = cacheBounds + maxSize;
4116 cacheValues = cacheCoeff + maxSize;
4118 if (cacheSize != 0) {
4119 for (j = 0; j < cacheSize; ++j) {
4120 cacheCoeff[j] = 1 / (cacheBounds[j+1] - cacheBounds[j]);
4122 } else if (tMax != tMin) {
4123 double step = (tMax - tMin) / (maxSize - 1);
4124 double coeff = (maxSize - 1) / (tMax - tMin);
4126 cacheSize = maxSize;
4128 for (j = 0; j < cacheSize; ++j) {
4129 cacheBounds[j] = tMin + j * step;
4130 cacheCoeff[j] = coeff;
4132 for (i = 0; i < nComps; ++i) {
4133 cacheValues[j*nComps + i] = 0;
4135 for (i = 0; i < nFuncs; ++i) {
4136 funcs[i]->transform(&cacheBounds[j], &cacheValues[j*nComps + i]);
4141 lastMatch = 1;
4145 //------------------------------------------------------------------------
4146 // GfxAxialShading
4147 //------------------------------------------------------------------------
4149 GfxAxialShading::GfxAxialShading(double x0A, double y0A,
4150 double x1A, double y1A,
4151 double t0A, double t1A,
4152 Function **funcsA, int nFuncsA,
4153 GBool extend0A, GBool extend1A):
4154 GfxUnivariateShading(2, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
4156 x0 = x0A;
4157 y0 = y0A;
4158 x1 = x1A;
4159 y1 = y1A;
4162 GfxAxialShading::GfxAxialShading(GfxAxialShading *shading):
4163 GfxUnivariateShading(shading)
4165 x0 = shading->x0;
4166 y0 = shading->y0;
4167 x1 = shading->x1;
4168 y1 = shading->y1;
4171 GfxAxialShading::~GfxAxialShading() {
4174 GfxAxialShading *GfxAxialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
4175 GfxAxialShading *shading;
4176 double x0A, y0A, x1A, y1A;
4177 double t0A, t1A;
4178 Function *funcsA[gfxColorMaxComps];
4179 int nFuncsA;
4180 GBool extend0A, extend1A;
4181 Object obj1, obj2;
4182 int i;
4184 x0A = y0A = x1A = y1A = 0;
4185 if (dict->lookup("Coords", &obj1)->isArray() &&
4186 obj1.arrayGetLength() == 4) {
4187 Object obj3, obj4, obj5;
4188 obj1.arrayGet(0, &obj2);
4189 obj1.arrayGet(1, &obj3);
4190 obj1.arrayGet(2, &obj4);
4191 obj1.arrayGet(3, &obj5);
4192 if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum()) {
4193 x0A = obj2.getNum();
4194 y0A = obj3.getNum();
4195 x1A = obj4.getNum();
4196 y1A = obj5.getNum();
4198 obj2.free();
4199 obj3.free();
4200 obj4.free();
4201 obj5.free();
4202 } else {
4203 error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
4204 goto err1;
4206 obj1.free();
4208 t0A = 0;
4209 t1A = 1;
4210 if (dict->lookup("Domain", &obj1)->isArray() &&
4211 obj1.arrayGetLength() == 2) {
4212 Object obj3;
4213 obj1.arrayGet(0, &obj2);
4214 obj1.arrayGet(1, &obj3);
4215 if (obj2.isNum() && obj3.isNum()) {
4216 t0A = obj2.getNum();
4217 t1A = obj3.getNum();
4219 obj2.free();
4220 obj3.free();
4222 obj1.free();
4224 dict->lookup("Function", &obj1);
4225 if (obj1.isArray()) {
4226 nFuncsA = obj1.arrayGetLength();
4227 if (nFuncsA > gfxColorMaxComps || nFuncsA == 0) {
4228 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4229 goto err1;
4231 for (i = 0; i < nFuncsA; ++i) {
4232 obj1.arrayGet(i, &obj2);
4233 if (!(funcsA[i] = Function::parse(&obj2))) {
4234 obj1.free();
4235 obj2.free();
4236 goto err1;
4238 obj2.free();
4240 } else {
4241 nFuncsA = 1;
4242 if (!(funcsA[0] = Function::parse(&obj1))) {
4243 obj1.free();
4244 goto err1;
4247 obj1.free();
4249 extend0A = extend1A = gFalse;
4250 if (dict->lookup("Extend", &obj1)->isArray() &&
4251 obj1.arrayGetLength() == 2) {
4252 obj1.arrayGet(0, &obj2);
4253 if (obj2.isBool()) {
4254 extend0A = obj2.getBool();
4255 } else {
4256 error(errSyntaxWarning, -1, "Invalid axial shading extend (0)");
4258 obj2.free();
4259 obj1.arrayGet(1, &obj2);
4260 if (obj2.isBool()) {
4261 extend1A = obj2.getBool();
4262 } else {
4263 error(errSyntaxWarning, -1, "Invalid axial shading extend (1)");
4265 obj2.free();
4267 obj1.free();
4269 shading = new GfxAxialShading(x0A, y0A, x1A, y1A, t0A, t1A,
4270 funcsA, nFuncsA, extend0A, extend1A);
4271 if (!shading->init(res, dict, out, state)) {
4272 delete shading;
4273 return NULL;
4275 return shading;
4277 err1:
4278 return NULL;
4281 GfxShading *GfxAxialShading::copy() {
4282 return new GfxAxialShading(this);
4285 double GfxAxialShading::getDistance(double sMin, double sMax) {
4286 double xMin, yMin, xMax, yMax;
4288 xMin = x0 + sMin * (x1 - x0);
4289 yMin = y0 + sMin * (y1 - y0);
4290 xMax = x0 + sMax * (x1 - x0);
4291 yMax = y0 + sMax * (y1 - y0);
4293 return hypot(xMax-xMin, yMax-yMin);
4296 void GfxAxialShading::getParameterRange(double *lower, double *upper,
4297 double xMin, double yMin,
4298 double xMax, double yMax) {
4299 double pdx, pdy, invsqnorm, tdx, tdy, t, range[2];
4301 // Linear gradients are orthogonal to the line passing through their
4302 // extremes. Because of convexity, the parameter range can be
4303 // computed as the convex hull (one the real line) of the parameter
4304 // values of the 4 corners of the box.
4306 // The parameter value t for a point (x,y) can be computed as:
4308 // t = (p2 - p1) . (x,y) / |p2 - p1|^2
4310 // t0 is the t value for the top left corner
4311 // tdx is the difference between left and right corners
4312 // tdy is the difference between top and bottom corners
4314 pdx = x1 - x0;
4315 pdy = y1 - y0;
4316 invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
4317 pdx *= invsqnorm;
4318 pdy *= invsqnorm;
4320 t = (xMin - x0) * pdx + (yMin - y0) * pdy;
4321 tdx = (xMax - xMin) * pdx;
4322 tdy = (yMax - yMin) * pdy;
4324 // Because of the linearity of the t value, tdx can simply be added
4325 // the t0 to move along the top edge. After this, *lower and *upper
4326 // represent the parameter range for the top edge, so extending it
4327 // to include the whole box simply requires adding tdy to the
4328 // correct extreme.
4330 range[0] = range[1] = t;
4331 if (tdx < 0)
4332 range[0] += tdx;
4333 else
4334 range[1] += tdx;
4336 if (tdy < 0)
4337 range[0] += tdy;
4338 else
4339 range[1] += tdy;
4341 *lower = std::max<double>(0., std::min<double>(1., range[0]));
4342 *upper = std::max<double>(0., std::min<double>(1., range[1]));
4345 //------------------------------------------------------------------------
4346 // GfxRadialShading
4347 //------------------------------------------------------------------------
4349 #ifndef RADIAL_EPSILON
4350 #define RADIAL_EPSILON (1. / 1024 / 1024)
4351 #endif
4353 GfxRadialShading::GfxRadialShading(double x0A, double y0A, double r0A,
4354 double x1A, double y1A, double r1A,
4355 double t0A, double t1A,
4356 Function **funcsA, int nFuncsA,
4357 GBool extend0A, GBool extend1A):
4358 GfxUnivariateShading(3, t0A, t1A, funcsA, nFuncsA, extend0A, extend1A)
4360 x0 = x0A;
4361 y0 = y0A;
4362 r0 = r0A;
4363 x1 = x1A;
4364 y1 = y1A;
4365 r1 = r1A;
4368 GfxRadialShading::GfxRadialShading(GfxRadialShading *shading):
4369 GfxUnivariateShading(shading)
4371 x0 = shading->x0;
4372 y0 = shading->y0;
4373 r0 = shading->r0;
4374 x1 = shading->x1;
4375 y1 = shading->y1;
4376 r1 = shading->r1;
4379 GfxRadialShading::~GfxRadialShading() {
4382 GfxRadialShading *GfxRadialShading::parse(GfxResources *res, Dict *dict, OutputDev *out, GfxState *state) {
4383 GfxRadialShading *shading;
4384 double x0A, y0A, r0A, x1A, y1A, r1A;
4385 double t0A, t1A;
4386 Function *funcsA[gfxColorMaxComps];
4387 int nFuncsA;
4388 GBool extend0A, extend1A;
4389 Object obj1, obj2;
4390 int i;
4392 x0A = y0A = r0A = x1A = y1A = r1A = 0;
4393 if (dict->lookup("Coords", &obj1)->isArray() &&
4394 obj1.arrayGetLength() == 6) {
4395 x0A = obj1.arrayGet(0, &obj2)->getNum();
4396 obj2.free();
4397 y0A = obj1.arrayGet(1, &obj2)->getNum();
4398 obj2.free();
4399 r0A = obj1.arrayGet(2, &obj2)->getNum();
4400 obj2.free();
4401 x1A = obj1.arrayGet(3, &obj2)->getNum();
4402 obj2.free();
4403 y1A = obj1.arrayGet(4, &obj2)->getNum();
4404 obj2.free();
4405 r1A = obj1.arrayGet(5, &obj2)->getNum();
4406 obj2.free();
4407 } else {
4408 error(errSyntaxWarning, -1, "Missing or invalid Coords in shading dictionary");
4409 goto err1;
4411 obj1.free();
4413 t0A = 0;
4414 t1A = 1;
4415 if (dict->lookup("Domain", &obj1)->isArray() &&
4416 obj1.arrayGetLength() == 2) {
4417 t0A = obj1.arrayGet(0, &obj2)->getNum();
4418 obj2.free();
4419 t1A = obj1.arrayGet(1, &obj2)->getNum();
4420 obj2.free();
4422 obj1.free();
4424 dict->lookup("Function", &obj1);
4425 if (obj1.isArray()) {
4426 nFuncsA = obj1.arrayGetLength();
4427 if (nFuncsA > gfxColorMaxComps) {
4428 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4429 goto err1;
4431 for (i = 0; i < nFuncsA; ++i) {
4432 obj1.arrayGet(i, &obj2);
4433 if (!(funcsA[i] = Function::parse(&obj2))) {
4434 obj1.free();
4435 obj2.free();
4436 goto err1;
4438 obj2.free();
4440 } else {
4441 nFuncsA = 1;
4442 if (!(funcsA[0] = Function::parse(&obj1))) {
4443 obj1.free();
4444 goto err1;
4447 obj1.free();
4449 extend0A = extend1A = gFalse;
4450 if (dict->lookup("Extend", &obj1)->isArray() &&
4451 obj1.arrayGetLength() == 2) {
4452 extend0A = obj1.arrayGet(0, &obj2)->getBool();
4453 obj2.free();
4454 extend1A = obj1.arrayGet(1, &obj2)->getBool();
4455 obj2.free();
4457 obj1.free();
4459 shading = new GfxRadialShading(x0A, y0A, r0A, x1A, y1A, r1A, t0A, t1A,
4460 funcsA, nFuncsA, extend0A, extend1A);
4461 if (!shading->init(res, dict, out, state)) {
4462 delete shading;
4463 return NULL;
4465 return shading;
4467 err1:
4468 return NULL;
4471 GfxShading *GfxRadialShading::copy() {
4472 return new GfxRadialShading(this);
4475 double GfxRadialShading::getDistance(double sMin, double sMax) {
4476 double xMin, yMin, rMin, xMax, yMax, rMax;
4478 xMin = x0 + sMin * (x1 - x0);
4479 yMin = y0 + sMin * (y1 - y0);
4480 rMin = r0 + sMin * (r1 - r0);
4482 xMax = x0 + sMax * (x1 - x0);
4483 yMax = y0 + sMax * (y1 - y0);
4484 rMax = r0 + sMax * (r1 - r0);
4486 return hypot(xMax-xMin, yMax-yMin) + fabs(rMax-rMin);
4489 // extend range, adapted from cairo, radialExtendRange
4490 static GBool
4491 radialExtendRange (double range[2], double value, GBool valid)
4493 if (!valid)
4494 range[0] = range[1] = value;
4495 else if (value < range[0])
4496 range[0] = value;
4497 else if (value > range[1])
4498 range[1] = value;
4500 return gTrue;
4503 inline void radialEdge(double num, double den, double delta, double lower, double upper,
4504 double dr, double mindr, GBool &valid, double *range)
4506 if (fabs (den) >= RADIAL_EPSILON) {
4507 double t_edge, v;
4508 t_edge = (num) / (den);
4509 v = t_edge * (delta);
4510 if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))
4511 valid = radialExtendRange (range, t_edge, valid);
4515 inline void radialCorner1(double x, double y, double &b, double dx, double dy, double cr,
4516 double dr, double mindr, GBool &valid, double *range)
4518 b = (x) * dx + (y) * dy + cr * dr;
4519 if (fabs (b) >= RADIAL_EPSILON) {
4520 double t_corner;
4521 double x2 = (x) * (x);
4522 double y2 = (y) * (y);
4523 double cr2 = (cr) * (cr);
4524 double c = x2 + y2 - cr2;
4526 t_corner = 0.5 * c / b;
4527 if (t_corner * dr >= mindr)
4528 valid = radialExtendRange (range, t_corner, valid);
4532 inline void radialCorner2(double x, double y, double a, double &b, double &c, double &d, double dx, double dy, double cr,
4533 double inva, double dr, double mindr, GBool &valid, double *range)
4535 b = (x) * dx + (y) * dy + cr * dr;
4536 c = (x) * (x) + (y) * (y) - cr * cr;
4537 d = b * b - a * c;
4538 if (d >= 0) {
4539 double t_corner;
4541 d = sqrt (d);
4542 t_corner = (b + d) * inva;
4543 if (t_corner * dr >= mindr)
4544 valid = radialExtendRange (range, t_corner, valid);
4545 t_corner = (b - d) * inva;
4546 if (t_corner * dr >= mindr)
4547 valid = radialExtendRange (range, t_corner, valid);
4550 void GfxRadialShading::getParameterRange(double *lower, double *upper,
4551 double xMin, double yMin,
4552 double xMax, double yMax) {
4553 double cx, cy, cr, dx, dy, dr;
4554 double a, x_focus, y_focus;
4555 double mindr, minx, miny, maxx, maxy;
4556 double range[2];
4557 GBool valid;
4559 // A radial pattern is considered degenerate if it can be
4560 // represented as a solid or clear pattern. This corresponds to one
4561 // of the two cases:
4563 // 1) The radii are both very small:
4564 // |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
4566 // 2) The two circles have about the same radius and are very
4567 // close to each other (approximately a cylinder gradient that
4568 // doesn't move with the parameter):
4569 // |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
4571 if (xMin >= xMax || yMin >=yMax ||
4572 (fabs (r0 - r1) < RADIAL_EPSILON &&
4573 (std::min<double>(r0, r1) < RADIAL_EPSILON ||
4574 std::max<double>(fabs (x0 - x1), fabs (y0 - y1)) < 2 * RADIAL_EPSILON))) {
4575 *lower = *upper = 0;
4576 return;
4579 range[0] = range[1] = 0;
4580 valid = gFalse;
4582 x_focus = y_focus = 0; // silence gcc
4584 cx = x0;
4585 cy = y0;
4586 cr = r0;
4587 dx = x1 - cx;
4588 dy = y1 - cy;
4589 dr = r1 - cr;
4591 // translate by -(cx, cy) to simplify computations
4592 xMin -= cx;
4593 yMin -= cy;
4594 xMax -= cx;
4595 yMax -= cy;
4597 // enlarge boundaries slightly to avoid rounding problems in the
4598 // parameter range computation
4599 xMin -= RADIAL_EPSILON;
4600 yMin -= RADIAL_EPSILON;
4601 xMax += RADIAL_EPSILON;
4602 yMax += RADIAL_EPSILON;
4604 // enlarge boundaries even more to avoid rounding problems when
4605 // testing if a point belongs to the box
4606 minx = xMin - RADIAL_EPSILON;
4607 miny = yMin - RADIAL_EPSILON;
4608 maxx = xMax + RADIAL_EPSILON;
4609 maxy = yMax + RADIAL_EPSILON;
4611 // we dont' allow negative radiuses, so we will be checking that
4612 // t*dr >= mindr to consider t valid
4613 mindr = -(cr + RADIAL_EPSILON);
4615 // After the previous transformations, the start circle is centered
4616 // in the origin and has radius cr. A 1-unit change in the t
4617 // parameter corresponds to dx,dy,dr changes in the x,y,r of the
4618 // circle (center coordinates, radius).
4620 // To compute the minimum range needed to correctly draw the
4621 // pattern, we start with an empty range and extend it to include
4622 // the circles touching the bounding box or within it.
4624 // Focus, the point where the circle has radius == 0.
4626 // r = cr + t * dr = 0
4627 // t = -cr / dr
4629 // If the radius is constant (dr == 0) there is no focus (the
4630 // gradient represents a cylinder instead of a cone).
4631 if (fabs (dr) >= RADIAL_EPSILON) {
4632 double t_focus;
4634 t_focus = -cr / dr;
4635 x_focus = t_focus * dx;
4636 y_focus = t_focus * dy;
4637 if (minx <= x_focus && x_focus <= maxx &&
4638 miny <= y_focus && y_focus <= maxy)
4640 valid = radialExtendRange (range, t_focus, valid);
4644 // Circles externally tangent to box edges.
4646 // All circles have center in (dx, dy) * t
4648 // If the circle is tangent to the line defined by the edge of the
4649 // box, then at least one of the following holds true:
4651 // (dx*t) + (cr + dr*t) == x0 (left edge)
4652 // (dx*t) - (cr + dr*t) == x1 (right edge)
4653 // (dy*t) + (cr + dr*t) == y0 (top edge)
4654 // (dy*t) - (cr + dr*t) == y1 (bottom edge)
4656 // The solution is only valid if the tangent point is actually on
4657 // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
4658 // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
4660 // For the first equation:
4662 // (dx + dr) * t = x0 - cr
4663 // t = (x0 - cr) / (dx + dr)
4664 // y = dy * t
4666 // in the code this becomes:
4668 // t_edge = (num) / (den)
4669 // v = (delta) * t_edge
4671 // If the denominator in t is 0, the pattern is tangent to a line
4672 // parallel to the edge under examination. The corner-case where the
4673 // boundary line is the same as the edge is handled by the focus
4674 // point case and/or by the a==0 case.
4676 // circles tangent (externally) to left/right/top/bottom edge
4677 radialEdge(xMin - cr, dx + dr, dy, miny, maxy, dr, mindr, valid, range);
4678 radialEdge(xMax + cr, dx - dr, dy, miny, maxy, dr, mindr, valid, range);
4679 radialEdge(yMin - cr, dy + dr, dx, minx, maxx, dr, mindr, valid, range);
4680 radialEdge(yMax + cr, dy - dr, dx, minx, maxx, dr, mindr, valid, range);
4682 // Circles passing through a corner.
4684 // A circle passing through the point (x,y) satisfies:
4686 // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
4688 // If we set:
4689 // a = dx^2 + dy^2 - dr^2
4690 // b = x*dx + y*dy + cr*dr
4691 // c = x^2 + y^2 - cr^2
4692 // we have:
4693 // a*t^2 - 2*b*t + c == 0
4695 a = dx * dx + dy * dy - dr * dr;
4696 if (fabs (a) < RADIAL_EPSILON * RADIAL_EPSILON) {
4697 double b;
4699 // Ensure that gradients with both a and dr small are
4700 // considered degenerate.
4701 // The floating point version of the degeneracy test implemented
4702 // in _radial_pattern_is_degenerate() is:
4704 // 1) The circles are practically the same size:
4705 // |dr| < RADIAL_EPSILON
4706 // AND
4707 // 2a) The circles are both very small:
4708 // min (r0, r1) < RADIAL_EPSILON
4709 // OR
4710 // 2b) The circles are very close to each other:
4711 // max (|dx|, |dy|) < 2 * RADIAL_EPSILON
4713 // Assuming that the gradient is not degenerate, we want to
4714 // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
4716 // If the gradient is not degenerate yet it has |dr| <
4717 // RADIAL_EPSILON, (2b) is false, thus:
4719 // max (|dx|, |dy|) >= 2*RADIAL_EPSILON
4720 // which implies:
4721 // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
4723 // From the definition of a, we get:
4724 // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
4725 // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
4726 // 3*RADIAL_EPSILON^2 < dr^2
4728 // which is inconsistent with the hypotheses, thus |dr| <
4729 // RADIAL_EPSILON is false or the gradient is degenerate.
4731 assert (fabs (dr) >= RADIAL_EPSILON);
4733 // If a == 0, all the circles are tangent to a line in the
4734 // focus point. If this line is within the box extents, we
4735 // should add the circle with infinite radius, but this would
4736 // make the range unbounded. We will be limiting the range to
4737 // [0,1] anyway, so we simply add the biggest legitimate
4738 // circle (it happens for 0 or for 1).
4739 if (dr < 0) {
4740 valid = radialExtendRange (range, 0, valid);
4741 } else {
4742 valid = radialExtendRange (range, 1, valid);
4745 // Nondegenerate, nonlimit circles passing through the corners.
4747 // a == 0 && a*t^2 - 2*b*t + c == 0
4749 // t = c / (2*b)
4751 // The b == 0 case has just been handled, so we only have to
4752 // compute this if b != 0.
4754 // circles touching each corner
4755 radialCorner1(xMin, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4756 radialCorner1(xMin, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4757 radialCorner1(xMax, yMin, b, dx, dy, cr, dr, mindr, valid, range);
4758 radialCorner1(xMax, yMax, b, dx, dy, cr, dr, mindr, valid, range);
4759 } else {
4760 double inva, b, c, d;
4762 inva = 1 / a;
4764 // Nondegenerate, nonlimit circles passing through the corners.
4766 // a != 0 && a*t^2 - 2*b*t + c == 0
4768 // t = (b +- sqrt (b*b - a*c)) / a
4770 // If the argument of sqrt() is negative, then no circle
4771 // passes through the corner.
4773 // circles touching each corner
4774 radialCorner2(xMin, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4775 radialCorner2(xMin, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4776 radialCorner2(xMax, yMin, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4777 radialCorner2(xMax, yMax, a, b, c, d, dx, dy, cr, inva, dr, mindr, valid, range);
4780 *lower = std::max<double>(0., std::min<double>(1., range[0]));
4781 *upper = std::max<double>(0., std::min<double>(1., range[1]));
4784 //------------------------------------------------------------------------
4785 // GfxShadingBitBuf
4786 //------------------------------------------------------------------------
4788 class GfxShadingBitBuf {
4789 public:
4791 GfxShadingBitBuf(Stream *strA);
4792 ~GfxShadingBitBuf();
4793 GBool getBits(int n, Guint *val);
4794 void flushBits();
4796 private:
4798 Stream *str;
4799 int bitBuf;
4800 int nBits;
4803 GfxShadingBitBuf::GfxShadingBitBuf(Stream *strA) {
4804 str = strA;
4805 str->reset();
4806 bitBuf = 0;
4807 nBits = 0;
4810 GfxShadingBitBuf::~GfxShadingBitBuf() {
4811 str->close();
4814 GBool GfxShadingBitBuf::getBits(int n, Guint *val) {
4815 int x;
4817 if (nBits >= n) {
4818 x = (bitBuf >> (nBits - n)) & ((1 << n) - 1);
4819 nBits -= n;
4820 } else {
4821 x = 0;
4822 if (nBits > 0) {
4823 x = bitBuf & ((1 << nBits) - 1);
4824 n -= nBits;
4825 nBits = 0;
4827 while (n > 0) {
4828 if ((bitBuf = str->getChar()) == EOF) {
4829 nBits = 0;
4830 return gFalse;
4832 if (n >= 8) {
4833 x = (x << 8) | bitBuf;
4834 n -= 8;
4835 } else {
4836 x = (x << n) | (bitBuf >> (8 - n));
4837 nBits = 8 - n;
4838 n = 0;
4842 *val = x;
4843 return gTrue;
4846 void GfxShadingBitBuf::flushBits() {
4847 bitBuf = 0;
4848 nBits = 0;
4851 //------------------------------------------------------------------------
4852 // GfxGouraudTriangleShading
4853 //------------------------------------------------------------------------
4855 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4856 int typeA,
4857 GfxGouraudVertex *verticesA, int nVerticesA,
4858 int (*trianglesA)[3], int nTrianglesA,
4859 Function **funcsA, int nFuncsA):
4860 GfxShading(typeA)
4862 int i;
4864 vertices = verticesA;
4865 nVertices = nVerticesA;
4866 triangles = trianglesA;
4867 nTriangles = nTrianglesA;
4868 nFuncs = nFuncsA;
4869 for (i = 0; i < nFuncs; ++i) {
4870 funcs[i] = funcsA[i];
4874 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4875 GfxGouraudTriangleShading *shading):
4876 GfxShading(shading)
4878 int i;
4880 nVertices = shading->nVertices;
4881 vertices = (GfxGouraudVertex *)gmallocn(nVertices, sizeof(GfxGouraudVertex));
4882 memcpy(vertices, shading->vertices, nVertices * sizeof(GfxGouraudVertex));
4883 nTriangles = shading->nTriangles;
4884 triangles = (int (*)[3])gmallocn(nTriangles * 3, sizeof(int));
4885 memcpy(triangles, shading->triangles, nTriangles * 3 * sizeof(int));
4886 nFuncs = shading->nFuncs;
4887 for (i = 0; i < nFuncs; ++i) {
4888 funcs[i] = shading->funcs[i]->copy();
4892 GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
4893 int i;
4895 gfree(vertices);
4896 gfree(triangles);
4897 for (i = 0; i < nFuncs; ++i) {
4898 delete funcs[i];
4902 GfxGouraudTriangleShading *GfxGouraudTriangleShading::parse(GfxResources *res, int typeA,
4903 Dict *dict,
4904 Stream *str,
4905 OutputDev *out, GfxState *gfxState) {
4906 GfxGouraudTriangleShading *shading;
4907 Function *funcsA[gfxColorMaxComps];
4908 int nFuncsA;
4909 int coordBits, compBits, flagBits, vertsPerRow, nRows;
4910 double xMin, xMax, yMin, yMax;
4911 double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
4912 double xMul, yMul;
4913 double cMul[gfxColorMaxComps];
4914 GfxGouraudVertex *verticesA;
4915 int (*trianglesA)[3];
4916 int nComps, nVerticesA, nTrianglesA, vertSize, triSize;
4917 Guint x, y, flag;
4918 Guint c[gfxColorMaxComps];
4919 GfxShadingBitBuf *bitBuf;
4920 Object obj1, obj2;
4921 int i, j, k, state;
4923 if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
4924 coordBits = obj1.getInt();
4925 } else {
4926 error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4927 goto err2;
4929 obj1.free();
4930 if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
4931 compBits = obj1.getInt();
4932 } else {
4933 error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4934 goto err2;
4936 obj1.free();
4937 flagBits = vertsPerRow = 0; // make gcc happy
4938 if (typeA == 4) {
4939 if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
4940 flagBits = obj1.getInt();
4941 } else {
4942 error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4943 goto err2;
4945 obj1.free();
4946 } else {
4947 if (dict->lookup("VerticesPerRow", &obj1)->isInt()) {
4948 vertsPerRow = obj1.getInt();
4949 } else {
4950 error(errSyntaxWarning, -1, "Missing or invalid VerticesPerRow in shading dictionary");
4951 goto err2;
4953 obj1.free();
4955 if (dict->lookup("Decode", &obj1)->isArray() &&
4956 obj1.arrayGetLength() >= 6) {
4957 xMin = obj1.arrayGet(0, &obj2)->getNum();
4958 obj2.free();
4959 xMax = obj1.arrayGet(1, &obj2)->getNum();
4960 obj2.free();
4961 xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
4962 yMin = obj1.arrayGet(2, &obj2)->getNum();
4963 obj2.free();
4964 yMax = obj1.arrayGet(3, &obj2)->getNum();
4965 obj2.free();
4966 yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
4967 for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
4968 cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
4969 obj2.free();
4970 cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
4971 obj2.free();
4972 cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
4974 nComps = i;
4975 } else {
4976 error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
4977 goto err2;
4979 obj1.free();
4981 if (!dict->lookup("Function", &obj1)->isNull()) {
4982 if (obj1.isArray()) {
4983 nFuncsA = obj1.arrayGetLength();
4984 if (nFuncsA > gfxColorMaxComps) {
4985 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
4986 goto err1;
4988 for (i = 0; i < nFuncsA; ++i) {
4989 obj1.arrayGet(i, &obj2);
4990 if (!(funcsA[i] = Function::parse(&obj2))) {
4991 obj1.free();
4992 obj2.free();
4993 goto err1;
4995 obj2.free();
4997 } else {
4998 nFuncsA = 1;
4999 if (!(funcsA[0] = Function::parse(&obj1))) {
5000 obj1.free();
5001 goto err1;
5004 } else {
5005 nFuncsA = 0;
5007 obj1.free();
5009 nVerticesA = nTrianglesA = 0;
5010 verticesA = NULL;
5011 trianglesA = NULL;
5012 vertSize = triSize = 0;
5013 state = 0;
5014 flag = 0; // make gcc happy
5015 bitBuf = new GfxShadingBitBuf(str);
5016 while (1) {
5017 if (typeA == 4) {
5018 if (!bitBuf->getBits(flagBits, &flag)) {
5019 break;
5022 if (!bitBuf->getBits(coordBits, &x) ||
5023 !bitBuf->getBits(coordBits, &y)) {
5024 break;
5026 for (i = 0; i < nComps; ++i) {
5027 if (!bitBuf->getBits(compBits, &c[i])) {
5028 break;
5031 if (i < nComps) {
5032 break;
5034 if (nVerticesA == vertSize) {
5035 int oldVertSize = vertSize;
5036 vertSize = (vertSize == 0) ? 16 : 2 * vertSize;
5037 verticesA = (GfxGouraudVertex *)
5038 greallocn(verticesA, vertSize, sizeof(GfxGouraudVertex));
5039 memset(verticesA + oldVertSize, 0, (vertSize - oldVertSize) * sizeof(GfxGouraudVertex));
5041 verticesA[nVerticesA].x = xMin + xMul * (double)x;
5042 verticesA[nVerticesA].y = yMin + yMul * (double)y;
5043 for (i = 0; i < nComps; ++i) {
5044 verticesA[nVerticesA].color.c[i] =
5045 dblToCol(cMin[i] + cMul[i] * (double)c[i]);
5047 ++nVerticesA;
5048 bitBuf->flushBits();
5049 if (typeA == 4) {
5050 if (state == 0 || state == 1) {
5051 ++state;
5052 } else if (state == 2 || flag > 0) {
5053 if (nTrianglesA == triSize) {
5054 triSize = (triSize == 0) ? 16 : 2 * triSize;
5055 trianglesA = (int (*)[3])
5056 greallocn(trianglesA, triSize * 3, sizeof(int));
5058 if (state == 2) {
5059 trianglesA[nTrianglesA][0] = nVerticesA - 3;
5060 trianglesA[nTrianglesA][1] = nVerticesA - 2;
5061 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5062 ++state;
5063 } else if (flag == 1) {
5064 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][1];
5065 trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
5066 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5067 } else { // flag == 2
5068 trianglesA[nTrianglesA][0] = trianglesA[nTrianglesA - 1][0];
5069 trianglesA[nTrianglesA][1] = trianglesA[nTrianglesA - 1][2];
5070 trianglesA[nTrianglesA][2] = nVerticesA - 1;
5072 ++nTrianglesA;
5073 } else { // state == 3 && flag == 0
5074 state = 1;
5078 delete bitBuf;
5079 if (typeA == 5 && nVerticesA > 0) {
5080 nRows = nVerticesA / vertsPerRow;
5081 nTrianglesA = (nRows - 1) * 2 * (vertsPerRow - 1);
5082 trianglesA = (int (*)[3])gmallocn(nTrianglesA * 3, sizeof(int));
5083 k = 0;
5084 for (i = 0; i < nRows - 1; ++i) {
5085 for (j = 0; j < vertsPerRow - 1; ++j) {
5086 trianglesA[k][0] = i * vertsPerRow + j;
5087 trianglesA[k][1] = i * vertsPerRow + j+1;
5088 trianglesA[k][2] = (i+1) * vertsPerRow + j;
5089 ++k;
5090 trianglesA[k][0] = i * vertsPerRow + j+1;
5091 trianglesA[k][1] = (i+1) * vertsPerRow + j;
5092 trianglesA[k][2] = (i+1) * vertsPerRow + j+1;
5093 ++k;
5098 shading = new GfxGouraudTriangleShading(typeA, verticesA, nVerticesA,
5099 trianglesA, nTrianglesA,
5100 funcsA, nFuncsA);
5101 if (!shading->init(res, dict, out, gfxState)) {
5102 delete shading;
5103 return NULL;
5105 return shading;
5107 err2:
5108 obj1.free();
5109 err1:
5110 return NULL;
5113 GfxShading *GfxGouraudTriangleShading::copy() {
5114 return new GfxGouraudTriangleShading(this);
5117 void GfxGouraudTriangleShading::getTriangle(
5118 int i,
5119 double *x0, double *y0, GfxColor *color0,
5120 double *x1, double *y1, GfxColor *color1,
5121 double *x2, double *y2, GfxColor *color2) {
5122 double in;
5123 double out[gfxColorMaxComps];
5124 int v, j;
5126 assert(!isParameterized());
5128 v = triangles[i][0];
5129 *x0 = vertices[v].x;
5130 *y0 = vertices[v].y;
5131 if (nFuncs > 0) {
5132 in = colToDbl(vertices[v].color.c[0]);
5133 for (j = 0; j < nFuncs; ++j) {
5134 funcs[j]->transform(&in, &out[j]);
5136 for (j = 0; j < gfxColorMaxComps; ++j) {
5137 color0->c[j] = dblToCol(out[j]);
5139 } else {
5140 *color0 = vertices[v].color;
5142 v = triangles[i][1];
5143 *x1 = vertices[v].x;
5144 *y1 = vertices[v].y;
5145 if (nFuncs > 0) {
5146 in = colToDbl(vertices[v].color.c[0]);
5147 for (j = 0; j < nFuncs; ++j) {
5148 funcs[j]->transform(&in, &out[j]);
5150 for (j = 0; j < gfxColorMaxComps; ++j) {
5151 color1->c[j] = dblToCol(out[j]);
5153 } else {
5154 *color1 = vertices[v].color;
5156 v = triangles[i][2];
5157 *x2 = vertices[v].x;
5158 *y2 = vertices[v].y;
5159 if (nFuncs > 0) {
5160 in = colToDbl(vertices[v].color.c[0]);
5161 for (j = 0; j < nFuncs; ++j) {
5162 funcs[j]->transform(&in, &out[j]);
5164 for (j = 0; j < gfxColorMaxComps; ++j) {
5165 color2->c[j] = dblToCol(out[j]);
5167 } else {
5168 *color2 = vertices[v].color;
5172 void GfxGouraudTriangleShading::getParameterizedColor(double t, GfxColor *color) {
5173 double out[gfxColorMaxComps];
5175 for (int j = 0; j < nFuncs; ++j) {
5176 funcs[j]->transform(&t, &out[j]);
5178 for (int j = 0; j < gfxColorMaxComps; ++j) {
5179 color->c[j] = dblToCol(out[j]);
5183 void GfxGouraudTriangleShading::getTriangle(int i,
5184 double *x0, double *y0, double *color0,
5185 double *x1, double *y1, double *color1,
5186 double *x2, double *y2, double *color2) {
5187 int v;
5189 assert(isParameterized());
5191 v = triangles[i][0];
5192 if (likely(v >= 0 && v < nVertices)) {
5193 *x0 = vertices[v].x;
5194 *y0 = vertices[v].y;
5195 *color0 = colToDbl(vertices[v].color.c[0]);
5197 v = triangles[i][1];
5198 if (likely(v >= 0 && v < nVertices)) {
5199 *x1 = vertices[v].x;
5200 *y1 = vertices[v].y;
5201 *color1 = colToDbl(vertices[v].color.c[0]);
5203 v = triangles[i][2];
5204 if (likely(v >= 0 && v < nVertices)) {
5205 *x2 = vertices[v].x;
5206 *y2 = vertices[v].y;
5207 *color2 = colToDbl(vertices[v].color.c[0]);
5211 //------------------------------------------------------------------------
5212 // GfxPatchMeshShading
5213 //------------------------------------------------------------------------
5215 GfxPatchMeshShading::GfxPatchMeshShading(int typeA,
5216 GfxPatch *patchesA, int nPatchesA,
5217 Function **funcsA, int nFuncsA):
5218 GfxShading(typeA)
5220 int i;
5222 patches = patchesA;
5223 nPatches = nPatchesA;
5224 nFuncs = nFuncsA;
5225 for (i = 0; i < nFuncs; ++i) {
5226 funcs[i] = funcsA[i];
5230 GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading *shading):
5231 GfxShading(shading)
5233 int i;
5235 nPatches = shading->nPatches;
5236 patches = (GfxPatch *)gmallocn(nPatches, sizeof(GfxPatch));
5237 memcpy(patches, shading->patches, nPatches * sizeof(GfxPatch));
5238 nFuncs = shading->nFuncs;
5239 for (i = 0; i < nFuncs; ++i) {
5240 funcs[i] = shading->funcs[i]->copy();
5244 GfxPatchMeshShading::~GfxPatchMeshShading() {
5245 int i;
5247 gfree(patches);
5248 for (i = 0; i < nFuncs; ++i) {
5249 delete funcs[i];
5253 GfxPatchMeshShading *GfxPatchMeshShading::parse(GfxResources *res, int typeA, Dict *dict,
5254 Stream *str, OutputDev *out, GfxState *state) {
5255 GfxPatchMeshShading *shading;
5256 Function *funcsA[gfxColorMaxComps];
5257 int nFuncsA;
5258 int coordBits, compBits, flagBits;
5259 double xMin, xMax, yMin, yMax;
5260 double cMin[gfxColorMaxComps], cMax[gfxColorMaxComps];
5261 double xMul, yMul;
5262 double cMul[gfxColorMaxComps];
5263 GfxPatch *patchesA, *p;
5264 int nComps, nPatchesA, patchesSize, nPts, nColors;
5265 Guint flag;
5266 double x[16], y[16];
5267 Guint xi, yi;
5268 double c[4][gfxColorMaxComps];
5269 Guint ci;
5270 GfxShadingBitBuf *bitBuf;
5271 Object obj1, obj2;
5272 int i, j;
5274 if (dict->lookup("BitsPerCoordinate", &obj1)->isInt()) {
5275 coordBits = obj1.getInt();
5276 } else {
5277 error(errSyntaxWarning, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
5278 goto err2;
5280 obj1.free();
5281 if (dict->lookup("BitsPerComponent", &obj1)->isInt()) {
5282 compBits = obj1.getInt();
5283 } else {
5284 error(errSyntaxWarning, -1, "Missing or invalid BitsPerComponent in shading dictionary");
5285 goto err2;
5287 obj1.free();
5288 if (dict->lookup("BitsPerFlag", &obj1)->isInt()) {
5289 flagBits = obj1.getInt();
5290 } else {
5291 error(errSyntaxWarning, -1, "Missing or invalid BitsPerFlag in shading dictionary");
5292 goto err2;
5294 obj1.free();
5295 if (dict->lookup("Decode", &obj1)->isArray() &&
5296 obj1.arrayGetLength() >= 6) {
5297 xMin = obj1.arrayGet(0, &obj2)->getNum();
5298 obj2.free();
5299 xMax = obj1.arrayGet(1, &obj2)->getNum();
5300 obj2.free();
5301 xMul = (xMax - xMin) / (pow(2.0, coordBits) - 1);
5302 yMin = obj1.arrayGet(2, &obj2)->getNum();
5303 obj2.free();
5304 yMax = obj1.arrayGet(3, &obj2)->getNum();
5305 obj2.free();
5306 yMul = (yMax - yMin) / (pow(2.0, coordBits) - 1);
5307 for (i = 0; 5 + 2*i < obj1.arrayGetLength() && i < gfxColorMaxComps; ++i) {
5308 cMin[i] = obj1.arrayGet(4 + 2*i, &obj2)->getNum();
5309 obj2.free();
5310 cMax[i] = obj1.arrayGet(5 + 2*i, &obj2)->getNum();
5311 obj2.free();
5312 cMul[i] = (cMax[i] - cMin[i]) / (double)((1 << compBits) - 1);
5314 nComps = i;
5315 } else {
5316 error(errSyntaxWarning, -1, "Missing or invalid Decode array in shading dictionary");
5317 goto err2;
5319 obj1.free();
5321 if (!dict->lookup("Function", &obj1)->isNull()) {
5322 if (obj1.isArray()) {
5323 nFuncsA = obj1.arrayGetLength();
5324 if (nFuncsA > gfxColorMaxComps) {
5325 error(errSyntaxWarning, -1, "Invalid Function array in shading dictionary");
5326 goto err1;
5328 for (i = 0; i < nFuncsA; ++i) {
5329 obj1.arrayGet(i, &obj2);
5330 if (!(funcsA[i] = Function::parse(&obj2))) {
5331 obj1.free();
5332 obj2.free();
5333 goto err1;
5335 obj2.free();
5337 } else {
5338 nFuncsA = 1;
5339 if (!(funcsA[0] = Function::parse(&obj1))) {
5340 obj1.free();
5341 goto err1;
5344 } else {
5345 nFuncsA = 0;
5347 obj1.free();
5349 nPatchesA = 0;
5350 patchesA = NULL;
5351 patchesSize = 0;
5352 bitBuf = new GfxShadingBitBuf(str);
5353 while (1) {
5354 if (!bitBuf->getBits(flagBits, &flag)) {
5355 break;
5357 if (typeA == 6) {
5358 switch (flag) {
5359 case 0: nPts = 12; nColors = 4; break;
5360 case 1:
5361 case 2:
5362 case 3:
5363 default: nPts = 8; nColors = 2; break;
5365 } else {
5366 switch (flag) {
5367 case 0: nPts = 16; nColors = 4; break;
5368 case 1:
5369 case 2:
5370 case 3:
5371 default: nPts = 12; nColors = 2; break;
5374 for (i = 0; i < nPts; ++i) {
5375 if (!bitBuf->getBits(coordBits, &xi) ||
5376 !bitBuf->getBits(coordBits, &yi)) {
5377 break;
5379 x[i] = xMin + xMul * (double)xi;
5380 y[i] = yMin + yMul * (double)yi;
5382 if (i < nPts) {
5383 break;
5385 for (i = 0; i < nColors; ++i) {
5386 for (j = 0; j < nComps; ++j) {
5387 if (!bitBuf->getBits(compBits, &ci)) {
5388 break;
5390 c[i][j] = cMin[j] + cMul[j] * (double)ci;
5391 if( nFuncsA == 0 ) {
5392 // ... and colorspace values can also be stored into doubles.
5393 // They will be casted later.
5394 c[i][j] = dblToCol(c[i][j]);
5397 if (j < nComps) {
5398 break;
5401 if (i < nColors) {
5402 break;
5404 if (nPatchesA == patchesSize) {
5405 int oldPatchesSize = patchesSize;
5406 patchesSize = (patchesSize == 0) ? 16 : 2 * patchesSize;
5407 patchesA = (GfxPatch *)greallocn(patchesA,
5408 patchesSize, sizeof(GfxPatch));
5409 memset(patchesA + oldPatchesSize, 0, (patchesSize - oldPatchesSize) * sizeof(GfxPatch));
5411 p = &patchesA[nPatchesA];
5412 if (typeA == 6) {
5413 switch (flag) {
5414 case 0:
5415 p->x[0][0] = x[0];
5416 p->y[0][0] = y[0];
5417 p->x[0][1] = x[1];
5418 p->y[0][1] = y[1];
5419 p->x[0][2] = x[2];
5420 p->y[0][2] = y[2];
5421 p->x[0][3] = x[3];
5422 p->y[0][3] = y[3];
5423 p->x[1][3] = x[4];
5424 p->y[1][3] = y[4];
5425 p->x[2][3] = x[5];
5426 p->y[2][3] = y[5];
5427 p->x[3][3] = x[6];
5428 p->y[3][3] = y[6];
5429 p->x[3][2] = x[7];
5430 p->y[3][2] = y[7];
5431 p->x[3][1] = x[8];
5432 p->y[3][1] = y[8];
5433 p->x[3][0] = x[9];
5434 p->y[3][0] = y[9];
5435 p->x[2][0] = x[10];
5436 p->y[2][0] = y[10];
5437 p->x[1][0] = x[11];
5438 p->y[1][0] = y[11];
5439 for (j = 0; j < nComps; ++j) {
5440 p->color[0][0].c[j] = c[0][j];
5441 p->color[0][1].c[j] = c[1][j];
5442 p->color[1][1].c[j] = c[2][j];
5443 p->color[1][0].c[j] = c[3][j];
5445 break;
5446 case 1:
5447 if (nPatchesA == 0) {
5448 goto err1;
5450 p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
5451 p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
5452 p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
5453 p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
5454 p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
5455 p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
5456 p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
5457 p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
5458 p->x[1][3] = x[0];
5459 p->y[1][3] = y[0];
5460 p->x[2][3] = x[1];
5461 p->y[2][3] = y[1];
5462 p->x[3][3] = x[2];
5463 p->y[3][3] = y[2];
5464 p->x[3][2] = x[3];
5465 p->y[3][2] = y[3];
5466 p->x[3][1] = x[4];
5467 p->y[3][1] = y[4];
5468 p->x[3][0] = x[5];
5469 p->y[3][0] = y[5];
5470 p->x[2][0] = x[6];
5471 p->y[2][0] = y[6];
5472 p->x[1][0] = x[7];
5473 p->y[1][0] = y[7];
5474 for (j = 0; j < nComps; ++j) {
5475 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
5476 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5477 p->color[1][1].c[j] = c[0][j];
5478 p->color[1][0].c[j] = c[1][j];
5480 break;
5481 case 2:
5482 if (nPatchesA == 0) {
5483 goto err1;
5485 p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
5486 p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
5487 p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
5488 p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
5489 p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
5490 p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
5491 p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
5492 p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
5493 p->x[1][3] = x[0];
5494 p->y[1][3] = y[0];
5495 p->x[2][3] = x[1];
5496 p->y[2][3] = y[1];
5497 p->x[3][3] = x[2];
5498 p->y[3][3] = y[2];
5499 p->x[3][2] = x[3];
5500 p->y[3][2] = y[3];
5501 p->x[3][1] = x[4];
5502 p->y[3][1] = y[4];
5503 p->x[3][0] = x[5];
5504 p->y[3][0] = y[5];
5505 p->x[2][0] = x[6];
5506 p->y[2][0] = y[6];
5507 p->x[1][0] = x[7];
5508 p->y[1][0] = y[7];
5509 for (j = 0; j < nComps; ++j) {
5510 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5511 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5512 p->color[1][1].c[j] = c[0][j];
5513 p->color[1][0].c[j] = c[1][j];
5515 break;
5516 case 3:
5517 if (nPatchesA == 0) {
5518 goto err1;
5520 p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
5521 p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
5522 p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
5523 p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
5524 p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
5525 p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
5526 p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
5527 p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
5528 p->x[1][3] = x[0];
5529 p->y[1][3] = y[0];
5530 p->x[2][3] = x[1];
5531 p->y[2][3] = y[1];
5532 p->x[3][3] = x[2];
5533 p->y[3][3] = y[2];
5534 p->x[3][2] = x[3];
5535 p->y[3][2] = y[3];
5536 p->x[3][1] = x[4];
5537 p->y[3][1] = y[4];
5538 p->x[3][0] = x[5];
5539 p->y[3][0] = y[5];
5540 p->x[2][0] = x[6];
5541 p->y[2][0] = y[6];
5542 p->x[1][0] = x[7];
5543 p->y[1][0] = y[7];
5544 for (j = 0; j < nComps; ++j) {
5545 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5546 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
5547 p->color[1][1].c[j] = c[0][j];
5548 p->color[1][0].c[j] = c[1][j];
5550 break;
5552 } else {
5553 switch (flag) {
5554 case 0:
5555 p->x[0][0] = x[0];
5556 p->y[0][0] = y[0];
5557 p->x[0][1] = x[1];
5558 p->y[0][1] = y[1];
5559 p->x[0][2] = x[2];
5560 p->y[0][2] = y[2];
5561 p->x[0][3] = x[3];
5562 p->y[0][3] = y[3];
5563 p->x[1][3] = x[4];
5564 p->y[1][3] = y[4];
5565 p->x[2][3] = x[5];
5566 p->y[2][3] = y[5];
5567 p->x[3][3] = x[6];
5568 p->y[3][3] = y[6];
5569 p->x[3][2] = x[7];
5570 p->y[3][2] = y[7];
5571 p->x[3][1] = x[8];
5572 p->y[3][1] = y[8];
5573 p->x[3][0] = x[9];
5574 p->y[3][0] = y[9];
5575 p->x[2][0] = x[10];
5576 p->y[2][0] = y[10];
5577 p->x[1][0] = x[11];
5578 p->y[1][0] = y[11];
5579 p->x[1][1] = x[12];
5580 p->y[1][1] = y[12];
5581 p->x[1][2] = x[13];
5582 p->y[1][2] = y[13];
5583 p->x[2][2] = x[14];
5584 p->y[2][2] = y[14];
5585 p->x[2][1] = x[15];
5586 p->y[2][1] = y[15];
5587 for (j = 0; j < nComps; ++j) {
5588 p->color[0][0].c[j] = c[0][j];
5589 p->color[0][1].c[j] = c[1][j];
5590 p->color[1][1].c[j] = c[2][j];
5591 p->color[1][0].c[j] = c[3][j];
5593 break;
5594 case 1:
5595 if (nPatchesA == 0) {
5596 goto err1;
5598 p->x[0][0] = patchesA[nPatchesA-1].x[0][3];
5599 p->y[0][0] = patchesA[nPatchesA-1].y[0][3];
5600 p->x[0][1] = patchesA[nPatchesA-1].x[1][3];
5601 p->y[0][1] = patchesA[nPatchesA-1].y[1][3];
5602 p->x[0][2] = patchesA[nPatchesA-1].x[2][3];
5603 p->y[0][2] = patchesA[nPatchesA-1].y[2][3];
5604 p->x[0][3] = patchesA[nPatchesA-1].x[3][3];
5605 p->y[0][3] = patchesA[nPatchesA-1].y[3][3];
5606 p->x[1][3] = x[0];
5607 p->y[1][3] = y[0];
5608 p->x[2][3] = x[1];
5609 p->y[2][3] = y[1];
5610 p->x[3][3] = x[2];
5611 p->y[3][3] = y[2];
5612 p->x[3][2] = x[3];
5613 p->y[3][2] = y[3];
5614 p->x[3][1] = x[4];
5615 p->y[3][1] = y[4];
5616 p->x[3][0] = x[5];
5617 p->y[3][0] = y[5];
5618 p->x[2][0] = x[6];
5619 p->y[2][0] = y[6];
5620 p->x[1][0] = x[7];
5621 p->y[1][0] = y[7];
5622 p->x[1][1] = x[8];
5623 p->y[1][1] = y[8];
5624 p->x[1][2] = x[9];
5625 p->y[1][2] = y[9];
5626 p->x[2][2] = x[10];
5627 p->y[2][2] = y[10];
5628 p->x[2][1] = x[11];
5629 p->y[2][1] = y[11];
5630 for (j = 0; j < nComps; ++j) {
5631 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[0][1].c[j];
5632 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5633 p->color[1][1].c[j] = c[0][j];
5634 p->color[1][0].c[j] = c[1][j];
5636 break;
5637 case 2:
5638 if (nPatchesA == 0) {
5639 goto err1;
5641 p->x[0][0] = patchesA[nPatchesA-1].x[3][3];
5642 p->y[0][0] = patchesA[nPatchesA-1].y[3][3];
5643 p->x[0][1] = patchesA[nPatchesA-1].x[3][2];
5644 p->y[0][1] = patchesA[nPatchesA-1].y[3][2];
5645 p->x[0][2] = patchesA[nPatchesA-1].x[3][1];
5646 p->y[0][2] = patchesA[nPatchesA-1].y[3][1];
5647 p->x[0][3] = patchesA[nPatchesA-1].x[3][0];
5648 p->y[0][3] = patchesA[nPatchesA-1].y[3][0];
5649 p->x[1][3] = x[0];
5650 p->y[1][3] = y[0];
5651 p->x[2][3] = x[1];
5652 p->y[2][3] = y[1];
5653 p->x[3][3] = x[2];
5654 p->y[3][3] = y[2];
5655 p->x[3][2] = x[3];
5656 p->y[3][2] = y[3];
5657 p->x[3][1] = x[4];
5658 p->y[3][1] = y[4];
5659 p->x[3][0] = x[5];
5660 p->y[3][0] = y[5];
5661 p->x[2][0] = x[6];
5662 p->y[2][0] = y[6];
5663 p->x[1][0] = x[7];
5664 p->y[1][0] = y[7];
5665 p->x[1][1] = x[8];
5666 p->y[1][1] = y[8];
5667 p->x[1][2] = x[9];
5668 p->y[1][2] = y[9];
5669 p->x[2][2] = x[10];
5670 p->y[2][2] = y[10];
5671 p->x[2][1] = x[11];
5672 p->y[2][1] = y[11];
5673 for (j = 0; j < nComps; ++j) {
5674 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][1].c[j];
5675 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5676 p->color[1][1].c[j] = c[0][j];
5677 p->color[1][0].c[j] = c[1][j];
5679 break;
5680 case 3:
5681 if (nPatchesA == 0) {
5682 goto err1;
5684 p->x[0][0] = patchesA[nPatchesA-1].x[3][0];
5685 p->y[0][0] = patchesA[nPatchesA-1].y[3][0];
5686 p->x[0][1] = patchesA[nPatchesA-1].x[2][0];
5687 p->y[0][1] = patchesA[nPatchesA-1].y[2][0];
5688 p->x[0][2] = patchesA[nPatchesA-1].x[1][0];
5689 p->y[0][2] = patchesA[nPatchesA-1].y[1][0];
5690 p->x[0][3] = patchesA[nPatchesA-1].x[0][0];
5691 p->y[0][3] = patchesA[nPatchesA-1].y[0][0];
5692 p->x[1][3] = x[0];
5693 p->y[1][3] = y[0];
5694 p->x[2][3] = x[1];
5695 p->y[2][3] = y[1];
5696 p->x[3][3] = x[2];
5697 p->y[3][3] = y[2];
5698 p->x[3][2] = x[3];
5699 p->y[3][2] = y[3];
5700 p->x[3][1] = x[4];
5701 p->y[3][1] = y[4];
5702 p->x[3][0] = x[5];
5703 p->y[3][0] = y[5];
5704 p->x[2][0] = x[6];
5705 p->y[2][0] = y[6];
5706 p->x[1][0] = x[7];
5707 p->y[1][0] = y[7];
5708 p->x[1][1] = x[8];
5709 p->y[1][1] = y[8];
5710 p->x[1][2] = x[9];
5711 p->y[1][2] = y[9];
5712 p->x[2][2] = x[10];
5713 p->y[2][2] = y[10];
5714 p->x[2][1] = x[11];
5715 p->y[2][1] = y[11];
5716 for (j = 0; j < nComps; ++j) {
5717 p->color[0][0].c[j] = patchesA[nPatchesA-1].color[1][0].c[j];
5718 p->color[0][1].c[j] = patchesA[nPatchesA-1].color[0][0].c[j];
5719 p->color[1][1].c[j] = c[0][j];
5720 p->color[1][0].c[j] = c[1][j];
5722 break;
5725 ++nPatchesA;
5726 bitBuf->flushBits();
5728 delete bitBuf;
5730 if (typeA == 6) {
5731 for (i = 0; i < nPatchesA; ++i) {
5732 p = &patchesA[i];
5733 p->x[1][1] = (-4 * p->x[0][0]
5734 +6 * (p->x[0][1] + p->x[1][0])
5735 -2 * (p->x[0][3] + p->x[3][0])
5736 +3 * (p->x[3][1] + p->x[1][3])
5737 - p->x[3][3]) / 9;
5738 p->y[1][1] = (-4 * p->y[0][0]
5739 +6 * (p->y[0][1] + p->y[1][0])
5740 -2 * (p->y[0][3] + p->y[3][0])
5741 +3 * (p->y[3][1] + p->y[1][3])
5742 - p->y[3][3]) / 9;
5743 p->x[1][2] = (-4 * p->x[0][3]
5744 +6 * (p->x[0][2] + p->x[1][3])
5745 -2 * (p->x[0][0] + p->x[3][3])
5746 +3 * (p->x[3][2] + p->x[1][0])
5747 - p->x[3][0]) / 9;
5748 p->y[1][2] = (-4 * p->y[0][3]
5749 +6 * (p->y[0][2] + p->y[1][3])
5750 -2 * (p->y[0][0] + p->y[3][3])
5751 +3 * (p->y[3][2] + p->y[1][0])
5752 - p->y[3][0]) / 9;
5753 p->x[2][1] = (-4 * p->x[3][0]
5754 +6 * (p->x[3][1] + p->x[2][0])
5755 -2 * (p->x[3][3] + p->x[0][0])
5756 +3 * (p->x[0][1] + p->x[2][3])
5757 - p->x[0][3]) / 9;
5758 p->y[2][1] = (-4 * p->y[3][0]
5759 +6 * (p->y[3][1] + p->y[2][0])
5760 -2 * (p->y[3][3] + p->y[0][0])
5761 +3 * (p->y[0][1] + p->y[2][3])
5762 - p->y[0][3]) / 9;
5763 p->x[2][2] = (-4 * p->x[3][3]
5764 +6 * (p->x[3][2] + p->x[2][3])
5765 -2 * (p->x[3][0] + p->x[0][3])
5766 +3 * (p->x[0][2] + p->x[2][0])
5767 - p->x[0][0]) / 9;
5768 p->y[2][2] = (-4 * p->y[3][3]
5769 +6 * (p->y[3][2] + p->y[2][3])
5770 -2 * (p->y[3][0] + p->y[0][3])
5771 +3 * (p->y[0][2] + p->y[2][0])
5772 - p->y[0][0]) / 9;
5776 shading = new GfxPatchMeshShading(typeA, patchesA, nPatchesA,
5777 funcsA, nFuncsA);
5778 if (!shading->init(res, dict, out, state)) {
5779 delete shading;
5780 return NULL;
5782 return shading;
5784 err2:
5785 obj1.free();
5786 err1:
5787 return NULL;
5790 void GfxPatchMeshShading::getParameterizedColor(double t, GfxColor *color) {
5791 double out[gfxColorMaxComps];
5793 for (int j = 0; j < nFuncs; ++j) {
5794 funcs[j]->transform(&t, &out[j]);
5796 for (int j = 0; j < gfxColorMaxComps; ++j) {
5797 color->c[j] = dblToCol(out[j]);
5801 GfxShading *GfxPatchMeshShading::copy() {
5802 return new GfxPatchMeshShading(this);
5805 //------------------------------------------------------------------------
5806 // GfxImageColorMap
5807 //------------------------------------------------------------------------
5809 GfxImageColorMap::GfxImageColorMap(int bitsA, Object *decode,
5810 GfxColorSpace *colorSpaceA) {
5811 GfxIndexedColorSpace *indexedCS;
5812 GfxSeparationColorSpace *sepCS;
5813 int maxPixel, indexHigh;
5814 Guchar *indexedLookup;
5815 Function *sepFunc;
5816 Object obj;
5817 double x[gfxColorMaxComps];
5818 double y[gfxColorMaxComps];
5819 int i, j, k;
5820 double mapped;
5821 GBool useByteLookup;
5823 ok = gTrue;
5825 // bits per component and color space
5826 bits = bitsA;
5827 maxPixel = (1 << bits) - 1;
5828 colorSpace = colorSpaceA;
5830 // this is a hack to support 16 bits images, everywhere
5831 // we assume a component fits in 8 bits, with this hack
5832 // we treat 16 bit images as 8 bit ones until it's fixed correctly.
5833 // The hack has another part on ImageStream::getLine
5834 if (maxPixel > 255) maxPixel = 255;
5836 // initialize
5837 for (k = 0; k < gfxColorMaxComps; ++k) {
5838 lookup[k] = NULL;
5839 lookup2[k] = NULL;
5841 byte_lookup = NULL;
5843 // get decode map
5844 if (decode->isNull()) {
5845 nComps = colorSpace->getNComps();
5846 colorSpace->getDefaultRanges(decodeLow, decodeRange, maxPixel);
5847 } else if (decode->isArray()) {
5848 nComps = decode->arrayGetLength() / 2;
5849 if (nComps < colorSpace->getNComps()) {
5850 goto err1;
5852 if (nComps > colorSpace->getNComps()) {
5853 error(errSyntaxWarning, -1, "Too many elements in Decode array");
5854 nComps = colorSpace->getNComps();
5856 for (i = 0; i < nComps; ++i) {
5857 decode->arrayGet(2*i, &obj);
5858 if (!obj.isNum()) {
5859 goto err2;
5861 decodeLow[i] = obj.getNum();
5862 obj.free();
5863 decode->arrayGet(2*i+1, &obj);
5864 if (!obj.isNum()) {
5865 goto err2;
5867 decodeRange[i] = obj.getNum() - decodeLow[i];
5868 obj.free();
5870 } else {
5871 goto err1;
5874 // Construct a lookup table -- this stores pre-computed decoded
5875 // values for each component, i.e., the result of applying the
5876 // decode mapping to each possible image pixel component value.
5877 for (k = 0; k < nComps; ++k) {
5878 lookup[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5879 sizeof(GfxColorComp));
5880 for (i = 0; i <= maxPixel; ++i) {
5881 lookup[k][i] = dblToCol(decodeLow[k] +
5882 (i * decodeRange[k]) / maxPixel);
5886 // Optimization: for Indexed and Separation color spaces (which have
5887 // only one component), we pre-compute a second lookup table with
5888 // color values
5889 colorSpace2 = NULL;
5890 nComps2 = 0;
5891 useByteLookup = gFalse;
5892 switch (colorSpace->getMode()) {
5893 case csIndexed:
5894 // Note that indexHigh may not be the same as maxPixel --
5895 // Distiller will remove unused palette entries, resulting in
5896 // indexHigh < maxPixel.
5897 indexedCS = (GfxIndexedColorSpace *)colorSpace;
5898 colorSpace2 = indexedCS->getBase();
5899 indexHigh = indexedCS->getIndexHigh();
5900 nComps2 = colorSpace2->getNComps();
5901 indexedLookup = indexedCS->getLookup();
5902 colorSpace2->getDefaultRanges(x, y, indexHigh);
5903 if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5904 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
5905 useByteLookup = gTrue;
5907 for (k = 0; k < nComps2; ++k) {
5908 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5909 sizeof(GfxColorComp));
5910 for (i = 0; i <= maxPixel; ++i) {
5911 j = (int)(decodeLow[0] + (i * decodeRange[0]) / maxPixel + 0.5);
5912 if (j < 0) {
5913 j = 0;
5914 } else if (j > indexHigh) {
5915 j = indexHigh;
5918 mapped = x[k] + (indexedLookup[j*nComps2 + k] / 255.0) * y[k];
5919 lookup2[k][i] = dblToCol(mapped);
5920 if (useByteLookup)
5921 byte_lookup[i * nComps2 + k] = (Guchar) (mapped * 255);
5924 break;
5925 case csSeparation:
5926 sepCS = (GfxSeparationColorSpace *)colorSpace;
5927 colorSpace2 = sepCS->getAlt();
5928 nComps2 = colorSpace2->getNComps();
5929 sepFunc = sepCS->getFunc();
5930 if (colorSpace2->useGetGrayLine() || colorSpace2->useGetRGBLine() || colorSpace2->useGetCMYKLine() || colorSpace2->useGetDeviceNLine()) {
5931 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps2);
5932 useByteLookup = gTrue;
5934 for (k = 0; k < nComps2; ++k) {
5935 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5936 sizeof(GfxColorComp));
5937 for (i = 0; i <= maxPixel; ++i) {
5938 x[0] = decodeLow[0] + (i * decodeRange[0]) / maxPixel;
5939 sepFunc->transform(x, y);
5940 lookup2[k][i] = dblToCol(y[k]);
5941 if (useByteLookup)
5942 byte_lookup[i*nComps2 + k] = (Guchar) (y[k] * 255);
5945 break;
5946 default:
5947 if (colorSpace->useGetGrayLine() || colorSpace->useGetRGBLine() || colorSpace->useGetCMYKLine() || colorSpace->useGetDeviceNLine()) {
5948 byte_lookup = (Guchar *)gmallocn ((maxPixel + 1), nComps);
5949 useByteLookup = gTrue;
5951 for (k = 0; k < nComps; ++k) {
5952 lookup2[k] = (GfxColorComp *)gmallocn(maxPixel + 1,
5953 sizeof(GfxColorComp));
5954 for (i = 0; i <= maxPixel; ++i) {
5955 mapped = decodeLow[k] + (i * decodeRange[k]) / maxPixel;
5956 lookup2[k][i] = dblToCol(mapped);
5957 if (useByteLookup) {
5958 int byte;
5960 byte = (int) (mapped * 255.0 + 0.5);
5961 if (byte < 0)
5962 byte = 0;
5963 else if (byte > 255)
5964 byte = 255;
5965 byte_lookup[i * nComps + k] = byte;
5971 return;
5973 err2:
5974 obj.free();
5975 err1:
5976 ok = gFalse;
5979 GfxImageColorMap::GfxImageColorMap(GfxImageColorMap *colorMap) {
5980 int n, i, k;
5982 colorSpace = colorMap->colorSpace->copy();
5983 bits = colorMap->bits;
5984 nComps = colorMap->nComps;
5985 nComps2 = colorMap->nComps2;
5986 colorSpace2 = NULL;
5987 for (k = 0; k < gfxColorMaxComps; ++k) {
5988 lookup[k] = NULL;
5990 n = 1 << bits;
5991 if (colorSpace->getMode() == csIndexed) {
5992 colorSpace2 = ((GfxIndexedColorSpace *)colorSpace)->getBase();
5993 for (k = 0; k < nComps2; ++k) {
5994 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
5995 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
5997 } else if (colorSpace->getMode() == csSeparation) {
5998 colorSpace2 = ((GfxSeparationColorSpace *)colorSpace)->getAlt();
5999 for (k = 0; k < nComps2; ++k) {
6000 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
6001 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
6003 } else {
6004 for (k = 0; k < nComps; ++k) {
6005 lookup[k] = (GfxColorComp *)gmallocn(n, sizeof(GfxColorComp));
6006 memcpy(lookup[k], colorMap->lookup[k], n * sizeof(GfxColorComp));
6009 if (colorMap->byte_lookup) {
6010 int nc = colorSpace2 ? nComps2 : nComps;
6012 byte_lookup = (Guchar *)gmallocn (n, nc);
6013 memcpy(byte_lookup, colorMap->byte_lookup, n * nc);
6015 for (i = 0; i < nComps; ++i) {
6016 decodeLow[i] = colorMap->decodeLow[i];
6017 decodeRange[i] = colorMap->decodeRange[i];
6019 ok = gTrue;
6022 GfxImageColorMap::~GfxImageColorMap() {
6023 int i;
6025 delete colorSpace;
6026 for (i = 0; i < gfxColorMaxComps; ++i) {
6027 gfree(lookup[i]);
6028 gfree(lookup2[i]);
6030 gfree(byte_lookup);
6033 void GfxImageColorMap::getGray(Guchar *x, GfxGray *gray) {
6034 GfxColor color;
6035 int i;
6037 if (colorSpace2) {
6038 for (i = 0; i < nComps2; ++i) {
6039 color.c[i] = lookup2[i][x[0]];
6041 colorSpace2->getGray(&color, gray);
6042 } else {
6043 for (i = 0; i < nComps; ++i) {
6044 color.c[i] = lookup2[i][x[i]];
6046 colorSpace->getGray(&color, gray);
6050 void GfxImageColorMap::getRGB(Guchar *x, GfxRGB *rgb) {
6051 GfxColor color;
6052 int i;
6054 if (colorSpace2) {
6055 for (i = 0; i < nComps2; ++i) {
6056 color.c[i] = lookup2[i][x[0]];
6058 colorSpace2->getRGB(&color, rgb);
6059 } else {
6060 for (i = 0; i < nComps; ++i) {
6061 color.c[i] = lookup2[i][x[i]];
6063 colorSpace->getRGB(&color, rgb);
6067 void GfxImageColorMap::getGrayLine(Guchar *in, Guchar *out, int length) {
6068 int i, j;
6069 Guchar *inp, *tmp_line;
6071 if ((colorSpace2 && !colorSpace2->useGetGrayLine ()) ||
6072 (!colorSpace2 && !colorSpace->useGetGrayLine ())) {
6073 GfxGray gray;
6075 inp = in;
6076 for (i = 0; i < length; i++) {
6077 getGray (inp, &gray);
6078 out[i] = colToByte(gray);
6079 inp += nComps;
6081 return;
6084 switch (colorSpace->getMode()) {
6085 case csIndexed:
6086 case csSeparation:
6087 tmp_line = (Guchar *) gmallocn (length, nComps2);
6088 for (i = 0; i < length; i++) {
6089 for (j = 0; j < nComps2; j++) {
6090 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6093 colorSpace2->getGrayLine(tmp_line, out, length);
6094 gfree (tmp_line);
6095 break;
6097 default:
6098 inp = in;
6099 for (j = 0; j < length; j++)
6100 for (i = 0; i < nComps; i++) {
6101 *inp = byte_lookup[*inp * nComps + i];
6102 inp++;
6104 colorSpace->getGrayLine(in, out, length);
6105 break;
6110 void GfxImageColorMap::getRGBLine(Guchar *in, unsigned int *out, int length) {
6111 int i, j;
6112 Guchar *inp, *tmp_line;
6114 if (!useRGBLine()) {
6115 GfxRGB rgb;
6117 inp = in;
6118 for (i = 0; i < length; i++) {
6119 getRGB (inp, &rgb);
6120 out[i] =
6121 ((int) colToByte(rgb.r) << 16) |
6122 ((int) colToByte(rgb.g) << 8) |
6123 ((int) colToByte(rgb.b) << 0);
6124 inp += nComps;
6126 return;
6129 switch (colorSpace->getMode()) {
6130 case csIndexed:
6131 case csSeparation:
6132 tmp_line = (Guchar *) gmallocn (length, nComps2);
6133 for (i = 0; i < length; i++) {
6134 for (j = 0; j < nComps2; j++) {
6135 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6138 colorSpace2->getRGBLine(tmp_line, out, length);
6139 gfree (tmp_line);
6140 break;
6142 default:
6143 inp = in;
6144 for (j = 0; j < length; j++)
6145 for (i = 0; i < nComps; i++) {
6146 *inp = byte_lookup[*inp * nComps + i];
6147 inp++;
6149 colorSpace->getRGBLine(in, out, length);
6150 break;
6155 void GfxImageColorMap::getRGBLine(Guchar *in, Guchar *out, int length) {
6156 int i, j;
6157 Guchar *inp, *tmp_line;
6159 if (!useRGBLine()) {
6160 GfxRGB rgb;
6162 inp = in;
6163 for (i = 0; i < length; i++) {
6164 getRGB (inp, &rgb);
6165 *out++ = colToByte(rgb.r);
6166 *out++ = colToByte(rgb.g);
6167 *out++ = colToByte(rgb.b);
6168 inp += nComps;
6170 return;
6173 switch (colorSpace->getMode()) {
6174 case csIndexed:
6175 case csSeparation:
6176 tmp_line = (Guchar *) gmallocn (length, nComps2);
6177 for (i = 0; i < length; i++) {
6178 for (j = 0; j < nComps2; j++) {
6179 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6182 colorSpace2->getRGBLine(tmp_line, out, length);
6183 gfree (tmp_line);
6184 break;
6186 default:
6187 inp = in;
6188 for (j = 0; j < length; j++)
6189 for (i = 0; i < nComps; i++) {
6190 *inp = byte_lookup[*inp * nComps + i];
6191 inp++;
6193 colorSpace->getRGBLine(in, out, length);
6194 break;
6199 void GfxImageColorMap::getRGBXLine(Guchar *in, Guchar *out, int length) {
6200 int i, j;
6201 Guchar *inp, *tmp_line;
6203 if (!useRGBLine()) {
6204 GfxRGB rgb;
6206 inp = in;
6207 for (i = 0; i < length; i++) {
6208 getRGB (inp, &rgb);
6209 *out++ = colToByte(rgb.r);
6210 *out++ = colToByte(rgb.g);
6211 *out++ = colToByte(rgb.b);
6212 *out++ = 255;
6213 inp += nComps;
6215 return;
6218 switch (colorSpace->getMode()) {
6219 case csIndexed:
6220 case csSeparation:
6221 tmp_line = (Guchar *) gmallocn (length, nComps2);
6222 for (i = 0; i < length; i++) {
6223 for (j = 0; j < nComps2; j++) {
6224 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6227 colorSpace2->getRGBXLine(tmp_line, out, length);
6228 gfree (tmp_line);
6229 break;
6231 default:
6232 inp = in;
6233 for (j = 0; j < length; j++)
6234 for (i = 0; i < nComps; i++) {
6235 *inp = byte_lookup[*inp * nComps + i];
6236 inp++;
6238 colorSpace->getRGBXLine(in, out, length);
6239 break;
6244 void GfxImageColorMap::getCMYKLine(Guchar *in, Guchar *out, int length) {
6245 int i, j;
6246 Guchar *inp, *tmp_line;
6248 if (!useCMYKLine()) {
6249 GfxCMYK cmyk;
6251 inp = in;
6252 for (i = 0; i < length; i++) {
6253 getCMYK (inp, &cmyk);
6254 *out++ = colToByte(cmyk.c);
6255 *out++ = colToByte(cmyk.m);
6256 *out++ = colToByte(cmyk.y);
6257 *out++ = colToByte(cmyk.k);
6258 inp += nComps;
6260 return;
6263 switch (colorSpace->getMode()) {
6264 case csIndexed:
6265 case csSeparation:
6266 tmp_line = (Guchar *) gmallocn (length, nComps2);
6267 for (i = 0; i < length; i++) {
6268 for (j = 0; j < nComps2; j++) {
6269 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6272 colorSpace2->getCMYKLine(tmp_line, out, length);
6273 gfree (tmp_line);
6274 break;
6276 default:
6277 inp = in;
6278 for (j = 0; j < length; j++)
6279 for (i = 0; i < nComps; i++) {
6280 *inp = byte_lookup[*inp * nComps + i];
6281 inp++;
6283 colorSpace->getCMYKLine(in, out, length);
6284 break;
6289 void GfxImageColorMap::getDeviceNLine(Guchar *in, Guchar *out, int length) {
6290 int i, j;
6291 Guchar *inp, *tmp_line;
6293 if (!useDeviceNLine()) {
6294 GfxColor deviceN;
6296 inp = in;
6297 for (i = 0; i < length; i++) {
6298 getDeviceN (inp, &deviceN);
6299 for (int j = 0; j < SPOT_NCOMPS+4; j++)
6300 *out++ = deviceN.c[j];
6301 inp += nComps;
6303 return;
6306 switch (colorSpace->getMode()) {
6307 case csIndexed:
6308 case csSeparation:
6309 tmp_line = (Guchar *) gmallocn (length, nComps2);
6310 for (i = 0; i < length; i++) {
6311 for (j = 0; j < nComps2; j++) {
6312 tmp_line[i * nComps2 + j] = byte_lookup[in[i] * nComps2 + j];
6315 colorSpace2->getDeviceNLine(tmp_line, out, length);
6316 gfree (tmp_line);
6317 break;
6319 default:
6320 inp = in;
6321 for (j = 0; j < length; j++)
6322 for (i = 0; i < nComps; i++) {
6323 *inp = byte_lookup[*inp * nComps + i];
6324 inp++;
6326 colorSpace->getDeviceNLine(in, out, length);
6327 break;
6332 void GfxImageColorMap::getCMYK(Guchar *x, GfxCMYK *cmyk) {
6333 GfxColor color;
6334 int i;
6336 if (colorSpace2) {
6337 for (i = 0; i < nComps2; ++i) {
6338 color.c[i] = lookup2[i][x[0]];
6340 colorSpace2->getCMYK(&color, cmyk);
6341 } else {
6342 for (i = 0; i < nComps; ++i) {
6343 color.c[i] = lookup[i][x[i]];
6345 colorSpace->getCMYK(&color, cmyk);
6349 void GfxImageColorMap::getDeviceN(Guchar *x, GfxColor *deviceN) {
6350 GfxColor color;
6351 int i;
6353 if (colorSpace2) {
6354 for (i = 0; i < nComps2; ++i) {
6355 color.c[i] = lookup2[i][x[0]];
6357 colorSpace2->getDeviceN(&color, deviceN);
6358 } else {
6359 for (i = 0; i < nComps; ++i) {
6360 color.c[i] = lookup[i][x[i]];
6362 colorSpace->getDeviceN(&color, deviceN);
6366 void GfxImageColorMap::getColor(Guchar *x, GfxColor *color) {
6367 int maxPixel, i;
6369 maxPixel = (1 << bits) - 1;
6370 for (i = 0; i < nComps; ++i) {
6371 color->c[i] = dblToCol(decodeLow[i] + (x[i] * decodeRange[i]) / maxPixel);
6375 //------------------------------------------------------------------------
6376 // GfxSubpath and GfxPath
6377 //------------------------------------------------------------------------
6379 GfxSubpath::GfxSubpath(double x1, double y1) {
6380 size = 16;
6381 x = (double *)gmallocn(size, sizeof(double));
6382 y = (double *)gmallocn(size, sizeof(double));
6383 curve = (GBool *)gmallocn(size, sizeof(GBool));
6384 n = 1;
6385 x[0] = x1;
6386 y[0] = y1;
6387 curve[0] = gFalse;
6388 closed = gFalse;
6391 GfxSubpath::~GfxSubpath() {
6392 gfree(x);
6393 gfree(y);
6394 gfree(curve);
6397 // Used for copy().
6398 GfxSubpath::GfxSubpath(GfxSubpath *subpath) {
6399 size = subpath->size;
6400 n = subpath->n;
6401 x = (double *)gmallocn(size, sizeof(double));
6402 y = (double *)gmallocn(size, sizeof(double));
6403 curve = (GBool *)gmallocn(size, sizeof(GBool));
6404 memcpy(x, subpath->x, n * sizeof(double));
6405 memcpy(y, subpath->y, n * sizeof(double));
6406 memcpy(curve, subpath->curve, n * sizeof(GBool));
6407 closed = subpath->closed;
6410 void GfxSubpath::lineTo(double x1, double y1) {
6411 if (n >= size) {
6412 size *= 2;
6413 x = (double *)greallocn(x, size, sizeof(double));
6414 y = (double *)greallocn(y, size, sizeof(double));
6415 curve = (GBool *)greallocn(curve, size, sizeof(GBool));
6417 x[n] = x1;
6418 y[n] = y1;
6419 curve[n] = gFalse;
6420 ++n;
6423 void GfxSubpath::curveTo(double x1, double y1, double x2, double y2,
6424 double x3, double y3) {
6425 if (n+3 > size) {
6426 size *= 2;
6427 x = (double *)greallocn(x, size, sizeof(double));
6428 y = (double *)greallocn(y, size, sizeof(double));
6429 curve = (GBool *)greallocn(curve, size, sizeof(GBool));
6431 x[n] = x1;
6432 y[n] = y1;
6433 x[n+1] = x2;
6434 y[n+1] = y2;
6435 x[n+2] = x3;
6436 y[n+2] = y3;
6437 curve[n] = curve[n+1] = gTrue;
6438 curve[n+2] = gFalse;
6439 n += 3;
6442 void GfxSubpath::close() {
6443 if (x[n-1] != x[0] || y[n-1] != y[0]) {
6444 lineTo(x[0], y[0]);
6446 closed = gTrue;
6449 void GfxSubpath::offset(double dx, double dy) {
6450 int i;
6452 for (i = 0; i < n; ++i) {
6453 x[i] += dx;
6454 y[i] += dy;
6458 GfxPath::GfxPath() {
6459 justMoved = gFalse;
6460 size = 16;
6461 n = 0;
6462 firstX = firstY = 0;
6463 subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
6466 GfxPath::~GfxPath() {
6467 int i;
6469 for (i = 0; i < n; ++i)
6470 delete subpaths[i];
6471 gfree(subpaths);
6474 // Used for copy().
6475 GfxPath::GfxPath(GBool justMoved1, double firstX1, double firstY1,
6476 GfxSubpath **subpaths1, int n1, int size1) {
6477 int i;
6479 justMoved = justMoved1;
6480 firstX = firstX1;
6481 firstY = firstY1;
6482 size = size1;
6483 n = n1;
6484 subpaths = (GfxSubpath **)gmallocn(size, sizeof(GfxSubpath *));
6485 for (i = 0; i < n; ++i)
6486 subpaths[i] = subpaths1[i]->copy();
6489 void GfxPath::moveTo(double x, double y) {
6490 justMoved = gTrue;
6491 firstX = x;
6492 firstY = y;
6495 void GfxPath::lineTo(double x, double y) {
6496 if (justMoved || (n > 0 && subpaths[n-1]->isClosed())) {
6497 if (n >= size) {
6498 size *= 2;
6499 subpaths = (GfxSubpath **)
6500 greallocn(subpaths, size, sizeof(GfxSubpath *));
6502 if (justMoved) {
6503 subpaths[n] = new GfxSubpath(firstX, firstY);
6504 } else {
6505 subpaths[n] = new GfxSubpath(subpaths[n-1]->getLastX(),
6506 subpaths[n-1]->getLastY());
6508 ++n;
6509 justMoved = gFalse;
6511 subpaths[n-1]->lineTo(x, y);
6514 void GfxPath::curveTo(double x1, double y1, double x2, double y2,
6515 double x3, double y3) {
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]->curveTo(x1, y1, x2, y2, x3, y3);
6534 void GfxPath::close() {
6535 // this is necessary to handle the pathological case of
6536 // moveto/closepath/clip, which defines an empty clipping region
6537 if (justMoved) {
6538 if (n >= size) {
6539 size *= 2;
6540 subpaths = (GfxSubpath **)
6541 greallocn(subpaths, size, sizeof(GfxSubpath *));
6543 subpaths[n] = new GfxSubpath(firstX, firstY);
6544 ++n;
6545 justMoved = gFalse;
6547 subpaths[n-1]->close();
6550 void GfxPath::append(GfxPath *path) {
6551 int i;
6553 if (n + path->n > size) {
6554 size = n + path->n;
6555 subpaths = (GfxSubpath **)
6556 greallocn(subpaths, size, sizeof(GfxSubpath *));
6558 for (i = 0; i < path->n; ++i) {
6559 subpaths[n++] = path->subpaths[i]->copy();
6561 justMoved = gFalse;
6564 void GfxPath::offset(double dx, double dy) {
6565 int i;
6567 for (i = 0; i < n; ++i) {
6568 subpaths[i]->offset(dx, dy);
6572 //------------------------------------------------------------------------
6573 // GfxState
6574 //------------------------------------------------------------------------
6575 GfxState::ReusablePathIterator::ReusablePathIterator(GfxPath *path)
6576 : path(path),
6577 subPathOff(0),
6578 coordOff(0),
6579 numCoords(0),
6580 curSubPath(NULL)
6582 if( path->getNumSubpaths() ) {
6583 curSubPath = path->getSubpath(subPathOff);
6584 numCoords = curSubPath->getNumPoints();
6588 bool GfxState::ReusablePathIterator::isEnd() const {
6589 return coordOff >= numCoords;
6592 void GfxState::ReusablePathIterator::next() {
6593 ++coordOff;
6594 if (coordOff == numCoords) {
6595 ++subPathOff;
6596 if (subPathOff < path->getNumSubpaths()) {
6597 coordOff = 0;
6598 curSubPath = path->getSubpath(subPathOff);
6599 numCoords = curSubPath->getNumPoints();
6604 void GfxState::ReusablePathIterator::setCoord(double x, double y) {
6605 curSubPath->setX(coordOff, x);
6606 curSubPath->setY(coordOff, y);
6609 void GfxState::ReusablePathIterator::reset() {
6610 coordOff = 0;
6611 subPathOff = 0;
6612 curSubPath = path->getSubpath(0);
6613 numCoords = curSubPath->getNumPoints();
6616 GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox,
6617 int rotateA, GBool upsideDown) {
6618 double kx, ky;
6620 hDPI = hDPIA;
6621 vDPI = vDPIA;
6622 rotate = rotateA;
6623 px1 = pageBox->x1;
6624 py1 = pageBox->y1;
6625 px2 = pageBox->x2;
6626 py2 = pageBox->y2;
6627 kx = hDPI / 72.0;
6628 ky = vDPI / 72.0;
6629 if (rotate == 90) {
6630 ctm[0] = 0;
6631 ctm[1] = upsideDown ? ky : -ky;
6632 ctm[2] = kx;
6633 ctm[3] = 0;
6634 ctm[4] = -kx * py1;
6635 ctm[5] = ky * (upsideDown ? -px1 : px2);
6636 pageWidth = kx * (py2 - py1);
6637 pageHeight = ky * (px2 - px1);
6638 } else if (rotate == 180) {
6639 ctm[0] = -kx;
6640 ctm[1] = 0;
6641 ctm[2] = 0;
6642 ctm[3] = upsideDown ? ky : -ky;
6643 ctm[4] = kx * px2;
6644 ctm[5] = ky * (upsideDown ? -py1 : py2);
6645 pageWidth = kx * (px2 - px1);
6646 pageHeight = ky * (py2 - py1);
6647 } else if (rotate == 270) {
6648 ctm[0] = 0;
6649 ctm[1] = upsideDown ? -ky : ky;
6650 ctm[2] = -kx;
6651 ctm[3] = 0;
6652 ctm[4] = kx * py2;
6653 ctm[5] = ky * (upsideDown ? px2 : -px1);
6654 pageWidth = kx * (py2 - py1);
6655 pageHeight = ky * (px2 - px1);
6656 } else {
6657 ctm[0] = kx;
6658 ctm[1] = 0;
6659 ctm[2] = 0;
6660 ctm[3] = upsideDown ? -ky : ky;
6661 ctm[4] = -kx * px1;
6662 ctm[5] = ky * (upsideDown ? py2 : -py1);
6663 pageWidth = kx * (px2 - px1);
6664 pageHeight = ky * (py2 - py1);
6667 fillColorSpace = new GfxDeviceGrayColorSpace();
6668 strokeColorSpace = new GfxDeviceGrayColorSpace();
6669 fillColor.c[0] = 0;
6670 strokeColor.c[0] = 0;
6671 fillPattern = NULL;
6672 strokePattern = NULL;
6673 blendMode = gfxBlendNormal;
6674 fillOpacity = 1;
6675 strokeOpacity = 1;
6676 fillOverprint = gFalse;
6677 strokeOverprint = gFalse;
6678 overprintMode = 0;
6679 transfer[0] = transfer[1] = transfer[2] = transfer[3] = NULL;
6681 lineWidth = 1;
6682 lineDash = NULL;
6683 lineDashLength = 0;
6684 lineDashStart = 0;
6685 flatness = 1;
6686 lineJoin = 0;
6687 lineCap = 0;
6688 miterLimit = 10;
6689 strokeAdjust = gFalse;
6690 alphaIsShape = gFalse;
6691 textKnockout = gFalse;
6693 font = NULL;
6694 fontSize = 0;
6695 textMat[0] = 1; textMat[1] = 0;
6696 textMat[2] = 0; textMat[3] = 1;
6697 textMat[4] = 0; textMat[5] = 0;
6698 charSpace = 0;
6699 wordSpace = 0;
6700 horizScaling = 1;
6701 leading = 0;
6702 rise = 0;
6703 render = 0;
6705 path = new GfxPath();
6706 curX = curY = 0;
6707 lineX = lineY = 0;
6709 clipXMin = 0;
6710 clipYMin = 0;
6711 clipXMax = pageWidth;
6712 clipYMax = pageHeight;
6714 renderingIntent[0] = 0;
6716 saved = NULL;
6717 #ifdef USE_CMS
6718 GfxColorSpace::setupColorProfiles();
6719 XYZ2DisplayTransformRelCol = NULL;
6720 XYZ2DisplayTransformAbsCol = NULL;
6721 XYZ2DisplayTransformSat = NULL;
6722 XYZ2DisplayTransformPerc = NULL;
6723 localDisplayProfile = NULL;
6724 displayProfileRef = 0;
6725 #endif
6728 GfxState::~GfxState() {
6729 int i;
6731 if (fillColorSpace) {
6732 delete fillColorSpace;
6734 if (strokeColorSpace) {
6735 delete strokeColorSpace;
6737 if (fillPattern) {
6738 delete fillPattern;
6740 if (strokePattern) {
6741 delete strokePattern;
6743 for (i = 0; i < 4; ++i) {
6744 if (transfer[i]) {
6745 delete transfer[i];
6748 gfree(lineDash);
6749 if (path) {
6750 // this gets set to NULL by restore()
6751 delete path;
6753 if (font) {
6754 font->decRefCnt();
6756 #ifdef USE_CMS
6757 if (XYZ2DisplayTransformRelCol) {
6758 if (XYZ2DisplayTransformRelCol->unref() == 0)
6759 delete XYZ2DisplayTransformRelCol;
6761 if (XYZ2DisplayTransformAbsCol) {
6762 if (XYZ2DisplayTransformAbsCol->unref() == 0)
6763 delete XYZ2DisplayTransformAbsCol;
6765 if (XYZ2DisplayTransformSat) {
6766 if (XYZ2DisplayTransformSat->unref() == 0)
6767 delete XYZ2DisplayTransformSat;
6769 if (XYZ2DisplayTransformPerc) {
6770 if (XYZ2DisplayTransformPerc->unref() == 0)
6771 delete XYZ2DisplayTransformPerc;
6773 if (--displayProfileRef == 0 && localDisplayProfile != NULL) {
6774 cmsCloseProfile(localDisplayProfile);
6776 #endif
6779 // Used for copy();
6780 GfxState::GfxState(GfxState *state, GBool copyPath) {
6781 int i;
6783 memcpy(this, state, sizeof(GfxState));
6784 if (fillColorSpace) {
6785 fillColorSpace = state->fillColorSpace->copy();
6787 if (strokeColorSpace) {
6788 strokeColorSpace = state->strokeColorSpace->copy();
6790 if (fillPattern) {
6791 fillPattern = state->fillPattern->copy();
6793 if (strokePattern) {
6794 strokePattern = state->strokePattern->copy();
6796 for (i = 0; i < 4; ++i) {
6797 if (transfer[i]) {
6798 transfer[i] = state->transfer[i]->copy();
6801 if (lineDashLength > 0) {
6802 lineDash = (double *)gmallocn(lineDashLength, sizeof(double));
6803 memcpy(lineDash, state->lineDash, lineDashLength * sizeof(double));
6805 if (font)
6806 font->incRefCnt();
6808 if (copyPath) {
6809 path = state->path->copy();
6811 saved = NULL;
6812 #ifdef USE_CMS
6813 if (XYZ2DisplayTransformRelCol) {
6814 XYZ2DisplayTransformRelCol->ref();
6816 if (XYZ2DisplayTransformAbsCol) {
6817 XYZ2DisplayTransformAbsCol->ref();
6819 if (XYZ2DisplayTransformSat) {
6820 XYZ2DisplayTransformSat->ref();
6822 if (XYZ2DisplayTransformPerc) {
6823 XYZ2DisplayTransformPerc->ref();
6825 if (localDisplayProfile) {
6826 displayProfileRef++;
6828 #endif
6831 #ifdef USE_CMS
6832 void GfxState::setDisplayProfile(cmsHPROFILE localDisplayProfileA) {
6833 if (localDisplayProfile != NULL) {
6834 cmsCloseProfile(localDisplayProfile);
6836 localDisplayProfile = localDisplayProfileA;
6837 if (localDisplayProfileA != NULL) {
6838 cmsHTRANSFORM transform;
6839 unsigned int nChannels;
6840 unsigned int localDisplayPixelType;
6842 localDisplayPixelType = getCMSColorSpaceType(cmsGetColorSpace(localDisplayProfile));
6843 nChannels = getCMSNChannels(cmsGetColorSpace(localDisplayProfile));
6844 displayProfileRef = 1;
6845 // create transform from XYZ
6846 cmsHPROFILE XYZProfile = cmsCreateXYZProfile();
6847 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6848 localDisplayProfile,
6849 COLORSPACE_SH(localDisplayPixelType) |
6850 CHANNELS_SH(nChannels) | BYTES_SH(1),
6851 INTENT_RELATIVE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
6852 error(errSyntaxWarning, -1, "Can't create Lab transform");
6853 } else {
6854 XYZ2DisplayTransformRelCol = new GfxColorTransform(transform, INTENT_RELATIVE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6856 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6857 localDisplayProfile,
6858 COLORSPACE_SH(localDisplayPixelType) |
6859 CHANNELS_SH(nChannels) | BYTES_SH(1),
6860 INTENT_ABSOLUTE_COLORIMETRIC,LCMS_FLAGS)) == 0) {
6861 error(errSyntaxWarning, -1, "Can't create Lab transform");
6862 } else {
6863 XYZ2DisplayTransformAbsCol = new GfxColorTransform(transform, INTENT_ABSOLUTE_COLORIMETRIC, PT_XYZ, localDisplayPixelType);
6865 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6866 localDisplayProfile,
6867 COLORSPACE_SH(localDisplayPixelType) |
6868 CHANNELS_SH(nChannels) | BYTES_SH(1),
6869 INTENT_SATURATION,LCMS_FLAGS)) == 0) {
6870 error(errSyntaxWarning, -1, "Can't create Lab transform");
6871 } else {
6872 XYZ2DisplayTransformSat = new GfxColorTransform(transform, INTENT_SATURATION, PT_XYZ, localDisplayPixelType);
6874 if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL,
6875 localDisplayProfile,
6876 COLORSPACE_SH(localDisplayPixelType) |
6877 CHANNELS_SH(nChannels) | BYTES_SH(1),
6878 INTENT_PERCEPTUAL,LCMS_FLAGS)) == 0) {
6879 error(errSyntaxWarning, -1, "Can't create Lab transform");
6880 } else {
6881 XYZ2DisplayTransformPerc = new GfxColorTransform(transform, INTENT_PERCEPTUAL, PT_XYZ, localDisplayPixelType);
6883 cmsCloseProfile(XYZProfile);
6887 GfxColorTransform *GfxState::getXYZ2DisplayTransform() {
6888 GfxColorTransform *transform;
6890 transform = XYZ2DisplayTransformRelCol;
6891 if (strcmp(renderingIntent, "AbsoluteColorimetric") == 0) {
6892 transform = XYZ2DisplayTransformAbsCol;
6893 } else if (strcmp(renderingIntent, "Saturation") == 0) {
6894 transform = XYZ2DisplayTransformSat;
6895 } else if (strcmp(renderingIntent, "Perceptual") == 0) {
6896 transform = XYZ2DisplayTransformPerc;
6898 if (transform == NULL) {
6899 transform = XYZ2DisplayTransform;
6901 return transform;
6904 #endif
6906 void GfxState::setPath(GfxPath *pathA) {
6907 delete path;
6908 path = pathA;
6911 void GfxState::getUserClipBBox(double *xMin, double *yMin,
6912 double *xMax, double *yMax) {
6913 double ictm[6];
6914 double xMin1, yMin1, xMax1, yMax1, det, tx, ty;
6916 // invert the CTM
6917 det = 1 / (ctm[0] * ctm[3] - ctm[1] * ctm[2]);
6918 ictm[0] = ctm[3] * det;
6919 ictm[1] = -ctm[1] * det;
6920 ictm[2] = -ctm[2] * det;
6921 ictm[3] = ctm[0] * det;
6922 ictm[4] = (ctm[2] * ctm[5] - ctm[3] * ctm[4]) * det;
6923 ictm[5] = (ctm[1] * ctm[4] - ctm[0] * ctm[5]) * det;
6925 // transform all four corners of the clip bbox; find the min and max
6926 // x and y values
6927 xMin1 = xMax1 = clipXMin * ictm[0] + clipYMin * ictm[2] + ictm[4];
6928 yMin1 = yMax1 = clipXMin * ictm[1] + clipYMin * ictm[3] + ictm[5];
6929 tx = clipXMin * ictm[0] + clipYMax * ictm[2] + ictm[4];
6930 ty = clipXMin * ictm[1] + clipYMax * ictm[3] + ictm[5];
6931 if (tx < xMin1) {
6932 xMin1 = tx;
6933 } else if (tx > xMax1) {
6934 xMax1 = tx;
6936 if (ty < yMin1) {
6937 yMin1 = ty;
6938 } else if (ty > yMax1) {
6939 yMax1 = ty;
6941 tx = clipXMax * ictm[0] + clipYMin * ictm[2] + ictm[4];
6942 ty = clipXMax * ictm[1] + clipYMin * ictm[3] + ictm[5];
6943 if (tx < xMin1) {
6944 xMin1 = tx;
6945 } else if (tx > xMax1) {
6946 xMax1 = tx;
6948 if (ty < yMin1) {
6949 yMin1 = ty;
6950 } else if (ty > yMax1) {
6951 yMax1 = ty;
6953 tx = clipXMax * ictm[0] + clipYMax * ictm[2] + ictm[4];
6954 ty = clipXMax * ictm[1] + clipYMax * ictm[3] + ictm[5];
6955 if (tx < xMin1) {
6956 xMin1 = tx;
6957 } else if (tx > xMax1) {
6958 xMax1 = tx;
6960 if (ty < yMin1) {
6961 yMin1 = ty;
6962 } else if (ty > yMax1) {
6963 yMax1 = ty;
6966 *xMin = xMin1;
6967 *yMin = yMin1;
6968 *xMax = xMax1;
6969 *yMax = yMax1;
6972 double GfxState::transformWidth(double w) {
6973 double x, y;
6975 x = ctm[0] + ctm[2];
6976 y = ctm[1] + ctm[3];
6977 return w * sqrt(0.5 * (x * x + y * y));
6980 double GfxState::getTransformedFontSize() {
6981 double x1, y1, x2, y2;
6983 x1 = textMat[2] * fontSize;
6984 y1 = textMat[3] * fontSize;
6985 x2 = ctm[0] * x1 + ctm[2] * y1;
6986 y2 = ctm[1] * x1 + ctm[3] * y1;
6987 return sqrt(x2 * x2 + y2 * y2);
6990 void GfxState::getFontTransMat(double *m11, double *m12,
6991 double *m21, double *m22) {
6992 *m11 = (textMat[0] * ctm[0] + textMat[1] * ctm[2]) * fontSize;
6993 *m12 = (textMat[0] * ctm[1] + textMat[1] * ctm[3]) * fontSize;
6994 *m21 = (textMat[2] * ctm[0] + textMat[3] * ctm[2]) * fontSize;
6995 *m22 = (textMat[2] * ctm[1] + textMat[3] * ctm[3]) * fontSize;
6998 void GfxState::setCTM(double a, double b, double c,
6999 double d, double e, double f) {
7000 ctm[0] = a;
7001 ctm[1] = b;
7002 ctm[2] = c;
7003 ctm[3] = d;
7004 ctm[4] = e;
7005 ctm[5] = f;
7008 void GfxState::concatCTM(double a, double b, double c,
7009 double d, double e, double f) {
7010 double a1 = ctm[0];
7011 double b1 = ctm[1];
7012 double c1 = ctm[2];
7013 double d1 = ctm[3];
7015 ctm[0] = a * a1 + b * c1;
7016 ctm[1] = a * b1 + b * d1;
7017 ctm[2] = c * a1 + d * c1;
7018 ctm[3] = c * b1 + d * d1;
7019 ctm[4] = e * a1 + f * c1 + ctm[4];
7020 ctm[5] = e * b1 + f * d1 + ctm[5];
7023 void GfxState::shiftCTMAndClip(double tx, double ty) {
7024 ctm[4] += tx;
7025 ctm[5] += ty;
7026 clipXMin += tx;
7027 clipYMin += ty;
7028 clipXMax += tx;
7029 clipYMax += ty;
7032 void GfxState::setFillColorSpace(GfxColorSpace *colorSpace) {
7033 if (fillColorSpace) {
7034 delete fillColorSpace;
7036 fillColorSpace = colorSpace;
7039 void GfxState::setStrokeColorSpace(GfxColorSpace *colorSpace) {
7040 if (strokeColorSpace) {
7041 delete strokeColorSpace;
7043 strokeColorSpace = colorSpace;
7046 void GfxState::setFillPattern(GfxPattern *pattern) {
7047 if (fillPattern) {
7048 delete fillPattern;
7050 fillPattern = pattern;
7053 void GfxState::setStrokePattern(GfxPattern *pattern) {
7054 if (strokePattern) {
7055 delete strokePattern;
7057 strokePattern = pattern;
7060 void GfxState::setFont(GfxFont *fontA, double fontSizeA) {
7061 if (font)
7062 font->decRefCnt();
7064 font = fontA;
7065 fontSize = fontSizeA;
7068 void GfxState::setTransfer(Function **funcs) {
7069 int i;
7071 for (i = 0; i < 4; ++i) {
7072 if (transfer[i]) {
7073 delete transfer[i];
7075 transfer[i] = funcs[i];
7079 void GfxState::setLineDash(double *dash, int length, double start) {
7080 if (lineDash)
7081 gfree(lineDash);
7082 lineDash = dash;
7083 lineDashLength = length;
7084 lineDashStart = start;
7087 void GfxState::clearPath() {
7088 delete path;
7089 path = new GfxPath();
7092 void GfxState::clip() {
7093 double xMin, yMin, xMax, yMax, x, y;
7094 GfxSubpath *subpath;
7095 int i, j;
7097 xMin = xMax = yMin = yMax = 0; // make gcc happy
7098 for (i = 0; i < path->getNumSubpaths(); ++i) {
7099 subpath = path->getSubpath(i);
7100 for (j = 0; j < subpath->getNumPoints(); ++j) {
7101 transform(subpath->getX(j), subpath->getY(j), &x, &y);
7102 if (i == 0 && j == 0) {
7103 xMin = xMax = x;
7104 yMin = yMax = y;
7105 } else {
7106 if (x < xMin) {
7107 xMin = x;
7108 } else if (x > xMax) {
7109 xMax = x;
7111 if (y < yMin) {
7112 yMin = y;
7113 } else if (y > yMax) {
7114 yMax = y;
7119 if (xMin > clipXMin) {
7120 clipXMin = xMin;
7122 if (yMin > clipYMin) {
7123 clipYMin = yMin;
7125 if (xMax < clipXMax) {
7126 clipXMax = xMax;
7128 if (yMax < clipYMax) {
7129 clipYMax = yMax;
7133 void GfxState::clipToStrokePath() {
7134 double xMin, yMin, xMax, yMax, x, y, t0, t1;
7135 GfxSubpath *subpath;
7136 int i, j;
7138 xMin = xMax = yMin = yMax = 0; // make gcc happy
7139 for (i = 0; i < path->getNumSubpaths(); ++i) {
7140 subpath = path->getSubpath(i);
7141 for (j = 0; j < subpath->getNumPoints(); ++j) {
7142 transform(subpath->getX(j), subpath->getY(j), &x, &y);
7143 if (i == 0 && j == 0) {
7144 xMin = xMax = x;
7145 yMin = yMax = y;
7146 } else {
7147 if (x < xMin) {
7148 xMin = x;
7149 } else if (x > xMax) {
7150 xMax = x;
7152 if (y < yMin) {
7153 yMin = y;
7154 } else if (y > yMax) {
7155 yMax = y;
7161 // allow for the line width
7162 //~ miter joins can extend farther than this
7163 t0 = fabs(ctm[0]);
7164 t1 = fabs(ctm[2]);
7165 if (t0 > t1) {
7166 xMin -= 0.5 * lineWidth * t0;
7167 xMax += 0.5 * lineWidth * t0;
7168 } else {
7169 xMin -= 0.5 * lineWidth * t1;
7170 xMax += 0.5 * lineWidth * t1;
7172 t0 = fabs(ctm[0]);
7173 t1 = fabs(ctm[3]);
7174 if (t0 > t1) {
7175 yMin -= 0.5 * lineWidth * t0;
7176 yMax += 0.5 * lineWidth * t0;
7177 } else {
7178 yMin -= 0.5 * lineWidth * t1;
7179 yMax += 0.5 * lineWidth * t1;
7182 if (xMin > clipXMin) {
7183 clipXMin = xMin;
7185 if (yMin > clipYMin) {
7186 clipYMin = yMin;
7188 if (xMax < clipXMax) {
7189 clipXMax = xMax;
7191 if (yMax < clipYMax) {
7192 clipYMax = yMax;
7196 void GfxState::clipToRect(double xMin, double yMin, double xMax, double yMax) {
7197 double x, y, xMin1, yMin1, xMax1, yMax1;
7199 transform(xMin, yMin, &x, &y);
7200 xMin1 = xMax1 = x;
7201 yMin1 = yMax1 = y;
7202 transform(xMax, yMin, &x, &y);
7203 if (x < xMin1) {
7204 xMin1 = x;
7205 } else if (x > xMax1) {
7206 xMax1 = x;
7208 if (y < yMin1) {
7209 yMin1 = y;
7210 } else if (y > yMax1) {
7211 yMax1 = y;
7213 transform(xMax, yMax, &x, &y);
7214 if (x < xMin1) {
7215 xMin1 = x;
7216 } else if (x > xMax1) {
7217 xMax1 = x;
7219 if (y < yMin1) {
7220 yMin1 = y;
7221 } else if (y > yMax1) {
7222 yMax1 = y;
7224 transform(xMin, yMax, &x, &y);
7225 if (x < xMin1) {
7226 xMin1 = x;
7227 } else if (x > xMax1) {
7228 xMax1 = x;
7230 if (y < yMin1) {
7231 yMin1 = y;
7232 } else if (y > yMax1) {
7233 yMax1 = y;
7236 if (xMin1 > clipXMin) {
7237 clipXMin = xMin1;
7239 if (yMin1 > clipYMin) {
7240 clipYMin = yMin1;
7242 if (xMax1 < clipXMax) {
7243 clipXMax = xMax1;
7245 if (yMax1 < clipYMax) {
7246 clipYMax = yMax1;
7250 void GfxState::textShift(double tx, double ty) {
7251 double dx, dy;
7253 textTransformDelta(tx, ty, &dx, &dy);
7254 curX += dx;
7255 curY += dy;
7258 void GfxState::shift(double dx, double dy) {
7259 curX += dx;
7260 curY += dy;
7263 GfxState *GfxState::save() {
7264 GfxState *newState;
7266 newState = copy();
7267 newState->saved = this;
7268 return newState;
7271 GfxState *GfxState::restore() {
7272 GfxState *oldState;
7274 if (saved) {
7275 oldState = saved;
7277 // these attributes aren't saved/restored by the q/Q operators
7278 oldState->path = path;
7279 oldState->curX = curX;
7280 oldState->curY = curY;
7281 oldState->lineX = lineX;
7282 oldState->lineY = lineY;
7284 path = NULL;
7285 saved = NULL;
7286 delete this;
7288 } else {
7289 oldState = this;
7292 return oldState;
7295 GBool GfxState::parseBlendMode(Object *obj, GfxBlendMode *mode) {
7296 Object obj2;
7297 int i, j;
7299 if (obj->isName()) {
7300 for (i = 0; i < nGfxBlendModeNames; ++i) {
7301 if (!strcmp(obj->getName(), gfxBlendModeNames[i].name)) {
7302 *mode = gfxBlendModeNames[i].mode;
7303 return gTrue;
7306 return gFalse;
7307 } else if (obj->isArray()) {
7308 for (i = 0; i < obj->arrayGetLength(); ++i) {
7309 obj->arrayGet(i, &obj2);
7310 if (!obj2.isName()) {
7311 obj2.free();
7312 return gFalse;
7314 for (j = 0; j < nGfxBlendModeNames; ++j) {
7315 if (!strcmp(obj2.getName(), gfxBlendModeNames[j].name)) {
7316 obj2.free();
7317 *mode = gfxBlendModeNames[j].mode;
7318 return gTrue;
7321 obj2.free();
7323 *mode = gfxBlendNormal;
7324 return gTrue;
7325 } else {
7326 return gFalse;