1 //========================================================================
5 // Copyright 1996-2003 Glyph & Cog, LLC
7 //========================================================================
9 //========================================================================
11 // Modified under the Poppler project - http://poppler.freedesktop.org
13 // All changes made under the Poppler project to this file are licensed
14 // under GPL version 2 or later
16 // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
17 // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net>
18 // Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org>
19 // Copyright (C) 2006-2016 Albert Astals Cid <aacid@kde.org>
20 // Copyright (C) 2009, 2012 Koji Otani <sho@bbr.jp>
21 // Copyright (C) 2009, 2011-2016 Thomas Freitag <Thomas.Freitag@alfa.de>
22 // Copyright (C) 2009 Christian Persch <chpe@gnome.org>
23 // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com>
24 // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com>
25 // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com>
26 // Copyright (C) 2012 William Bader <williambader@hotmail.com>
27 // Copyright (C) 2013 Lu Wang <coolwanglu@gmail.com>
28 // Copyright (C) 2013 Hib Eris <hib@hiberis.nl>
29 // Copyright (C) 2013 Fabio D'Urso <fabiodurso@hotmail.it>
30 // Copyright (C) 2015 Adrian Johnson <ajohnson@redneon.com>
31 // Copyright (C) 2016 Marek Kasik <mkasik@redhat.com>
33 // To see a description of the changes please see the Changelog file that
34 // came with your tarball or type make ChangeLog if you are building from git
36 //========================================================================
40 #ifdef USE_GCC_PRAGMAS
41 #pragma implementation
55 #include "GfxState_helpers.h"
57 #include "GlobalParams.h"
58 #include "PopplerCache.h"
59 #include "OutputDev.h"
60 #include "splash/SplashTypes.h"
62 //------------------------------------------------------------------------
64 // Max depth of nested color spaces. This is used to catch infinite
65 // loops in the color space object structure.
66 #define colorSpaceRecursionLimit 8
68 //------------------------------------------------------------------------
70 GBool
Matrix::invertTo(Matrix
*other
) const
74 det
= 1 / determinant();
75 other
->m
[0] = m
[3] * det
;
76 other
->m
[1] = -m
[1] * det
;
77 other
->m
[2] = -m
[2] * det
;
78 other
->m
[3] = m
[0] * det
;
79 other
->m
[4] = (m
[2] * m
[5] - m
[3] * m
[4]) * det
;
80 other
->m
[5] = (m
[1] * m
[4] - m
[0] * m
[5]) * det
;
85 void Matrix::translate(double tx
, double ty
)
87 double x0
= tx
*m
[0] + ty
*m
[2] + m
[4];
88 double y0
= tx
*m
[1] + ty
*m
[3] + m
[5];
93 void Matrix::scale(double sx
, double sy
)
101 void Matrix::transform(double x
, double y
, double *tx
, double *ty
) const
103 double temp_x
, temp_y
;
105 temp_x
= m
[0] * x
+ m
[2] * y
+ m
[4];
106 temp_y
= m
[1] * x
+ m
[3] * y
+ m
[5];
112 // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis
113 double Matrix::norm() const
115 double f
, g
, h
, i
, j
;
117 i
= m
[0]*m
[0] + m
[1]*m
[1];
118 j
= m
[2]*m
[2] + m
[3]*m
[3];
122 h
= m
[0]*m
[2] + m
[1]*m
[3];
124 return sqrt (f
+ hypot (g
, h
));
127 //------------------------------------------------------------------------
129 struct GfxBlendModeInfo
{
134 static const GfxBlendModeInfo gfxBlendModeNames
[] = {
135 { "Normal", gfxBlendNormal
},
136 { "Compatible", gfxBlendNormal
},
137 { "Multiply", gfxBlendMultiply
},
138 { "Screen", gfxBlendScreen
},
139 { "Overlay", gfxBlendOverlay
},
140 { "Darken", gfxBlendDarken
},
141 { "Lighten", gfxBlendLighten
},
142 { "ColorDodge", gfxBlendColorDodge
},
143 { "ColorBurn", gfxBlendColorBurn
},
144 { "HardLight", gfxBlendHardLight
},
145 { "SoftLight", gfxBlendSoftLight
},
146 { "Difference", gfxBlendDifference
},
147 { "Exclusion", gfxBlendExclusion
},
148 { "Hue", gfxBlendHue
},
149 { "Saturation", gfxBlendSaturation
},
150 { "Color", gfxBlendColor
},
151 { "Luminosity", gfxBlendLuminosity
}
154 #define nGfxBlendModeNames \
155 ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo))))
157 //------------------------------------------------------------------------
159 // NB: This must match the GfxColorSpaceMode enum defined in
161 static const char *gfxColorSpaceModeNames
[] = {
175 #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *)))
179 static const std::map
<unsigned int, unsigned int>::size_type CMSCACHE_LIMIT
= 2048;
183 #define cmsColorSpaceSignature icColorSpaceSignature
184 #define cmsSetLogErrorHandler cmsSetErrorHandler
185 #define cmsSigXYZData icSigXYZData
186 #define cmsSigLuvData icSigLuvData
187 #define cmsSigLabData icSigLabData
188 #define cmsSigYCbCrData icSigYCbCrData
189 #define cmsSigYxyData icSigYxyData
190 #define cmsSigRgbData icSigRgbData
191 #define cmsSigHsvData icSigHsvData
192 #define cmsSigHlsData icSigHlsData
193 #define cmsSigCmyData icSigCmyData
194 #define cmsSig3colorData icSig3colorData
195 #define cmsSigGrayData icSigGrayData
196 #define cmsSigCmykData icSigCmykData
197 #define cmsSig4colorData icSig4colorData
198 #define cmsSig2colorData icSig2colorData
199 #define cmsSig5colorData icSig5colorData
200 #define cmsSig6colorData icSig6colorData
201 #define cmsSig7colorData icSig7colorData
202 #define cmsSig8colorData icSig8colorData
203 #define cmsSig9colorData icSig9colorData
204 #define cmsSig10colorData icSig10colorData
205 #define cmsSig11colorData icSig11colorData
206 #define cmsSig12colorData icSig12colorData
207 #define cmsSig13colorData icSig13colorData
208 #define cmsSig14colorData icSig14colorData
209 #define cmsSig15colorData icSig15colorData
213 #define LCMS_FLAGS cmsFLAGS_NOOPTIMIZE | cmsFLAGS_BLACKPOINTCOMPENSATION
216 #define COLOR_PROFILE_DIR "/ColorProfiles/"
217 #define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR
219 void GfxColorTransform::doTransform(void *in
, void *out
, unsigned int size
) {
220 cmsDoTransform(transform
, in
, out
, size
);
223 // transformA should be a cmsHTRANSFORM
224 GfxColorTransform::GfxColorTransform(void *transformA
, int cmsIntentA
, unsigned int inputPixelTypeA
, unsigned int transformPixelTypeA
) {
225 transform
= transformA
;
227 cmsIntent
= cmsIntentA
;
228 inputPixelType
= inputPixelTypeA
;
229 transformPixelType
= transformPixelTypeA
;
232 GfxColorTransform::~GfxColorTransform() {
233 cmsDeleteTransform(transform
);
236 void GfxColorTransform::ref() {
240 unsigned int GfxColorTransform::unref() {
244 static cmsHPROFILE RGBProfile
= NULL
;
245 static GooString
*displayProfileName
= NULL
; // display profile file Name
246 static cmsHPROFILE displayProfile
= NULL
; // display profile
247 static unsigned int displayPixelType
= 0;
248 static GfxColorTransform
*XYZ2DisplayTransform
= NULL
;
250 // convert color space signature to cmsColor type
251 static unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs
);
252 static unsigned int getCMSNChannels(cmsColorSpaceSignature cs
);
253 static cmsHPROFILE
loadColorProfile(const char *fileName
);
255 void GfxColorSpace::setDisplayProfile(void *displayProfileA
) {
256 displayProfile
= displayProfileA
;
257 if (displayProfile
!= NULL
) {
258 cmsHTRANSFORM transform
;
259 unsigned int nChannels
;
261 displayPixelType
= getCMSColorSpaceType(cmsGetColorSpace(displayProfile
));
262 nChannels
= getCMSNChannels(cmsGetColorSpace(displayProfile
));
263 // create transform from XYZ
264 cmsHPROFILE XYZProfile
= cmsCreateXYZProfile();
265 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
267 COLORSPACE_SH(displayPixelType
) |
268 CHANNELS_SH(nChannels
) | BYTES_SH(1),
269 INTENT_RELATIVE_COLORIMETRIC
,LCMS_FLAGS
)) == 0) {
270 error(errSyntaxWarning
, -1, "Can't create Lab transform");
272 XYZ2DisplayTransform
= new GfxColorTransform(transform
, INTENT_RELATIVE_COLORIMETRIC
, PT_XYZ
, displayPixelType
);
274 cmsCloseProfile(XYZProfile
);
278 void GfxColorSpace::setDisplayProfileName(GooString
*name
) {
279 displayProfileName
= name
->copy();
282 cmsHPROFILE
GfxColorSpace::getRGBProfile() {
286 cmsHPROFILE
GfxColorSpace::getDisplayProfile() {
287 return displayProfile
;
292 //------------------------------------------------------------------------
294 //------------------------------------------------------------------------
296 GfxColorSpace::GfxColorSpace() {
297 overprintMask
= 0x0f;
301 GfxColorSpace::~GfxColorSpace() {
304 GfxColorSpace
*GfxColorSpace::parse(GfxResources
*res
, Object
*csObj
, OutputDev
*out
, GfxState
*state
, int recursion
) {
308 if (recursion
> colorSpaceRecursionLimit
) {
309 error(errSyntaxError
, -1, "Loop detected in color space objects");
314 if (csObj
->isName()) {
315 if (csObj
->isName("DeviceGray") || csObj
->isName("G")) {
318 res
->lookupColorSpace("DefaultGray", &objCS
);
319 if (objCS
.isNull()) {
320 cs
= new GfxDeviceGrayColorSpace();
322 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
326 cs
= new GfxDeviceGrayColorSpace();
328 } else if (csObj
->isName("DeviceRGB") || csObj
->isName("RGB")) {
331 res
->lookupColorSpace("DefaultRGB", &objCS
);
332 if (objCS
.isNull()) {
333 cs
= new GfxDeviceRGBColorSpace();
335 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
339 cs
= new GfxDeviceRGBColorSpace();
341 } else if (csObj
->isName("DeviceCMYK") || csObj
->isName("CMYK")) {
344 res
->lookupColorSpace("DefaultCMYK", &objCS
);
345 if (objCS
.isNull()) {
346 cs
= new GfxDeviceCMYKColorSpace();
348 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
352 cs
= new GfxDeviceCMYKColorSpace();
354 } else if (csObj
->isName("Pattern")) {
355 cs
= new GfxPatternColorSpace(NULL
);
357 error(errSyntaxWarning
, -1, "Bad color space '{0:s}'", csObj
->getName());
359 } else if (csObj
->isArray() && csObj
->arrayGetLength() > 0) {
360 csObj
->arrayGet(0, &obj1
);
361 if (obj1
.isName("DeviceGray") || obj1
.isName("G")) {
364 res
->lookupColorSpace("DefaultGray", &objCS
);
365 if (objCS
.isNull()) {
366 cs
= new GfxDeviceGrayColorSpace();
368 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
372 cs
= new GfxDeviceGrayColorSpace();
374 } else if (obj1
.isName("DeviceRGB") || obj1
.isName("RGB")) {
377 res
->lookupColorSpace("DefaultRGB", &objCS
);
378 if (objCS
.isNull()) {
379 cs
= new GfxDeviceRGBColorSpace();
381 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
385 cs
= new GfxDeviceRGBColorSpace();
387 } else if (obj1
.isName("DeviceCMYK") || obj1
.isName("CMYK")) {
390 res
->lookupColorSpace("DefaultCMYK", &objCS
);
391 if (objCS
.isNull()) {
392 cs
= new GfxDeviceCMYKColorSpace();
394 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
398 cs
= new GfxDeviceCMYKColorSpace();
400 } else if (obj1
.isName("CalGray")) {
401 cs
= GfxCalGrayColorSpace::parse(csObj
->getArray(), state
);
402 } else if (obj1
.isName("CalRGB")) {
403 cs
= GfxCalRGBColorSpace::parse(csObj
->getArray(), state
);
404 } else if (obj1
.isName("Lab")) {
405 cs
= GfxLabColorSpace::parse(csObj
->getArray(), state
);
406 } else if (obj1
.isName("ICCBased")) {
407 cs
= GfxICCBasedColorSpace::parse(csObj
->getArray(), out
, state
, recursion
);
408 } else if (obj1
.isName("Indexed") || obj1
.isName("I")) {
409 cs
= GfxIndexedColorSpace::parse(res
, csObj
->getArray(), out
, state
, recursion
);
410 } else if (obj1
.isName("Separation")) {
411 cs
= GfxSeparationColorSpace::parse(res
, csObj
->getArray(), out
, state
, recursion
);
412 } else if (obj1
.isName("DeviceN")) {
413 cs
= GfxDeviceNColorSpace::parse(res
, csObj
->getArray(), out
, state
, recursion
);
414 } else if (obj1
.isName("Pattern")) {
415 cs
= GfxPatternColorSpace::parse(res
, csObj
->getArray(), out
, state
, recursion
);
417 error(errSyntaxWarning
, -1, "Bad color space");
420 } else if (csObj
->isDict()) {
421 csObj
->dictLookup("ColorSpace", &obj1
);
422 if (obj1
.isName("DeviceGray")) {
425 res
->lookupColorSpace("DefaultGray", &objCS
);
426 if (objCS
.isNull()) {
427 cs
= new GfxDeviceGrayColorSpace();
429 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
433 cs
= new GfxDeviceGrayColorSpace();
435 } else if (obj1
.isName("DeviceRGB")) {
438 res
->lookupColorSpace("DefaultRGB", &objCS
);
439 if (objCS
.isNull()) {
440 cs
= new GfxDeviceRGBColorSpace();
442 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
446 cs
= new GfxDeviceRGBColorSpace();
448 } else if (obj1
.isName("DeviceCMYK")) {
451 res
->lookupColorSpace("DefaultCMYK", &objCS
);
452 if (objCS
.isNull()) {
453 cs
= new GfxDeviceCMYKColorSpace();
455 cs
= GfxColorSpace::parse(NULL
, &objCS
, out
, state
);
459 cs
= new GfxDeviceCMYKColorSpace();
462 error(errSyntaxWarning
, -1, "Bad color space dict'");
466 error(errSyntaxWarning
, -1, "Bad color space - expected name or array or dict");
471 void GfxColorSpace::createMapping(GooList
*separationList
, int maxSepComps
) {
475 void GfxColorSpace::getDefaultRanges(double *decodeLow
, double *decodeRange
,
479 for (i
= 0; i
< getNComps(); ++i
) {
485 int GfxColorSpace::getNumColorSpaceModes() {
486 return nGfxColorSpaceModes
;
489 const char *GfxColorSpace::getColorSpaceModeName(int idx
) {
490 return gfxColorSpaceModeNames
[idx
];
494 cmsHPROFILE
loadColorProfile(const char *fileName
)
496 cmsHPROFILE hp
= NULL
;
499 if (fileName
[0] == '/') {
501 // check if open the file
502 if ((fp
= fopen(fileName
,"r")) != NULL
) {
504 hp
= cmsOpenProfileFromFile(fileName
,"r");
508 // try to load from global directory
509 GooString
*path
= new GooString(GLOBAL_COLOR_PROFILE_DIR
);
510 path
->append(fileName
);
511 // check if open the file
512 if ((fp
= fopen(path
->getCString(),"r")) != NULL
) {
514 hp
= cmsOpenProfileFromFile(path
->getCString(),"r");
521 static int CMSError(int ecode
, const char *msg
)
523 error(errSyntaxWarning
, -1, "{0:s}", msg
);
527 static void CMSError(cmsContext
/*contextId*/, cmsUInt32Number
/*ecode*/, const char *text
)
529 error(errSyntaxWarning
, -1, "{0:s}", text
);
533 int GfxColorSpace::setupColorProfiles()
535 static GBool initialized
= gFalse
;
536 cmsHTRANSFORM transform
;
537 unsigned int nChannels
;
540 if (initialized
) return 0;
544 cmsSetLogErrorHandler(CMSError
);
546 if (displayProfile
== NULL
) {
547 // load display profile if it was not already loaded.
548 if (displayProfileName
== NULL
) {
549 displayProfile
= loadColorProfile("display.icc");
550 } else if (displayProfileName
->getLength() > 0) {
551 displayProfile
= loadColorProfile(displayProfileName
->getCString());
555 RGBProfile
= loadColorProfile("RGB.icc");
556 if (RGBProfile
== NULL
) {
557 /* use built in sRGB profile */
558 RGBProfile
= cmsCreate_sRGBProfile();
561 if (displayProfile
!= NULL
) {
562 displayPixelType
= getCMSColorSpaceType(cmsGetColorSpace(displayProfile
));
563 nChannels
= getCMSNChannels(cmsGetColorSpace(displayProfile
));
564 // create transform from XYZ
565 cmsHPROFILE XYZProfile
= cmsCreateXYZProfile();
566 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
568 COLORSPACE_SH(displayPixelType
) |
569 CHANNELS_SH(nChannels
) | BYTES_SH(1),
570 INTENT_RELATIVE_COLORIMETRIC
,LCMS_FLAGS
)) == 0) {
571 error(errSyntaxWarning
, -1, "Can't create Lab transform");
573 XYZ2DisplayTransform
= new GfxColorTransform(transform
, INTENT_RELATIVE_COLORIMETRIC
, PT_XYZ
, displayPixelType
);
575 cmsCloseProfile(XYZProfile
);
580 unsigned int getCMSColorSpaceType(cmsColorSpaceSignature cs
)
592 case cmsSigYCbCrData
:
616 case cmsSig2colorData
:
617 case cmsSig3colorData
:
618 case cmsSig4colorData
:
619 case cmsSig5colorData
:
620 case cmsSig6colorData
:
621 case cmsSig7colorData
:
622 case cmsSig8colorData
:
623 case cmsSig9colorData
:
624 case cmsSig10colorData
:
625 case cmsSig11colorData
:
626 case cmsSig12colorData
:
627 case cmsSig13colorData
:
628 case cmsSig14colorData
:
629 case cmsSig15colorData
:
636 unsigned int getCMSNChannels(cmsColorSpaceSignature cs
)
642 case cmsSigYCbCrData
:
648 case cmsSig3colorData
:
655 case cmsSig4colorData
:
658 case cmsSig2colorData
:
661 case cmsSig5colorData
:
664 case cmsSig6colorData
:
667 case cmsSig7colorData
:
670 case cmsSig8colorData
:
673 case cmsSig9colorData
:
676 case cmsSig10colorData
:
679 case cmsSig11colorData
:
682 case cmsSig12colorData
:
685 case cmsSig13colorData
:
688 case cmsSig14colorData
:
691 case cmsSig15colorData
:
700 //------------------------------------------------------------------------
701 // GfxDeviceGrayColorSpace
702 //------------------------------------------------------------------------
704 GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() {
707 GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() {
710 GfxColorSpace
*GfxDeviceGrayColorSpace::copy() {
711 return new GfxDeviceGrayColorSpace();
714 void GfxDeviceGrayColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
715 *gray
= clip01(color
->c
[0]);
718 void GfxDeviceGrayColorSpace::getGrayLine(Guchar
*in
, Guchar
*out
, int length
) {
719 memcpy (out
, in
, length
);
722 void GfxDeviceGrayColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
723 rgb
->r
= rgb
->g
= rgb
->b
= clip01(color
->c
[0]);
726 void GfxDeviceGrayColorSpace::getRGBLine(Guchar
*in
, unsigned int *out
,
730 for (i
= 0; i
< length
; i
++)
731 out
[i
] = (in
[i
] << 16) | (in
[i
] << 8) | (in
[i
] << 0);
734 void GfxDeviceGrayColorSpace::getRGBLine(Guchar
*in
, Guchar
*out
, int length
) {
735 for (int i
= 0; i
< length
; i
++) {
742 void GfxDeviceGrayColorSpace::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
) {
743 for (int i
= 0; i
< length
; i
++) {
751 void GfxDeviceGrayColorSpace::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
) {
752 for (int i
= 0; i
< length
; i
++) {
760 void GfxDeviceGrayColorSpace::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
) {
761 for (int i
= 0; i
< length
; i
++) {
762 for (int j
= 0; j
< SPOT_NCOMPS
+4; j
++)
765 out
+= (SPOT_NCOMPS
+4);
769 void GfxDeviceGrayColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
770 cmyk
->c
= cmyk
->m
= cmyk
->y
= 0;
771 cmyk
->k
= clip01(gfxColorComp1
- color
->c
[0]);
774 void GfxDeviceGrayColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
775 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
777 deviceN
->c
[3] = clip01(gfxColorComp1
- color
->c
[0]);
780 void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor
*color
) {
784 //------------------------------------------------------------------------
785 // GfxCalGrayColorSpace
786 //------------------------------------------------------------------------
788 GfxCalGrayColorSpace::GfxCalGrayColorSpace() {
789 whiteX
= whiteY
= whiteZ
= 1;
790 blackX
= blackY
= blackZ
= 0;
794 GfxCalGrayColorSpace::~GfxCalGrayColorSpace() {
796 if (transform
!= NULL
) {
797 if (transform
->unref() == 0) delete transform
;
802 GfxColorSpace
*GfxCalGrayColorSpace::copy() {
803 GfxCalGrayColorSpace
*cs
;
805 cs
= new GfxCalGrayColorSpace();
817 cs
->transform
= transform
;
818 if (transform
!= NULL
) transform
->ref();
823 // This is the inverse of MatrixLMN in Example 4.10 from the PostScript
824 // Language Reference, Third Edition.
825 static const double xyzrgb
[3][3] = {
826 { 3.240449, -1.537136, -0.498531 },
827 { -0.969265, 1.876011, 0.041556 },
828 { 0.055643, -0.204026, 1.057229 }
831 GfxColorSpace
*GfxCalGrayColorSpace::parse(Array
*arr
, GfxState
*state
) {
832 GfxCalGrayColorSpace
*cs
;
833 Object obj1
, obj2
, obj3
;
836 if (!obj1
.isDict()) {
837 error(errSyntaxWarning
, -1, "Bad CalGray color space");
841 cs
= new GfxCalGrayColorSpace();
842 if (obj1
.dictLookup("WhitePoint", &obj2
)->isArray() &&
843 obj2
.arrayGetLength() == 3) {
844 obj2
.arrayGet(0, &obj3
);
845 if (likely(obj3
.isNum()))
846 cs
->whiteX
= obj3
.getNum();
848 obj2
.arrayGet(1, &obj3
);
849 if (likely(obj3
.isNum()))
850 cs
->whiteY
= obj3
.getNum();
852 obj2
.arrayGet(2, &obj3
);
853 if (likely(obj3
.isNum()))
854 cs
->whiteZ
= obj3
.getNum();
858 if (obj1
.dictLookup("BlackPoint", &obj2
)->isArray() &&
859 obj2
.arrayGetLength() == 3) {
860 obj2
.arrayGet(0, &obj3
);
861 if (likely(obj3
.isNum()))
862 cs
->blackX
= obj3
.getNum();
864 obj2
.arrayGet(1, &obj3
);
865 if (likely(obj3
.isNum()))
866 cs
->blackY
= obj3
.getNum();
868 obj2
.arrayGet(2, &obj3
);
869 if (likely(obj3
.isNum()))
870 cs
->blackZ
= obj3
.getNum();
874 if (obj1
.dictLookup("Gamma", &obj2
)->isNum()) {
875 cs
->gamma
= obj2
.getNum();
880 cs
->kr
= 1 / (xyzrgb
[0][0] * cs
->whiteX
+
881 xyzrgb
[0][1] * cs
->whiteY
+
882 xyzrgb
[0][2] * cs
->whiteZ
);
883 cs
->kg
= 1 / (xyzrgb
[1][0] * cs
->whiteX
+
884 xyzrgb
[1][1] * cs
->whiteY
+
885 xyzrgb
[1][2] * cs
->whiteZ
);
886 cs
->kb
= 1 / (xyzrgb
[2][0] * cs
->whiteX
+
887 xyzrgb
[2][1] * cs
->whiteY
+
888 xyzrgb
[2][2] * cs
->whiteZ
);
890 cs
->transform
= (state
!= NULL
) ? state
->getXYZ2DisplayTransform() : XYZ2DisplayTransform
;
891 if (cs
->transform
!= NULL
) cs
->transform
->ref();
896 // convert CalGray to media XYZ color space
897 // (not multiply by the white point)
898 void GfxCalGrayColorSpace::getXYZ(GfxColor
*color
,
899 double *pX
, double *pY
, double *pZ
) {
900 const double A
= colToDbl(color
->c
[0]);
901 const double xyzColor
= pow(A
,gamma
);
907 void GfxCalGrayColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
911 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_GRAY
) {
912 Guchar out
[gfxColorMaxComps
];
913 double in
[gfxColorMaxComps
];
916 getXYZ(color
,&X
,&Y
,&Z
);
920 transform
->doTransform(in
,out
,1);
921 *gray
= byteToCol(out
[0]);
926 *gray
= clip01((GfxColorComp
)(0.299 * rgb
.r
+
928 0.114 * rgb
.b
+ 0.5));
931 void GfxCalGrayColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
935 getXYZ(color
,&X
,&Y
,&Z
);
937 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_RGB
) {
938 Guchar out
[gfxColorMaxComps
];
939 double in
[gfxColorMaxComps
];
944 transform
->doTransform(in
,out
,1);
945 rgb
->r
= byteToCol(out
[0]);
946 rgb
->g
= byteToCol(out
[1]);
947 rgb
->b
= byteToCol(out
[2]);
954 // convert XYZ to RGB, including gamut mapping and gamma correction
955 r
= xyzrgb
[0][0] * X
+ xyzrgb
[0][1] * Y
+ xyzrgb
[0][2] * Z
;
956 g
= xyzrgb
[1][0] * X
+ xyzrgb
[1][1] * Y
+ xyzrgb
[1][2] * Z
;
957 b
= xyzrgb
[2][0] * X
+ xyzrgb
[2][1] * Y
+ xyzrgb
[2][2] * Z
;
958 rgb
->r
= dblToCol(sqrt(clip01(r
* kr
)));
959 rgb
->g
= dblToCol(sqrt(clip01(g
* kg
)));
960 rgb
->b
= dblToCol(sqrt(clip01(b
* kb
)));
963 void GfxCalGrayColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
965 GfxColorComp c
, m
, y
, k
;
968 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
969 double in
[gfxColorMaxComps
];
970 Guchar out
[gfxColorMaxComps
];
973 getXYZ(color
,&X
,&Y
,&Z
);
978 transform
->doTransform(in
,out
,1);
979 cmyk
->c
= byteToCol(out
[0]);
980 cmyk
->m
= byteToCol(out
[1]);
981 cmyk
->y
= byteToCol(out
[2]);
982 cmyk
->k
= byteToCol(out
[3]);
987 c
= clip01(gfxColorComp1
- rgb
.r
);
988 m
= clip01(gfxColorComp1
- rgb
.g
);
989 y
= clip01(gfxColorComp1
- rgb
.b
);
1003 void GfxCalGrayColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
1005 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
1007 getCMYK(color
, &cmyk
);
1008 deviceN
->c
[0] = cmyk
.c
;
1009 deviceN
->c
[1] = cmyk
.m
;
1010 deviceN
->c
[2] = cmyk
.y
;
1011 deviceN
->c
[3] = cmyk
.k
;
1014 void GfxCalGrayColorSpace::getDefaultColor(GfxColor
*color
) {
1018 //------------------------------------------------------------------------
1019 // GfxDeviceRGBColorSpace
1020 //------------------------------------------------------------------------
1022 GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() {
1025 GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() {
1028 GfxColorSpace
*GfxDeviceRGBColorSpace::copy() {
1029 return new GfxDeviceRGBColorSpace();
1032 void GfxDeviceRGBColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
1033 *gray
= clip01((GfxColorComp
)(0.3 * color
->c
[0] +
1034 0.59 * color
->c
[1] +
1035 0.11 * color
->c
[2] + 0.5));
1038 void GfxDeviceRGBColorSpace::getGrayLine(Guchar
*in
, Guchar
*out
, int length
) {
1041 for (i
= 0; i
< length
; i
++) {
1043 (in
[i
* 3 + 0] * 19595 +
1044 in
[i
* 3 + 1] * 38469 +
1045 in
[i
* 3 + 2] * 7472) / 65536;
1049 void GfxDeviceRGBColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
1050 rgb
->r
= clip01(color
->c
[0]);
1051 rgb
->g
= clip01(color
->c
[1]);
1052 rgb
->b
= clip01(color
->c
[2]);
1055 void GfxDeviceRGBColorSpace::getRGBLine(Guchar
*in
, unsigned int *out
,
1060 for (i
= 0, p
= in
; i
< length
; i
++, p
+= 3)
1061 out
[i
] = (p
[0] << 16) | (p
[1] << 8) | (p
[2] << 0);
1064 void GfxDeviceRGBColorSpace::getRGBLine(Guchar
*in
, Guchar
*out
, int length
) {
1065 for (int i
= 0; i
< length
; i
++) {
1072 void GfxDeviceRGBColorSpace::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
) {
1073 for (int i
= 0; i
< length
; i
++) {
1081 void GfxDeviceRGBColorSpace::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
) {
1082 GfxColorComp c
, m
, y
, k
;
1084 for (int i
= 0; i
< length
; i
++) {
1085 c
= byteToCol(255 - *in
++);
1086 m
= byteToCol(255 - *in
++);
1087 y
= byteToCol(255 - *in
++);
1095 *out
++ = colToByte(c
- k
);
1096 *out
++ = colToByte(m
- k
);
1097 *out
++ = colToByte(y
- k
);
1098 *out
++ = colToByte(k
);
1102 void GfxDeviceRGBColorSpace::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
) {
1103 GfxColorComp c
, m
, y
, k
;
1105 for (int i
= 0; i
< length
; i
++) {
1106 for (int j
= 0; j
< SPOT_NCOMPS
+4; j
++)
1108 c
= byteToCol(255 - *in
++);
1109 m
= byteToCol(255 - *in
++);
1110 y
= byteToCol(255 - *in
++);
1118 out
[0] = colToByte(c
- k
);
1119 out
[1] = colToByte(m
- k
);
1120 out
[2] = colToByte(y
- k
);
1121 out
[3] = colToByte(k
);
1122 out
+= (SPOT_NCOMPS
+4);
1126 void GfxDeviceRGBColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
1127 GfxColorComp c
, m
, y
, k
;
1129 c
= clip01(gfxColorComp1
- color
->c
[0]);
1130 m
= clip01(gfxColorComp1
- color
->c
[1]);
1131 y
= clip01(gfxColorComp1
- color
->c
[2]);
1145 void GfxDeviceRGBColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
1147 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
1149 getCMYK(color
, &cmyk
);
1150 deviceN
->c
[0] = cmyk
.c
;
1151 deviceN
->c
[1] = cmyk
.m
;
1152 deviceN
->c
[2] = cmyk
.y
;
1153 deviceN
->c
[3] = cmyk
.k
;
1156 void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor
*color
) {
1162 //------------------------------------------------------------------------
1163 // GfxCalRGBColorSpace
1164 //------------------------------------------------------------------------
1166 GfxCalRGBColorSpace::GfxCalRGBColorSpace() {
1167 whiteX
= whiteY
= whiteZ
= 1;
1168 blackX
= blackY
= blackZ
= 0;
1169 gammaR
= gammaG
= gammaB
= 1;
1170 mat
[0] = 1; mat
[1] = 0; mat
[2] = 0;
1171 mat
[3] = 0; mat
[4] = 1; mat
[5] = 0;
1172 mat
[6] = 0; mat
[7] = 0; mat
[8] = 1;
1175 GfxCalRGBColorSpace::~GfxCalRGBColorSpace() {
1177 if (transform
!= NULL
) {
1178 if (transform
->unref() == 0) delete transform
;
1183 GfxColorSpace
*GfxCalRGBColorSpace::copy() {
1184 GfxCalRGBColorSpace
*cs
;
1187 cs
= new GfxCalRGBColorSpace();
1188 cs
->whiteX
= whiteX
;
1189 cs
->whiteY
= whiteY
;
1190 cs
->whiteZ
= whiteZ
;
1191 cs
->blackX
= blackX
;
1192 cs
->blackY
= blackY
;
1193 cs
->blackZ
= blackZ
;
1194 cs
->gammaR
= gammaR
;
1195 cs
->gammaG
= gammaG
;
1196 cs
->gammaB
= gammaB
;
1200 for (i
= 0; i
< 9; ++i
) {
1201 cs
->mat
[i
] = mat
[i
];
1204 cs
->transform
= transform
;
1205 if (transform
!= NULL
) transform
->ref();
1210 GfxColorSpace
*GfxCalRGBColorSpace::parse(Array
*arr
, GfxState
*state
) {
1211 GfxCalRGBColorSpace
*cs
;
1212 Object obj1
, obj2
, obj3
;
1216 if (!obj1
.isDict()) {
1217 error(errSyntaxWarning
, -1, "Bad CalRGB color space");
1221 cs
= new GfxCalRGBColorSpace();
1222 if (obj1
.dictLookup("WhitePoint", &obj2
)->isArray() &&
1223 obj2
.arrayGetLength() == 3) {
1224 obj2
.arrayGet(0, &obj3
);
1225 if (likely(obj3
.isNum()))
1226 cs
->whiteX
= obj3
.getNum();
1228 obj2
.arrayGet(1, &obj3
);
1229 if (likely(obj3
.isNum()))
1230 cs
->whiteY
= obj3
.getNum();
1232 obj2
.arrayGet(2, &obj3
);
1233 if (likely(obj3
.isNum()))
1234 cs
->whiteZ
= obj3
.getNum();
1238 if (obj1
.dictLookup("BlackPoint", &obj2
)->isArray() &&
1239 obj2
.arrayGetLength() == 3) {
1240 obj2
.arrayGet(0, &obj3
);
1241 if (likely(obj3
.isNum()))
1242 cs
->blackX
= obj3
.getNum();
1244 obj2
.arrayGet(1, &obj3
);
1245 if (likely(obj3
.isNum()))
1246 cs
->blackY
= obj3
.getNum();
1248 obj2
.arrayGet(2, &obj3
);
1249 if (likely(obj3
.isNum()))
1250 cs
->blackZ
= obj3
.getNum();
1254 if (obj1
.dictLookup("Gamma", &obj2
)->isArray() &&
1255 obj2
.arrayGetLength() == 3) {
1256 obj2
.arrayGet(0, &obj3
);
1257 if (likely(obj3
.isNum()))
1258 cs
->gammaR
= obj3
.getNum();
1260 obj2
.arrayGet(1, &obj3
);
1261 if (likely(obj3
.isNum()))
1262 cs
->gammaG
= obj3
.getNum();
1264 obj2
.arrayGet(2, &obj3
);
1265 if (likely(obj3
.isNum()))
1266 cs
->gammaB
= obj3
.getNum();
1270 if (obj1
.dictLookup("Matrix", &obj2
)->isArray() &&
1271 obj2
.arrayGetLength() == 9) {
1272 for (i
= 0; i
< 9; ++i
) {
1273 obj2
.arrayGet(i
, &obj3
);
1274 if (likely(obj3
.isNum()))
1275 cs
->mat
[i
] = obj3
.getNum();
1282 cs
->kr
= 1 / (xyzrgb
[0][0] * cs
->whiteX
+
1283 xyzrgb
[0][1] * cs
->whiteY
+
1284 xyzrgb
[0][2] * cs
->whiteZ
);
1285 cs
->kg
= 1 / (xyzrgb
[1][0] * cs
->whiteX
+
1286 xyzrgb
[1][1] * cs
->whiteY
+
1287 xyzrgb
[1][2] * cs
->whiteZ
);
1288 cs
->kb
= 1 / (xyzrgb
[2][0] * cs
->whiteX
+
1289 xyzrgb
[2][1] * cs
->whiteY
+
1290 xyzrgb
[2][2] * cs
->whiteZ
);
1293 cs
->transform
= (state
!= NULL
) ? state
->getXYZ2DisplayTransform() : XYZ2DisplayTransform
;
1294 if (cs
->transform
!= NULL
) cs
->transform
->ref();
1299 // convert CalRGB to XYZ color space
1300 void GfxCalRGBColorSpace::getXYZ(GfxColor
*color
,
1301 double *pX
, double *pY
, double *pZ
) {
1304 A
= pow(colToDbl(color
->c
[0]), gammaR
);
1305 B
= pow(colToDbl(color
->c
[1]), gammaG
);
1306 C
= pow(colToDbl(color
->c
[2]), gammaB
);
1307 *pX
= mat
[0] * A
+ mat
[3] * B
+ mat
[6] * C
;
1308 *pY
= mat
[1] * A
+ mat
[4] * B
+ mat
[7] * C
;
1309 *pZ
= mat
[2] * A
+ mat
[5] * B
+ mat
[8] * C
;
1312 void GfxCalRGBColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
1316 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_GRAY
) {
1317 Guchar out
[gfxColorMaxComps
];
1318 double in
[gfxColorMaxComps
];
1321 getXYZ(color
,&X
,&Y
,&Z
);
1325 transform
->doTransform(in
,out
,1);
1326 *gray
= byteToCol(out
[0]);
1330 getRGB(color
, &rgb
);
1331 *gray
= clip01((GfxColorComp
)(0.299 * rgb
.r
+
1333 0.114 * rgb
.b
+ 0.5));
1336 void GfxCalRGBColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
1340 getXYZ(color
,&X
,&Y
,&Z
);
1342 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_RGB
) {
1343 Guchar out
[gfxColorMaxComps
];
1344 double in
[gfxColorMaxComps
];
1346 in
[0] = clip01(X
/whiteX
);
1347 in
[1] = clip01(Y
/whiteY
);
1348 in
[2] = clip01(Z
/whiteZ
);
1349 transform
->doTransform(in
,out
,1);
1350 rgb
->r
= byteToCol(out
[0]);
1351 rgb
->g
= byteToCol(out
[1]);
1352 rgb
->b
= byteToCol(out
[2]);
1356 // convert XYZ to RGB, including gamut mapping and gamma correction
1357 r
= xyzrgb
[0][0] * X
+ xyzrgb
[0][1] * Y
+ xyzrgb
[0][2] * Z
;
1358 g
= xyzrgb
[1][0] * X
+ xyzrgb
[1][1] * Y
+ xyzrgb
[1][2] * Z
;
1359 b
= xyzrgb
[2][0] * X
+ xyzrgb
[2][1] * Y
+ xyzrgb
[2][2] * Z
;
1360 rgb
->r
= dblToCol(sqrt(clip01(r
)));
1361 rgb
->g
= dblToCol(sqrt(clip01(g
)));
1362 rgb
->b
= dblToCol(sqrt(clip01(b
)));
1365 void GfxCalRGBColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
1367 GfxColorComp c
, m
, y
, k
;
1370 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
1371 double in
[gfxColorMaxComps
];
1372 Guchar out
[gfxColorMaxComps
];
1375 getXYZ(color
,&X
,&Y
,&Z
);
1379 transform
->doTransform(in
,out
,1);
1380 cmyk
->c
= byteToCol(out
[0]);
1381 cmyk
->m
= byteToCol(out
[1]);
1382 cmyk
->y
= byteToCol(out
[2]);
1383 cmyk
->k
= byteToCol(out
[3]);
1387 getRGB(color
, &rgb
);
1388 c
= clip01(gfxColorComp1
- rgb
.r
);
1389 m
= clip01(gfxColorComp1
- rgb
.g
);
1390 y
= clip01(gfxColorComp1
- rgb
.b
);
1404 void GfxCalRGBColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
1406 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
1408 getCMYK(color
, &cmyk
);
1409 deviceN
->c
[0] = cmyk
.c
;
1410 deviceN
->c
[1] = cmyk
.m
;
1411 deviceN
->c
[2] = cmyk
.y
;
1412 deviceN
->c
[3] = cmyk
.k
;
1415 void GfxCalRGBColorSpace::getDefaultColor(GfxColor
*color
) {
1421 //------------------------------------------------------------------------
1422 // GfxDeviceCMYKColorSpace
1423 //------------------------------------------------------------------------
1425 GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() {
1428 GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() {
1431 GfxColorSpace
*GfxDeviceCMYKColorSpace::copy() {
1432 return new GfxDeviceCMYKColorSpace();
1435 void GfxDeviceCMYKColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
1436 *gray
= clip01((GfxColorComp
)(gfxColorComp1
- color
->c
[3]
1438 - 0.59 * color
->c
[1]
1439 - 0.11 * color
->c
[2] + 0.5));
1442 void GfxDeviceCMYKColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
1443 double c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
;
1445 c
= colToDbl(color
->c
[0]);
1446 m
= colToDbl(color
->c
[1]);
1447 y
= colToDbl(color
->c
[2]);
1448 k
= colToDbl(color
->c
[3]);
1453 cmykToRGBMatrixMultiplication(c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
);
1454 rgb
->r
= clip01(dblToCol(r
));
1455 rgb
->g
= clip01(dblToCol(g
));
1456 rgb
->b
= clip01(dblToCol(b
));
1459 static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(Guchar
*&in
, double &r
, double &g
, double &b
)
1461 double c
, m
, y
, k
, c1
, m1
, y1
, k1
;
1463 c
= byteToDbl(*in
++);
1464 m
= byteToDbl(*in
++);
1465 y
= byteToDbl(*in
++);
1466 k
= byteToDbl(*in
++);
1471 cmykToRGBMatrixMultiplication(c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
);
1474 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar
*in
, unsigned int *out
, int length
)
1477 for (int i
= 0; i
< length
; i
++) {
1478 GfxDeviceCMYKColorSpacegetRGBLineHelper(in
, r
, g
, b
);
1479 *out
++ = (dblToByte(clip01(r
)) << 16) | (dblToByte(clip01(g
)) << 8) | dblToByte(clip01(b
));
1483 void GfxDeviceCMYKColorSpace::getRGBLine(Guchar
*in
, Guchar
*out
, int length
)
1487 for (int i
= 0; i
< length
; i
++) {
1488 GfxDeviceCMYKColorSpacegetRGBLineHelper(in
, r
, g
, b
);
1489 *out
++ = dblToByte(clip01(r
));
1490 *out
++ = dblToByte(clip01(g
));
1491 *out
++ = dblToByte(clip01(b
));
1495 void GfxDeviceCMYKColorSpace::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
)
1499 for (int i
= 0; i
< length
; i
++) {
1500 GfxDeviceCMYKColorSpacegetRGBLineHelper(in
, r
, g
, b
);
1501 *out
++ = dblToByte(clip01(r
));
1502 *out
++ = dblToByte(clip01(g
));
1503 *out
++ = dblToByte(clip01(b
));
1508 void GfxDeviceCMYKColorSpace::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
)
1510 for (int i
= 0; i
< length
; i
++) {
1518 void GfxDeviceCMYKColorSpace::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
)
1520 for (int i
= 0; i
< length
; i
++) {
1521 for (int j
= 0; j
< SPOT_NCOMPS
+4; j
++)
1527 out
+= (SPOT_NCOMPS
+4);
1531 void GfxDeviceCMYKColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
1532 cmyk
->c
= clip01(color
->c
[0]);
1533 cmyk
->m
= clip01(color
->c
[1]);
1534 cmyk
->y
= clip01(color
->c
[2]);
1535 cmyk
->k
= clip01(color
->c
[3]);
1538 void GfxDeviceCMYKColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
1539 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
1541 deviceN
->c
[0] = clip01(color
->c
[0]);
1542 deviceN
->c
[1] = clip01(color
->c
[1]);
1543 deviceN
->c
[2] = clip01(color
->c
[2]);
1544 deviceN
->c
[3] = clip01(color
->c
[3]);
1547 void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor
*color
) {
1551 color
->c
[3] = gfxColorComp1
;
1554 //------------------------------------------------------------------------
1556 //------------------------------------------------------------------------
1558 GfxLabColorSpace::GfxLabColorSpace() {
1559 whiteX
= whiteY
= whiteZ
= 1;
1560 blackX
= blackY
= blackZ
= 0;
1565 GfxLabColorSpace::~GfxLabColorSpace() {
1567 if (transform
!= NULL
) {
1568 if (transform
->unref() == 0) delete transform
;
1573 GfxColorSpace
*GfxLabColorSpace::copy() {
1574 GfxLabColorSpace
*cs
;
1576 cs
= new GfxLabColorSpace();
1577 cs
->whiteX
= whiteX
;
1578 cs
->whiteY
= whiteY
;
1579 cs
->whiteZ
= whiteZ
;
1580 cs
->blackX
= blackX
;
1581 cs
->blackY
= blackY
;
1582 cs
->blackZ
= blackZ
;
1591 cs
->transform
= transform
;
1592 if (transform
!= NULL
) transform
->ref();
1597 GfxColorSpace
*GfxLabColorSpace::parse(Array
*arr
, GfxState
*state
) {
1598 GfxLabColorSpace
*cs
;
1599 Object obj1
, obj2
, obj3
;
1602 if (!obj1
.isDict()) {
1603 error(errSyntaxWarning
, -1, "Bad Lab color space");
1607 cs
= new GfxLabColorSpace();
1608 if (obj1
.dictLookup("WhitePoint", &obj2
)->isArray() &&
1609 obj2
.arrayGetLength() == 3) {
1610 obj2
.arrayGet(0, &obj3
);
1611 cs
->whiteX
= obj3
.getNum();
1613 obj2
.arrayGet(1, &obj3
);
1614 cs
->whiteY
= obj3
.getNum();
1616 obj2
.arrayGet(2, &obj3
);
1617 cs
->whiteZ
= obj3
.getNum();
1621 if (obj1
.dictLookup("BlackPoint", &obj2
)->isArray() &&
1622 obj2
.arrayGetLength() == 3) {
1623 obj2
.arrayGet(0, &obj3
);
1624 cs
->blackX
= obj3
.getNum();
1626 obj2
.arrayGet(1, &obj3
);
1627 cs
->blackY
= obj3
.getNum();
1629 obj2
.arrayGet(2, &obj3
);
1630 cs
->blackZ
= obj3
.getNum();
1634 if (obj1
.dictLookup("Range", &obj2
)->isArray() &&
1635 obj2
.arrayGetLength() == 4) {
1636 obj2
.arrayGet(0, &obj3
);
1637 cs
->aMin
= obj3
.getNum();
1639 obj2
.arrayGet(1, &obj3
);
1640 cs
->aMax
= obj3
.getNum();
1642 obj2
.arrayGet(2, &obj3
);
1643 cs
->bMin
= obj3
.getNum();
1645 obj2
.arrayGet(3, &obj3
);
1646 cs
->bMax
= obj3
.getNum();
1652 cs
->kr
= 1 / (xyzrgb
[0][0] * cs
->whiteX
+
1653 xyzrgb
[0][1] * cs
->whiteY
+
1654 xyzrgb
[0][2] * cs
->whiteZ
);
1655 cs
->kg
= 1 / (xyzrgb
[1][0] * cs
->whiteX
+
1656 xyzrgb
[1][1] * cs
->whiteY
+
1657 xyzrgb
[1][2] * cs
->whiteZ
);
1658 cs
->kb
= 1 / (xyzrgb
[2][0] * cs
->whiteX
+
1659 xyzrgb
[2][1] * cs
->whiteY
+
1660 xyzrgb
[2][2] * cs
->whiteZ
);
1663 cs
->transform
= (state
!= NULL
) ? state
->getXYZ2DisplayTransform() : XYZ2DisplayTransform
;
1664 if (cs
->transform
!= NULL
) cs
->transform
->ref();
1669 void GfxLabColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
1673 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_GRAY
) {
1674 Guchar out
[gfxColorMaxComps
];
1675 double in
[gfxColorMaxComps
];
1677 getXYZ(color
, &in
[0], &in
[1], &in
[2]);
1678 transform
->doTransform(in
,out
,1);
1679 *gray
= byteToCol(out
[0]);
1683 getRGB(color
, &rgb
);
1684 *gray
= clip01((GfxColorComp
)(0.299 * rgb
.r
+
1686 0.114 * rgb
.b
+ 0.5));
1689 // convert L*a*b* to media XYZ color space
1690 // (not multiply by the white point)
1691 void GfxLabColorSpace::getXYZ(GfxColor
*color
,
1692 double *pX
, double *pY
, double *pZ
) {
1696 t1
= (colToDbl(color
->c
[0]) + 16) / 116;
1697 t2
= t1
+ colToDbl(color
->c
[1]) / 500;
1698 if (t2
>= (6.0 / 29.0)) {
1701 X
= (108.0 / 841.0) * (t2
- (4.0 / 29.0));
1703 if (t1
>= (6.0 / 29.0)) {
1706 Y
= (108.0 / 841.0) * (t1
- (4.0 / 29.0));
1708 t2
= t1
- colToDbl(color
->c
[2]) / 200;
1709 if (t2
>= (6.0 / 29.0)) {
1712 Z
= (108.0 / 841.0) * (t2
- (4.0 / 29.0));
1719 void GfxLabColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
1723 getXYZ(color
, &X
, &Y
, &Z
);
1725 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_RGB
) {
1726 Guchar out
[gfxColorMaxComps
];
1727 double in
[gfxColorMaxComps
];
1732 transform
->doTransform(in
,out
,1);
1733 rgb
->r
= byteToCol(out
[0]);
1734 rgb
->g
= byteToCol(out
[1]);
1735 rgb
->b
= byteToCol(out
[2]);
1737 } else if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
1738 Guchar out
[gfxColorMaxComps
];
1739 double in
[gfxColorMaxComps
];
1740 double c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
;
1745 transform
->doTransform(in
,out
,1);
1746 c
= byteToDbl(out
[0]);
1747 m
= byteToDbl(out
[1]);
1748 y
= byteToDbl(out
[2]);
1749 k
= byteToDbl(out
[3]);
1754 cmykToRGBMatrixMultiplication(c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
);
1755 rgb
->r
= clip01(dblToCol(r
));
1756 rgb
->g
= clip01(dblToCol(g
));
1757 rgb
->b
= clip01(dblToCol(b
));
1764 // convert XYZ to RGB, including gamut mapping and gamma correction
1765 r
= xyzrgb
[0][0] * X
+ xyzrgb
[0][1] * Y
+ xyzrgb
[0][2] * Z
;
1766 g
= xyzrgb
[1][0] * X
+ xyzrgb
[1][1] * Y
+ xyzrgb
[1][2] * Z
;
1767 b
= xyzrgb
[2][0] * X
+ xyzrgb
[2][1] * Y
+ xyzrgb
[2][2] * Z
;
1768 rgb
->r
= dblToCol(sqrt(clip01(r
* kr
)));
1769 rgb
->g
= dblToCol(sqrt(clip01(g
* kg
)));
1770 rgb
->b
= dblToCol(sqrt(clip01(b
* kb
)));
1773 void GfxLabColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
1775 GfxColorComp c
, m
, y
, k
;
1778 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
1779 double in
[gfxColorMaxComps
];
1780 Guchar out
[gfxColorMaxComps
];
1782 getXYZ(color
, &in
[0], &in
[1], &in
[2]);
1783 transform
->doTransform(in
,out
,1);
1784 cmyk
->c
= byteToCol(out
[0]);
1785 cmyk
->m
= byteToCol(out
[1]);
1786 cmyk
->y
= byteToCol(out
[2]);
1787 cmyk
->k
= byteToCol(out
[3]);
1791 getRGB(color
, &rgb
);
1792 c
= clip01(gfxColorComp1
- rgb
.r
);
1793 m
= clip01(gfxColorComp1
- rgb
.g
);
1794 y
= clip01(gfxColorComp1
- rgb
.b
);
1808 void GfxLabColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
1810 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
1812 getCMYK(color
, &cmyk
);
1813 deviceN
->c
[0] = cmyk
.c
;
1814 deviceN
->c
[1] = cmyk
.m
;
1815 deviceN
->c
[2] = cmyk
.y
;
1816 deviceN
->c
[3] = cmyk
.k
;
1819 void GfxLabColorSpace::getDefaultColor(GfxColor
*color
) {
1822 color
->c
[1] = dblToCol(aMin
);
1823 } else if (aMax
< 0) {
1824 color
->c
[1] = dblToCol(aMax
);
1829 color
->c
[2] = dblToCol(bMin
);
1830 } else if (bMax
< 0) {
1831 color
->c
[2] = dblToCol(bMax
);
1837 void GfxLabColorSpace::getDefaultRanges(double *decodeLow
, double *decodeRange
,
1840 decodeRange
[0] = 100;
1841 decodeLow
[1] = aMin
;
1842 decodeRange
[1] = aMax
- aMin
;
1843 decodeLow
[2] = bMin
;
1844 decodeRange
[2] = bMax
- bMin
;
1847 //------------------------------------------------------------------------
1848 // GfxICCBasedColorSpace
1849 //------------------------------------------------------------------------
1851 class GfxICCBasedColorSpaceKey
: public PopplerCacheKey
1854 GfxICCBasedColorSpaceKey(int numA
, int genA
) : num(numA
), gen(genA
)
1858 bool operator==(const PopplerCacheKey
&key
) const
1860 const GfxICCBasedColorSpaceKey
*k
= static_cast<const GfxICCBasedColorSpaceKey
*>(&key
);
1861 return k
->num
== num
&& k
->gen
== gen
;
1867 class GfxICCBasedColorSpaceItem
: public PopplerCacheItem
1870 GfxICCBasedColorSpaceItem(GfxICCBasedColorSpace
*csA
)
1872 cs
= static_cast<GfxICCBasedColorSpace
*>(csA
->copy());
1875 ~GfxICCBasedColorSpaceItem()
1880 GfxICCBasedColorSpace
*cs
;
1883 GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA
, GfxColorSpace
*altA
,
1884 Ref
*iccProfileStreamA
) {
1887 iccProfileStream
= *iccProfileStreamA
;
1888 rangeMin
[0] = rangeMin
[1] = rangeMin
[2] = rangeMin
[3] = 0;
1889 rangeMax
[0] = rangeMax
[1] = rangeMax
[2] = rangeMax
[3] = 1;
1892 lineTransform
= NULL
;
1896 GfxICCBasedColorSpace::~GfxICCBasedColorSpace() {
1899 if (transform
!= NULL
) {
1900 if (transform
->unref() == 0) delete transform
;
1902 if (lineTransform
!= NULL
) {
1903 if (lineTransform
->unref() == 0) delete lineTransform
;
1908 GfxColorSpace
*GfxICCBasedColorSpace::copy() {
1909 GfxICCBasedColorSpace
*cs
;
1912 cs
= new GfxICCBasedColorSpace(nComps
, alt
->copy(), &iccProfileStream
);
1913 for (i
= 0; i
< 4; ++i
) {
1914 cs
->rangeMin
[i
] = rangeMin
[i
];
1915 cs
->rangeMax
[i
] = rangeMax
[i
];
1918 cs
->transform
= transform
;
1919 if (transform
!= NULL
) transform
->ref();
1920 cs
->lineTransform
= lineTransform
;
1921 if (lineTransform
!= NULL
) lineTransform
->ref();
1926 GfxColorSpace
*GfxICCBasedColorSpace::parse(Array
*arr
, OutputDev
*out
, GfxState
*state
, int recursion
) {
1927 GfxICCBasedColorSpace
*cs
;
1928 Ref iccProfileStreamA
;
1930 GfxColorSpace
*altA
;
1932 Object obj1
, obj2
, obj3
;
1935 if (arr
->getLength() < 2) {
1936 error(errSyntaxError
, -1, "Bad ICCBased color space");
1939 arr
->getNF(1, &obj1
);
1941 iccProfileStreamA
= obj1
.getRef();
1943 iccProfileStreamA
.num
= 0;
1944 iccProfileStreamA
.gen
= 0;
1949 if (out
&& iccProfileStreamA
.num
> 0) {
1950 GfxICCBasedColorSpaceKey
k(iccProfileStreamA
.num
, iccProfileStreamA
.gen
);
1951 GfxICCBasedColorSpaceItem
*item
= static_cast<GfxICCBasedColorSpaceItem
*>(out
->getIccColorSpaceCache()->lookup(k
));
1954 cs
= static_cast<GfxICCBasedColorSpace
*>(item
->cs
->copy());
1955 int transformIntent
= cs
->getIntent();
1956 int cmsIntent
= INTENT_RELATIVE_COLORIMETRIC
;
1957 if (state
!= NULL
) {
1958 const char *intent
= state
->getRenderingIntent();
1959 if (intent
!= NULL
) {
1960 if (strcmp(intent
, "AbsoluteColorimetric") == 0) {
1961 cmsIntent
= INTENT_ABSOLUTE_COLORIMETRIC
;
1962 } else if (strcmp(intent
, "Saturation") == 0) {
1963 cmsIntent
= INTENT_SATURATION
;
1964 } else if (strcmp(intent
, "Perceptual") == 0) {
1965 cmsIntent
= INTENT_PERCEPTUAL
;
1969 if (transformIntent
== cmsIntent
) {
1977 if (!obj1
.isStream()) {
1978 error(errSyntaxWarning
, -1, "Bad ICCBased color space (stream)");
1982 dict
= obj1
.streamGetDict();
1983 if (!dict
->lookup("N", &obj2
)->isInt()) {
1984 error(errSyntaxWarning
, -1, "Bad ICCBased color space (N)");
1989 nCompsA
= obj2
.getInt();
1992 error(errSyntaxError
, -1,
1993 "ICCBased color space with too many ({0:d} > 4) components",
1997 if (dict
->lookup("Alternate", &obj2
)->isNull() ||
1998 !(altA
= GfxColorSpace::parse(NULL
, &obj2
, out
, state
, recursion
+ 1))) {
2001 altA
= new GfxDeviceGrayColorSpace();
2004 altA
= new GfxDeviceRGBColorSpace();
2007 altA
= new GfxDeviceCMYKColorSpace();
2010 error(errSyntaxWarning
, -1, "Bad ICCBased color space - invalid N");
2017 if (altA
->getNComps() != nCompsA
) {
2018 error(errSyntaxWarning
, -1, "Bad ICCBased color space - N doesn't match alt color space");
2023 cs
= new GfxICCBasedColorSpace(nCompsA
, altA
, &iccProfileStreamA
);
2024 if (dict
->lookup("Range", &obj2
)->isArray() &&
2025 obj2
.arrayGetLength() == 2 * nCompsA
) {
2027 for (i
= 0; i
< nCompsA
; ++i
) {
2028 obj2
.arrayGet(2*i
, &obj3
);
2029 obj2
.arrayGet(2*i
+1, &obj4
);
2030 if (obj3
.isNum() && obj4
.isNum()) {
2031 cs
->rangeMin
[i
] = obj3
.getNum();
2032 cs
->rangeMax
[i
] = obj4
.getNum();
2044 Stream
*iccStream
= obj1
.getStream();
2047 profBuf
= iccStream
->toUnsignedChars(&length
, 65536, 65536);
2048 cmsHPROFILE hp
= cmsOpenProfileFromMem(profBuf
,length
);
2051 error(errSyntaxWarning
, -1, "read ICCBased color space profile error");
2053 cmsHPROFILE dhp
= (state
!= NULL
&& state
->getDisplayProfile() != NULL
) ? state
->getDisplayProfile() : displayProfile
;
2055 if (unlikely(RGBProfile
== NULL
)) {
2056 GfxColorSpace::setupColorProfiles();
2060 unsigned int cst
= getCMSColorSpaceType(cmsGetColorSpace(hp
));
2061 unsigned int dNChannels
= getCMSNChannels(cmsGetColorSpace(dhp
));
2062 unsigned int dcst
= getCMSColorSpaceType(cmsGetColorSpace(dhp
));
2063 cmsHTRANSFORM transform
;
2065 int cmsIntent
= INTENT_RELATIVE_COLORIMETRIC
;
2066 if (state
!= NULL
) {
2067 const char *intent
= state
->getRenderingIntent();
2068 if (intent
!= NULL
) {
2069 if (strcmp(intent
, "AbsoluteColorimetric") == 0) {
2070 cmsIntent
= INTENT_ABSOLUTE_COLORIMETRIC
;
2071 } else if (strcmp(intent
, "Saturation") == 0) {
2072 cmsIntent
= INTENT_SATURATION
;
2073 } else if (strcmp(intent
, "Perceptual") == 0) {
2074 cmsIntent
= INTENT_PERCEPTUAL
;
2078 if ((transform
= cmsCreateTransform(hp
,
2079 COLORSPACE_SH(cst
) |CHANNELS_SH(nCompsA
) | BYTES_SH(1),
2081 COLORSPACE_SH(dcst
) |
2082 CHANNELS_SH(dNChannels
) | BYTES_SH(1),
2083 cmsIntent
, LCMS_FLAGS
)) == 0) {
2084 error(errSyntaxWarning
, -1, "Can't create transform");
2085 cs
->transform
= NULL
;
2087 cs
->transform
= new GfxColorTransform(transform
, cmsIntent
, cst
, dcst
);
2089 if (dcst
== PT_RGB
|| dcst
== PT_CMYK
) {
2090 // create line transform only when the display is RGB type color space
2091 if ((transform
= cmsCreateTransform(hp
,
2092 CHANNELS_SH(nCompsA
) | BYTES_SH(1),dhp
,
2093 (dcst
== PT_RGB
) ? TYPE_RGB_8
: TYPE_CMYK_8
, cmsIntent
, LCMS_FLAGS
)) == 0) {
2094 error(errSyntaxWarning
, -1, "Can't create transform");
2095 cs
->lineTransform
= NULL
;
2097 cs
->lineTransform
= new GfxColorTransform(transform
, cmsIntent
, cst
, dcst
);
2100 cmsCloseProfile(hp
);
2103 // put this colorSpace into cache
2104 if (out
&& iccProfileStreamA
.num
> 0) {
2105 GfxICCBasedColorSpaceKey
*k
= new GfxICCBasedColorSpaceKey(iccProfileStreamA
.num
, iccProfileStreamA
.gen
);
2106 GfxICCBasedColorSpaceItem
*item
= new GfxICCBasedColorSpaceItem(cs
);
2107 out
->getIccColorSpaceCache()->put(k
, item
);
2113 void GfxICCBasedColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
2115 if (transform
!= 0 && transform
->getTransformPixelType() == PT_GRAY
) {
2116 Guchar in
[gfxColorMaxComps
];
2117 Guchar out
[gfxColorMaxComps
];
2119 if (nComps
== 3 && transform
->getInputPixelType() == PT_Lab
) {
2120 in
[0] = colToByte(dblToCol(colToDbl(color
->c
[0]) / 100.0));
2121 in
[1] = colToByte(dblToCol((colToDbl(color
->c
[1]) + 128.0) / 255.0));
2122 in
[2] = colToByte(dblToCol((colToDbl(color
->c
[2]) + 128.0) / 255.0));
2124 for (int i
= 0;i
< nComps
;i
++) {
2125 in
[i
] = colToByte(color
->c
[i
]);
2129 unsigned int key
= 0;
2130 for (int j
= 0; j
< nComps
; j
++) {
2131 key
= (key
<< 8) + in
[j
];
2133 std::map
<unsigned int, unsigned int>::iterator it
= cmsCache
.find(key
);
2134 if (it
!= cmsCache
.end()) {
2135 unsigned int value
= it
->second
;
2136 *gray
= byteToCol(value
& 0xff);
2140 transform
->doTransform(in
,out
,1);
2141 *gray
= byteToCol(out
[0]);
2142 if (nComps
<= 4 && cmsCache
.size() <= CMSCACHE_LIMIT
) {
2143 unsigned int key
= 0;
2144 for (int j
= 0; j
< nComps
; j
++) {
2145 key
= (key
<< 8) + in
[j
];
2147 unsigned int value
= out
[0];
2148 cmsCache
.insert(std::pair
<unsigned int, unsigned int>(key
, value
));
2153 *gray
= clip01((GfxColorComp
)(0.3 * rgb
.r
+
2155 0.11 * rgb
.b
+ 0.5));
2158 alt
->getGray(color
, gray
);
2162 void GfxICCBasedColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
2164 if (transform
!= 0 && transform
->getTransformPixelType() == PT_RGB
) {
2165 Guchar in
[gfxColorMaxComps
];
2166 Guchar out
[gfxColorMaxComps
];
2168 if (nComps
== 3 && transform
->getInputPixelType() == PT_Lab
) {
2169 in
[0] = colToByte(dblToCol(colToDbl(color
->c
[0]) / 100.0));
2170 in
[1] = colToByte(dblToCol((colToDbl(color
->c
[1]) + 128.0) / 255.0));
2171 in
[2] = colToByte(dblToCol((colToDbl(color
->c
[2]) + 128.0) / 255.0));
2173 for (int i
= 0;i
< nComps
;i
++) {
2174 in
[i
] = colToByte(color
->c
[i
]);
2178 unsigned int key
= 0;
2179 for (int j
= 0; j
< nComps
; j
++) {
2180 key
= (key
<< 8) + in
[j
];
2182 std::map
<unsigned int, unsigned int>::iterator it
= cmsCache
.find(key
);
2183 if (it
!= cmsCache
.end()) {
2184 unsigned int value
= it
->second
;
2185 rgb
->r
= byteToCol(value
>> 16);
2186 rgb
->g
= byteToCol((value
>> 8) & 0xff);
2187 rgb
->b
= byteToCol(value
& 0xff);
2191 transform
->doTransform(in
,out
,1);
2192 rgb
->r
= byteToCol(out
[0]);
2193 rgb
->g
= byteToCol(out
[1]);
2194 rgb
->b
= byteToCol(out
[2]);
2195 if (nComps
<= 4 && cmsCache
.size() <= CMSCACHE_LIMIT
) {
2196 unsigned int key
= 0;
2197 for (int j
= 0; j
< nComps
; j
++) {
2198 key
= (key
<< 8) + in
[j
];
2200 unsigned int value
= (out
[0] << 16) + (out
[1] << 8) + out
[2];
2201 cmsCache
.insert(std::pair
<unsigned int, unsigned int>(key
, value
));
2203 } else if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
2204 Guchar in
[gfxColorMaxComps
];
2205 Guchar out
[gfxColorMaxComps
];
2206 double c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
;
2208 if (nComps
== 3 && transform
->getInputPixelType() == PT_Lab
) {
2209 in
[0] = colToByte(dblToCol(colToDbl(color
->c
[0]) / 100.0));
2210 in
[1] = colToByte(dblToCol((colToDbl(color
->c
[1]) + 128.0) / 255.0));
2211 in
[2] = colToByte(dblToCol((colToDbl(color
->c
[2]) + 128.0) / 255.0));
2213 for (int i
= 0;i
< nComps
;i
++) {
2214 in
[i
] = colToByte(color
->c
[i
]);
2218 unsigned int key
= 0;
2219 for (int j
= 0; j
< nComps
; j
++) {
2220 key
= (key
<< 8) + in
[j
];
2222 std::map
<unsigned int, unsigned int>::iterator it
= cmsCache
.find(key
);
2223 if (it
!= cmsCache
.end()) {
2224 unsigned int value
= it
->second
;
2225 rgb
->r
= byteToCol(value
>> 16);
2226 rgb
->g
= byteToCol((value
>> 8) & 0xff);
2227 rgb
->b
= byteToCol(value
& 0xff);
2231 transform
->doTransform(in
,out
,1);
2232 c
= byteToDbl(out
[0]);
2233 m
= byteToDbl(out
[1]);
2234 y
= byteToDbl(out
[2]);
2235 k
= byteToDbl(out
[3]);
2240 cmykToRGBMatrixMultiplication(c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
);
2241 rgb
->r
= clip01(dblToCol(r
));
2242 rgb
->g
= clip01(dblToCol(g
));
2243 rgb
->b
= clip01(dblToCol(b
));
2244 if (nComps
<= 4 && cmsCache
.size() <= CMSCACHE_LIMIT
) {
2245 unsigned int key
= 0;
2246 for (int j
= 0; j
< nComps
; j
++) {
2247 key
= (key
<< 8) + in
[j
];
2249 unsigned int value
= (dblToByte(r
) << 16) + (dblToByte(g
) << 8) + dblToByte(b
);
2250 cmsCache
.insert(std::pair
<unsigned int, unsigned int>(key
, value
));
2253 alt
->getRGB(color
, rgb
);
2256 alt
->getRGB(color
, rgb
);
2260 void GfxICCBasedColorSpace::getRGBLine(Guchar
*in
, unsigned int *out
,
2263 if (lineTransform
!= 0 && lineTransform
->getTransformPixelType() == PT_RGB
) {
2264 Guchar
* tmp
= (Guchar
*)gmallocn(3 * length
, sizeof(Guchar
));
2265 lineTransform
->doTransform(in
, tmp
, length
);
2266 for (int i
= 0; i
< length
; ++i
) {
2267 Guchar
*current
= tmp
+ (i
* 3);
2268 out
[i
] = (current
[0] << 16) | (current
[1] << 8) | current
[2];
2272 alt
->getRGBLine(in
, out
, length
);
2275 alt
->getRGBLine(in
, out
, length
);
2279 void GfxICCBasedColorSpace::getRGBLine(Guchar
*in
, Guchar
*out
, int length
) {
2281 if (lineTransform
!= 0 && lineTransform
->getTransformPixelType() == PT_RGB
) {
2282 Guchar
* tmp
= (Guchar
*)gmallocn(3 * length
, sizeof(Guchar
));
2283 lineTransform
->doTransform(in
, tmp
, length
);
2284 Guchar
*current
= tmp
;
2285 for (int i
= 0; i
< length
; ++i
) {
2286 *out
++ = *current
++;
2287 *out
++ = *current
++;
2288 *out
++ = *current
++;
2291 } else if (lineTransform
!= NULL
&& lineTransform
->getTransformPixelType() == PT_CMYK
) {
2292 Guchar
* tmp
= (Guchar
*)gmallocn(4 * length
, sizeof(Guchar
));
2293 lineTransform
->doTransform(in
, tmp
, length
);
2294 Guchar
*current
= tmp
;
2295 double c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
;
2296 for (int i
= 0; i
< length
; ++i
) {
2297 c
= byteToDbl(*current
++);
2298 m
= byteToDbl(*current
++);
2299 y
= byteToDbl(*current
++);
2300 k
= byteToDbl(*current
++);
2305 cmykToRGBMatrixMultiplication(c
, m
, y
, k
, c1
, m1
, y1
, k1
, r
, g
, b
);
2306 *out
++ = dblToByte(r
);
2307 *out
++ = dblToByte(g
);
2308 *out
++ = dblToByte(b
);
2312 alt
->getRGBLine(in
, out
, length
);
2315 alt
->getRGBLine(in
, out
, length
);
2319 void GfxICCBasedColorSpace::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
) {
2321 if (lineTransform
!= 0 && lineTransform
->getTransformPixelType() == PT_RGB
) {
2322 Guchar
* tmp
= (Guchar
*)gmallocn(3 * length
, sizeof(Guchar
));
2323 lineTransform
->doTransform(in
, tmp
, length
);
2324 Guchar
*current
= tmp
;
2325 for (int i
= 0; i
< length
; ++i
) {
2326 *out
++ = *current
++;
2327 *out
++ = *current
++;
2328 *out
++ = *current
++;
2333 alt
->getRGBXLine(in
, out
, length
);
2336 alt
->getRGBXLine(in
, out
, length
);
2340 void GfxICCBasedColorSpace::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
) {
2342 if (lineTransform
!= NULL
&& lineTransform
->getTransformPixelType() == PT_CMYK
) {
2343 transform
->doTransform(in
,out
,length
);
2344 } else if (lineTransform
!= NULL
&& nComps
!= 4) {
2345 GfxColorComp c
, m
, y
, k
;
2346 Guchar
* tmp
= (Guchar
*)gmallocn(3 * length
, sizeof(Guchar
));
2347 getRGBLine(in
, tmp
, length
);
2349 for (int i
= 0; i
< length
; i
++) {
2350 c
= byteToCol(255 - *p
++);
2351 m
= byteToCol(255 - *p
++);
2352 y
= byteToCol(255 - *p
++);
2360 *out
++ = colToByte(c
- k
);
2361 *out
++ = colToByte(m
- k
);
2362 *out
++ = colToByte(y
- k
);
2363 *out
++ = colToByte(k
);
2367 alt
->getCMYKLine(in
, out
, length
);
2370 alt
->getCMYKLine(in
, out
, length
);
2374 void GfxICCBasedColorSpace::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
) {
2376 if (lineTransform
!= NULL
&& lineTransform
->getTransformPixelType() == PT_CMYK
) {
2377 Guchar
* tmp
= (Guchar
*)gmallocn(4 * length
, sizeof(Guchar
));
2378 transform
->doTransform(in
,tmp
,length
);
2380 for (int i
= 0; i
< length
; i
++) {
2381 for (int j
= 0; j
< 4; j
++)
2383 for (int j
= 4; j
< SPOT_NCOMPS
+4; j
++)
2387 } else if (lineTransform
!= NULL
&& nComps
!= 4) {
2388 GfxColorComp c
, m
, y
, k
;
2389 Guchar
* tmp
= (Guchar
*)gmallocn(3 * length
, sizeof(Guchar
));
2390 getRGBLine(in
, tmp
, length
);
2392 for (int i
= 0; i
< length
; i
++) {
2393 for (int j
= 0; j
< SPOT_NCOMPS
+4; j
++)
2395 c
= byteToCol(255 - *p
++);
2396 m
= byteToCol(255 - *p
++);
2397 y
= byteToCol(255 - *p
++);
2405 out
[0] = colToByte(c
- k
);
2406 out
[1] = colToByte(m
- k
);
2407 out
[2] = colToByte(y
- k
);
2408 out
[3] = colToByte(k
);
2409 out
+= (SPOT_NCOMPS
+4);
2413 alt
->getDeviceNLine(in
, out
, length
);
2416 alt
->getDeviceNLine(in
, out
, length
);
2420 void GfxICCBasedColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
2422 if (transform
!= NULL
&& transform
->getTransformPixelType() == PT_CMYK
) {
2423 Guchar in
[gfxColorMaxComps
];
2424 Guchar out
[gfxColorMaxComps
];
2426 if (nComps
== 3 && transform
->getInputPixelType() == PT_Lab
) {
2427 in
[0] = colToByte(dblToCol(colToDbl(color
->c
[0]) / 100.0));
2428 in
[1] = colToByte(dblToCol((colToDbl(color
->c
[1]) + 128.0) / 255.0));
2429 in
[2] = colToByte(dblToCol((colToDbl(color
->c
[2]) + 128.0) / 255.0));
2431 for (int i
= 0;i
< nComps
;i
++) {
2432 in
[i
] = colToByte(color
->c
[i
]);
2436 unsigned int key
= 0;
2437 for (int j
= 0; j
< nComps
; j
++) {
2438 key
= (key
<< 8) + in
[j
];
2440 std::map
<unsigned int, unsigned int>::iterator it
= cmsCache
.find(key
);
2441 if (it
!= cmsCache
.end()) {
2442 unsigned int value
= it
->second
;
2443 cmyk
->c
= byteToCol(value
>> 24);
2444 cmyk
->m
= byteToCol((value
>> 16) & 0xff);
2445 cmyk
->y
= byteToCol((value
>> 8) & 0xff);
2446 cmyk
->k
= byteToCol(value
& 0xff);
2450 transform
->doTransform(in
,out
,1);
2451 cmyk
->c
= byteToCol(out
[0]);
2452 cmyk
->m
= byteToCol(out
[1]);
2453 cmyk
->y
= byteToCol(out
[2]);
2454 cmyk
->k
= byteToCol(out
[3]);
2455 if (nComps
<= 4 && cmsCache
.size() <= CMSCACHE_LIMIT
) {
2456 unsigned int key
= 0;
2457 for (int j
= 0; j
< nComps
; j
++) {
2458 key
= (key
<< 8) + in
[j
];
2460 unsigned int value
= (out
[0] << 24) + (out
[1] << 16) + (out
[2] << 8) + out
[3];
2461 cmsCache
.insert(std::pair
<unsigned int, unsigned int>(key
, value
));
2463 } else if (nComps
!= 4 && transform
!= NULL
&& transform
->getTransformPixelType() == PT_RGB
) {
2465 GfxColorComp c
, m
, y
, k
;
2468 c
= clip01(gfxColorComp1
- rgb
.r
);
2469 m
= clip01(gfxColorComp1
- rgb
.g
);
2470 y
= clip01(gfxColorComp1
- rgb
.b
);
2483 alt
->getCMYK(color
, cmyk
);
2486 alt
->getCMYK(color
, cmyk
);
2490 GBool
GfxICCBasedColorSpace::useGetRGBLine() {
2492 return lineTransform
!= NULL
|| alt
->useGetRGBLine();
2494 return alt
->useGetRGBLine();
2498 GBool
GfxICCBasedColorSpace::useGetCMYKLine() {
2500 return lineTransform
!= NULL
|| alt
->useGetCMYKLine();
2502 return alt
->useGetCMYKLine();
2506 GBool
GfxICCBasedColorSpace::useGetDeviceNLine() {
2508 return lineTransform
!= NULL
|| alt
->useGetDeviceNLine();
2510 return alt
->useGetDeviceNLine();
2514 void GfxICCBasedColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
2516 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
2518 getCMYK(color
, &cmyk
);
2519 deviceN
->c
[0] = cmyk
.c
;
2520 deviceN
->c
[1] = cmyk
.m
;
2521 deviceN
->c
[2] = cmyk
.y
;
2522 deviceN
->c
[3] = cmyk
.k
;
2525 void GfxICCBasedColorSpace::getDefaultColor(GfxColor
*color
) {
2528 for (i
= 0; i
< nComps
; ++i
) {
2529 if (rangeMin
[i
] > 0) {
2530 color
->c
[i
] = dblToCol(rangeMin
[i
]);
2531 } else if (rangeMax
[i
] < 0) {
2532 color
->c
[i
] = dblToCol(rangeMax
[i
]);
2539 void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow
,
2540 double *decodeRange
,
2542 alt
->getDefaultRanges(decodeLow
, decodeRange
, maxImgPixel
);
2545 // this is nominally correct, but some PDF files don't set the
2546 // correct ranges in the ICCBased dict
2549 for (i
= 0; i
< nComps
; ++i
) {
2550 decodeLow
[i
] = rangeMin
[i
];
2551 decodeRange
[i
] = rangeMax
[i
] - rangeMin
[i
];
2556 //------------------------------------------------------------------------
2557 // GfxIndexedColorSpace
2558 //------------------------------------------------------------------------
2560 GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace
*baseA
,
2563 indexHigh
= indexHighA
;
2564 lookup
= (Guchar
*)gmallocn((indexHigh
+ 1) * base
->getNComps(),
2566 overprintMask
= base
->getOverprintMask();
2569 GfxIndexedColorSpace::~GfxIndexedColorSpace() {
2574 GfxColorSpace
*GfxIndexedColorSpace::copy() {
2575 GfxIndexedColorSpace
*cs
;
2577 cs
= new GfxIndexedColorSpace(base
->copy(), indexHigh
);
2578 memcpy(cs
->lookup
, lookup
,
2579 (indexHigh
+ 1) * base
->getNComps() * sizeof(Guchar
));
2583 GfxColorSpace
*GfxIndexedColorSpace::parse(GfxResources
*res
, Array
*arr
, OutputDev
*out
, GfxState
*state
, int recursion
) {
2584 GfxIndexedColorSpace
*cs
;
2585 GfxColorSpace
*baseA
;
2591 if (arr
->getLength() != 4) {
2592 error(errSyntaxWarning
, -1, "Bad Indexed color space");
2596 if (!(baseA
= GfxColorSpace::parse(res
, &obj1
, out
, state
, recursion
+ 1))) {
2597 error(errSyntaxWarning
, -1, "Bad Indexed color space (base color space)");
2601 if (!arr
->get(2, &obj1
)->isInt()) {
2602 error(errSyntaxWarning
, -1, "Bad Indexed color space (hival)");
2606 indexHighA
= obj1
.getInt();
2607 if (indexHighA
< 0 || indexHighA
> 255) {
2608 // the PDF spec requires indexHigh to be in [0,255] -- allowing
2609 // values larger than 255 creates a security hole: if nComps *
2610 // indexHigh is greater than 2^31, the loop below may overwrite
2611 // past the end of the array
2612 int previousValue
= indexHighA
;
2613 if (indexHighA
< 0) indexHighA
= 0;
2614 else indexHighA
= 255;
2615 error(errSyntaxWarning
, -1, "Bad Indexed color space (invalid indexHigh value, was {0:d} using {1:d} to try to recover)", previousValue
, indexHighA
);
2618 cs
= new GfxIndexedColorSpace(baseA
, indexHighA
);
2620 n
= baseA
->getNComps();
2621 if (obj1
.isStream()) {
2623 for (i
= 0; i
<= indexHighA
; ++i
) {
2624 const int readChars
= obj1
.streamGetChars(n
, &cs
->lookup
[i
*n
]);
2625 for (j
= readChars
; j
< n
; ++j
) {
2626 error(errSyntaxWarning
, -1, "Bad Indexed color space (lookup table stream too short) padding with zeroes");
2627 cs
->lookup
[i
*n
+ j
] = 0;
2631 } else if (obj1
.isString()) {
2632 if (obj1
.getString()->getLength() < (indexHighA
+ 1) * n
) {
2633 error(errSyntaxWarning
, -1, "Bad Indexed color space (lookup table string too short)");
2636 s
= obj1
.getString()->getCString();
2637 for (i
= 0; i
<= indexHighA
; ++i
) {
2638 for (j
= 0; j
< n
; ++j
) {
2639 cs
->lookup
[i
*n
+ j
] = (Guchar
)*s
++;
2643 error(errSyntaxWarning
, -1, "Bad Indexed color space (lookup table)");
2657 GfxColor
*GfxIndexedColorSpace::mapColorToBase(GfxColor
*color
,
2658 GfxColor
*baseColor
) {
2660 double low
[gfxColorMaxComps
], range
[gfxColorMaxComps
];
2663 n
= base
->getNComps();
2664 base
->getDefaultRanges(low
, range
, indexHigh
);
2665 const int idx
= (int)(colToDbl(color
->c
[0]) + 0.5) * n
;
2666 if (likely((idx
+ n
< (indexHigh
+ 1) * base
->getNComps()) && idx
>= 0)) {
2668 for (i
= 0; i
< n
; ++i
) {
2669 baseColor
->c
[i
] = dblToCol(low
[i
] + (p
[i
] / 255.0) * range
[i
]);
2672 for (i
= 0; i
< n
; ++i
) {
2673 baseColor
->c
[i
] = 0;
2679 void GfxIndexedColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
2682 base
->getGray(mapColorToBase(color
, &color2
), gray
);
2685 void GfxIndexedColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
2688 base
->getRGB(mapColorToBase(color
, &color2
), rgb
);
2691 void GfxIndexedColorSpace::getRGBLine(Guchar
*in
, unsigned int *out
, int length
) {
2695 n
= base
->getNComps();
2696 line
= (Guchar
*) gmallocn (length
, n
);
2697 for (i
= 0; i
< length
; i
++)
2698 for (j
= 0; j
< n
; j
++)
2699 line
[i
* n
+ j
] = lookup
[in
[i
] * n
+ j
];
2701 base
->getRGBLine(line
, out
, length
);
2706 void GfxIndexedColorSpace::getRGBLine(Guchar
*in
, Guchar
*out
, int length
)
2711 n
= base
->getNComps();
2712 line
= (Guchar
*) gmallocn (length
, n
);
2713 for (i
= 0; i
< length
; i
++)
2714 for (j
= 0; j
< n
; j
++)
2715 line
[i
* n
+ j
] = lookup
[in
[i
] * n
+ j
];
2717 base
->getRGBLine(line
, out
, length
);
2722 void GfxIndexedColorSpace::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
)
2727 n
= base
->getNComps();
2728 line
= (Guchar
*) gmallocn (length
, n
);
2729 for (i
= 0; i
< length
; i
++)
2730 for (j
= 0; j
< n
; j
++)
2731 line
[i
* n
+ j
] = lookup
[in
[i
] * n
+ j
];
2733 base
->getRGBXLine(line
, out
, length
);
2738 void GfxIndexedColorSpace::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
) {
2742 n
= base
->getNComps();
2743 line
= (Guchar
*) gmallocn (length
, n
);
2744 for (i
= 0; i
< length
; i
++)
2745 for (j
= 0; j
< n
; j
++)
2746 line
[i
* n
+ j
] = lookup
[in
[i
] * n
+ j
];
2748 base
->getCMYKLine(line
, out
, length
);
2753 void GfxIndexedColorSpace::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
) {
2757 n
= base
->getNComps();
2758 line
= (Guchar
*) gmallocn (length
, n
);
2759 for (i
= 0; i
< length
; i
++)
2760 for (j
= 0; j
< n
; j
++)
2761 line
[i
* n
+ j
] = lookup
[in
[i
] * n
+ j
];
2763 base
->getDeviceNLine(line
, out
, length
);
2768 void GfxIndexedColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
2771 base
->getCMYK(mapColorToBase(color
, &color2
), cmyk
);
2774 void GfxIndexedColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
2777 base
->getDeviceN(mapColorToBase(color
, &color2
), deviceN
);
2780 void GfxIndexedColorSpace::getDefaultColor(GfxColor
*color
) {
2784 void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow
,
2785 double *decodeRange
,
2788 decodeRange
[0] = maxImgPixel
;
2791 //------------------------------------------------------------------------
2792 // GfxSeparationColorSpace
2793 //------------------------------------------------------------------------
2795 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString
*nameA
,
2796 GfxColorSpace
*altA
,
2801 nonMarking
= !name
->cmp("None");
2802 if (!name
->cmp("Cyan")) {
2803 overprintMask
= 0x01;
2804 } else if (!name
->cmp("Magenta")) {
2805 overprintMask
= 0x02;
2806 } else if (!name
->cmp("Yellow")) {
2807 overprintMask
= 0x04;
2808 } else if (!name
->cmp("Black")) {
2809 overprintMask
= 0x08;
2810 } else if (!name
->cmp("All")) {
2811 overprintMask
= 0xffffffff;
2815 GfxSeparationColorSpace::GfxSeparationColorSpace(GooString
*nameA
,
2816 GfxColorSpace
*altA
,
2819 Guint overprintMaskA
,
2824 nonMarking
= nonMarkingA
;
2825 overprintMask
= overprintMaskA
;
2829 GfxSeparationColorSpace::~GfxSeparationColorSpace() {
2833 if (mapping
!= NULL
)
2837 GfxColorSpace
*GfxSeparationColorSpace::copy() {
2838 int *mappingA
= NULL
;
2839 if (mapping
!= NULL
) {
2840 mappingA
= (int *) gmalloc(sizeof(int));
2841 *mappingA
= *mapping
;
2843 return new GfxSeparationColorSpace(name
->copy(), alt
->copy(), func
->copy(),
2844 nonMarking
, overprintMask
, mappingA
);
2847 //~ handle the 'All' and 'None' colorants
2848 GfxColorSpace
*GfxSeparationColorSpace::parse(GfxResources
*res
, Array
*arr
, OutputDev
*out
, GfxState
*state
, int recursion
) {
2849 GfxSeparationColorSpace
*cs
;
2851 GfxColorSpace
*altA
;
2855 if (arr
->getLength() != 4) {
2856 error(errSyntaxWarning
, -1, "Bad Separation color space");
2859 if (!arr
->get(1, &obj1
)->isName()) {
2860 error(errSyntaxWarning
, -1, "Bad Separation color space (name)");
2863 nameA
= new GooString(obj1
.getName());
2866 if (!(altA
= GfxColorSpace::parse(res
, &obj1
, out
, state
, recursion
+ 1))) {
2867 error(errSyntaxWarning
, -1, "Bad Separation color space (alternate color space)");
2872 if (!(funcA
= Function::parse(&obj1
))) {
2875 if (funcA
->getInputSize() != 1) {
2876 error(errSyntaxWarning
, -1, "Bad SeparationColorSpace function");
2880 cs
= new GfxSeparationColorSpace(nameA
, altA
, funcA
);
2895 void GfxSeparationColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
2897 double c
[gfxColorMaxComps
];
2901 if (alt
->getMode() == csDeviceGray
&& name
->cmp("Black") == 0) {
2902 *gray
= clip01(gfxColorComp1
- color
->c
[0]);
2904 x
= colToDbl(color
->c
[0]);
2905 func
->transform(&x
, c
);
2906 for (i
= 0; i
< alt
->getNComps(); ++i
) {
2907 color2
.c
[i
] = dblToCol(c
[i
]);
2909 alt
->getGray(&color2
, gray
);
2913 void GfxSeparationColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
2915 double c
[gfxColorMaxComps
];
2919 if (alt
->getMode() == csDeviceGray
&& name
->cmp("Black") == 0) {
2920 rgb
->r
= clip01(gfxColorComp1
- color
->c
[0]);
2921 rgb
->g
= clip01(gfxColorComp1
- color
->c
[0]);
2922 rgb
->b
= clip01(gfxColorComp1
- color
->c
[0]);
2924 x
= colToDbl(color
->c
[0]);
2925 func
->transform(&x
, c
);
2926 for (i
= 0; i
< alt
->getNComps(); ++i
) {
2927 color2
.c
[i
] = dblToCol(c
[i
]);
2929 alt
->getRGB(&color2
, rgb
);
2933 void GfxSeparationColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
2935 double c
[gfxColorMaxComps
];
2939 if (name
->cmp("Black") == 0) {
2943 cmyk
->k
= color
->c
[0];
2944 } else if (name
->cmp("Cyan") == 0) {
2945 cmyk
->c
= color
->c
[0];
2949 } else if (name
->cmp("Magenta") == 0) {
2951 cmyk
->m
= color
->c
[0];
2954 } else if (name
->cmp("Yellow") == 0) {
2957 cmyk
->y
= color
->c
[0];
2960 x
= colToDbl(color
->c
[0]);
2961 func
->transform(&x
, c
);
2962 for (i
= 0; i
< alt
->getNComps(); ++i
) {
2963 color2
.c
[i
] = dblToCol(c
[i
]);
2965 alt
->getCMYK(&color2
, cmyk
);
2969 void GfxSeparationColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
2970 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
2972 if (mapping
== NULL
|| mapping
[0] == -1) {
2975 getCMYK(color
, &cmyk
);
2976 deviceN
->c
[0] = cmyk
.c
;
2977 deviceN
->c
[1] = cmyk
.m
;
2978 deviceN
->c
[2] = cmyk
.y
;
2979 deviceN
->c
[3] = cmyk
.k
;
2981 deviceN
->c
[mapping
[0]] = color
->c
[0];
2985 void GfxSeparationColorSpace::getDefaultColor(GfxColor
*color
) {
2986 color
->c
[0] = gfxColorComp1
;
2989 void GfxSeparationColorSpace::createMapping(GooList
*separationList
, int maxSepComps
) {
2992 mapping
= (int *)gmalloc(sizeof(int));
2993 switch (overprintMask
) {
3007 Guint newOverprintMask
= 0x10;
3008 for (int i
= 0; i
< separationList
->getLength(); i
++) {
3009 GfxSeparationColorSpace
*sepCS
= (GfxSeparationColorSpace
*)separationList
->get(i
);
3010 if (!sepCS
->getName()->cmp(name
)) {
3011 if (sepCS
->getFunc()->hasDifferentResultSet(func
)) {
3012 error(errSyntaxWarning
, -1,
3013 "Different functions found for '{0:t}', convert immediately", name
);
3019 overprintMask
= newOverprintMask
;
3022 newOverprintMask
<<=1;
3024 if (separationList
->getLength() == maxSepComps
) {
3025 error(errSyntaxWarning
, -1,
3026 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps
, name
);
3031 *mapping
= separationList
->getLength() + 4;
3032 separationList
->append(copy());
3033 overprintMask
= newOverprintMask
;
3038 //------------------------------------------------------------------------
3039 // GfxDeviceNColorSpace
3040 //------------------------------------------------------------------------
3042 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA
,
3044 GfxColorSpace
*altA
,
3056 for (i
= 0; i
< nComps
; ++i
) {
3057 names
[i
] = namesA
[i
];
3058 if (names
[i
]->cmp("None")) {
3059 nonMarking
= gFalse
;
3061 if (!names
[i
]->cmp("Cyan")) {
3062 overprintMask
|= 0x01;
3063 } else if (!names
[i
]->cmp("Magenta")) {
3064 overprintMask
|= 0x02;
3065 } else if (!names
[i
]->cmp("Yellow")) {
3066 overprintMask
|= 0x04;
3067 } else if (!names
[i
]->cmp("Black")) {
3068 overprintMask
|= 0x08;
3069 } else if (!names
[i
]->cmp("All")) {
3070 overprintMask
= 0xffffffff;
3072 overprintMask
= 0x0f;
3077 GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA
,
3079 GfxColorSpace
*altA
,
3084 Guint overprintMaskA
) {
3092 nonMarking
= nonMarkingA
;
3093 overprintMask
= overprintMaskA
;
3094 for (i
= 0; i
< nComps
; ++i
) {
3095 names
[i
] = namesA
[i
]->copy();
3099 GfxDeviceNColorSpace::~GfxDeviceNColorSpace() {
3102 for (i
= 0; i
< nComps
; ++i
) {
3107 deleteGooList(sepsCS
, GfxSeparationColorSpace
);
3108 if (mapping
!= NULL
)
3112 GfxColorSpace
*GfxDeviceNColorSpace::copy() {
3114 int *mappingA
= NULL
;
3116 GooList
*sepsCSA
= new GooList(sepsCS
->getLength());
3117 for (i
= 0; i
< sepsCS
->getLength(); i
++) {
3118 GfxSeparationColorSpace
*scs
= (GfxSeparationColorSpace
*) sepsCS
->get(i
);
3119 if (likely(scs
!= NULL
)) {
3120 sepsCSA
->append(scs
->copy());
3123 if (mapping
!= NULL
) {
3124 mappingA
= (int *)gmalloc(sizeof(int) * nComps
);
3125 for (i
= 0; i
< nComps
; i
++)
3126 mappingA
[i
] = mapping
[i
];
3128 return new GfxDeviceNColorSpace(nComps
, names
, alt
->copy(), func
->copy(),
3129 sepsCSA
, mappingA
, nonMarking
, overprintMask
);
3132 //~ handle the 'None' colorant
3133 GfxColorSpace
*GfxDeviceNColorSpace::parse(GfxResources
*res
, Array
*arr
, OutputDev
*out
, GfxState
*state
, int recursion
) {
3134 GfxDeviceNColorSpace
*cs
;
3136 GooString
*namesA
[gfxColorMaxComps
];
3137 GfxColorSpace
*altA
;
3141 GooList
*separationList
= new GooList();
3143 if (arr
->getLength() != 4 && arr
->getLength() != 5) {
3144 error(errSyntaxWarning
, -1, "Bad DeviceN color space");
3147 if (!arr
->get(1, &obj1
)->isArray()) {
3148 error(errSyntaxWarning
, -1, "Bad DeviceN color space (names)");
3151 nCompsA
= obj1
.arrayGetLength();
3152 if (nCompsA
> gfxColorMaxComps
) {
3153 error(errSyntaxWarning
, -1, "DeviceN color space with too many ({0:d} > {1:d}) components",
3154 nCompsA
, gfxColorMaxComps
);
3155 nCompsA
= gfxColorMaxComps
;
3157 for (i
= 0; i
< nCompsA
; ++i
) {
3158 if (!obj1
.arrayGet(i
, &obj2
)->isName()) {
3159 error(errSyntaxWarning
, -1, "Bad DeviceN color space (names)");
3163 namesA
[i
] = new GooString(obj2
.getName());
3168 if (!(altA
= GfxColorSpace::parse(res
, &obj1
, out
, state
, recursion
+ 1))) {
3169 error(errSyntaxWarning
, -1, "Bad DeviceN color space (alternate color space)");
3174 if (!(funcA
= Function::parse(&obj1
))) {
3178 if (arr
->getLength() == 5) {
3179 if (!arr
->get(4, &obj1
)->isDict()) {
3180 error(errSyntaxWarning
, -1, "Bad DeviceN color space (attributes)");
3183 Dict
*attribs
= obj1
.getDict();
3184 attribs
->lookup("Colorants", &obj2
);
3185 if (obj2
.isDict()) {
3186 Dict
*colorants
= obj2
.getDict();
3187 for (i
= 0; i
< colorants
->getLength(); i
++) {
3189 colorants
->getVal(i
, &obj3
);
3190 if (obj3
.isArray()) {
3191 separationList
->append(GfxSeparationColorSpace::parse(res
, obj3
.getArray(), out
, state
, recursion
));
3195 error(errSyntaxWarning
, -1, "Bad DeviceN color space (colorant value entry is not an Array)");
3204 cs
= new GfxDeviceNColorSpace(nCompsA
, namesA
, altA
, funcA
, separationList
);
3210 for (i
= 0; i
< nCompsA
; ++i
) {
3216 delete separationList
;
3220 void GfxDeviceNColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
3221 double x
[gfxColorMaxComps
], c
[gfxColorMaxComps
];
3225 for (i
= 0; i
< nComps
; ++i
) {
3226 x
[i
] = colToDbl(color
->c
[i
]);
3228 func
->transform(x
, c
);
3229 for (i
= 0; i
< alt
->getNComps(); ++i
) {
3230 color2
.c
[i
] = dblToCol(c
[i
]);
3232 alt
->getGray(&color2
, gray
);
3235 void GfxDeviceNColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
3236 double x
[gfxColorMaxComps
], c
[gfxColorMaxComps
];
3240 for (i
= 0; i
< nComps
; ++i
) {
3241 x
[i
] = colToDbl(color
->c
[i
]);
3243 func
->transform(x
, c
);
3244 for (i
= 0; i
< alt
->getNComps(); ++i
) {
3245 color2
.c
[i
] = dblToCol(c
[i
]);
3247 alt
->getRGB(&color2
, rgb
);
3250 void GfxDeviceNColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
3251 double x
[gfxColorMaxComps
], c
[gfxColorMaxComps
];
3255 for (i
= 0; i
< nComps
; ++i
) {
3256 x
[i
] = colToDbl(color
->c
[i
]);
3258 func
->transform(x
, c
);
3259 for (i
= 0; i
< alt
->getNComps(); ++i
) {
3260 color2
.c
[i
] = dblToCol(c
[i
]);
3262 alt
->getCMYK(&color2
, cmyk
);
3265 void GfxDeviceNColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
3266 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
3268 if (mapping
== NULL
) {
3271 getCMYK(color
, &cmyk
);
3272 deviceN
->c
[0] = cmyk
.c
;
3273 deviceN
->c
[1] = cmyk
.m
;
3274 deviceN
->c
[2] = cmyk
.y
;
3275 deviceN
->c
[3] = cmyk
.k
;
3277 for (int j
= 0; j
< nComps
; j
++)
3278 if (mapping
[j
] != -1)
3279 deviceN
->c
[mapping
[j
]] = color
->c
[j
];
3283 void GfxDeviceNColorSpace::getDefaultColor(GfxColor
*color
) {
3286 for (i
= 0; i
< nComps
; ++i
) {
3287 color
->c
[i
] = gfxColorComp1
;
3291 void GfxDeviceNColorSpace::createMapping(GooList
*separationList
, int maxSepComps
) {
3292 if (nonMarking
) // None
3294 mapping
= (int *)gmalloc(sizeof(int) * nComps
);
3295 Guint newOverprintMask
= 0;
3296 for (int i
= 0; i
< nComps
; i
++) {
3297 if (!names
[i
]->cmp("None")) {
3299 } else if (!names
[i
]->cmp("Cyan")) {
3300 newOverprintMask
|= 0x01;
3302 } else if (!names
[i
]->cmp("Magenta")) {
3303 newOverprintMask
|= 0x02;
3305 } else if (!names
[i
]->cmp("Yellow")) {
3306 newOverprintMask
|= 0x04;
3308 } else if (!names
[i
]->cmp("Black")) {
3309 newOverprintMask
|= 0x08;
3312 Guint startOverprintMask
= 0x10;
3313 GBool found
= gFalse
;
3314 Function
*sepFunc
= NULL
;
3318 for (int k
= 0; k
< sepsCS
->getLength(); k
++) {
3319 GfxSeparationColorSpace
*sepCS
= (GfxSeparationColorSpace
*)sepsCS
->get(k
);
3320 if (!sepCS
->getName()->cmp(names
[i
])) {
3321 sepFunc
= sepCS
->getFunc();
3326 for (int j
= 0; j
< separationList
->getLength(); j
++) {
3327 GfxSeparationColorSpace
*sepCS
= (GfxSeparationColorSpace
*)separationList
->get(j
);
3328 if (!sepCS
->getName()->cmp(names
[i
])) {
3329 if (sepFunc
!= NULL
&& sepCS
->getFunc()->hasDifferentResultSet(sepFunc
)) {
3330 error(errSyntaxWarning
, -1,
3331 "Different functions found for '{0:t}', convert immediately", names
[i
]);
3334 overprintMask
= 0xffffffff;
3338 newOverprintMask
|= startOverprintMask
;
3342 startOverprintMask
<<=1;
3345 if (separationList
->getLength() == maxSepComps
) {
3346 error(errSyntaxWarning
, -1,
3347 "Too many ({0:d}) spots, convert '{1:t}' immediately", maxSepComps
, names
[i
]);
3350 overprintMask
= 0xffffffff;
3353 mapping
[i
] = separationList
->getLength() + 4;
3354 newOverprintMask
|= startOverprintMask
;
3356 separationList
->append(new GfxSeparationColorSpace(names
[i
]->copy(),alt
->copy(), func
->copy()));
3358 for (int k
= 0; k
< sepsCS
->getLength(); k
++) {
3359 GfxSeparationColorSpace
*sepCS
= (GfxSeparationColorSpace
*)sepsCS
->get(k
);
3360 if (!sepCS
->getName()->cmp(names
[i
])) {
3362 separationList
->append(sepCS
->copy());
3367 error(errSyntaxWarning
, -1, "DeviceN has no suitable colorant");
3370 overprintMask
= 0xffffffff;
3377 overprintMask
= newOverprintMask
;
3380 //------------------------------------------------------------------------
3381 // GfxPatternColorSpace
3382 //------------------------------------------------------------------------
3384 GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace
*underA
) {
3388 GfxPatternColorSpace::~GfxPatternColorSpace() {
3394 GfxColorSpace
*GfxPatternColorSpace::copy() {
3395 return new GfxPatternColorSpace(under
? under
->copy() :
3396 (GfxColorSpace
*)NULL
);
3399 GfxColorSpace
*GfxPatternColorSpace::parse(GfxResources
*res
, Array
*arr
, OutputDev
*out
, GfxState
*state
, int recursion
) {
3400 GfxPatternColorSpace
*cs
;
3401 GfxColorSpace
*underA
;
3404 if (arr
->getLength() != 1 && arr
->getLength() != 2) {
3405 error(errSyntaxWarning
, -1, "Bad Pattern color space");
3409 if (arr
->getLength() == 2) {
3411 if (!(underA
= GfxColorSpace::parse(res
, &obj1
, out
, state
, recursion
+ 1))) {
3412 error(errSyntaxWarning
, -1, "Bad Pattern color space (underlying color space)");
3418 cs
= new GfxPatternColorSpace(underA
);
3422 void GfxPatternColorSpace::getGray(GfxColor
*color
, GfxGray
*gray
) {
3426 void GfxPatternColorSpace::getRGB(GfxColor
*color
, GfxRGB
*rgb
) {
3427 rgb
->r
= rgb
->g
= rgb
->b
= 0;
3430 void GfxPatternColorSpace::getCMYK(GfxColor
*color
, GfxCMYK
*cmyk
) {
3431 cmyk
->c
= cmyk
->m
= cmyk
->y
= 0;
3435 void GfxPatternColorSpace::getDeviceN(GfxColor
*color
, GfxColor
*deviceN
) {
3436 for (int i
= 0; i
< gfxColorMaxComps
; i
++)
3441 void GfxPatternColorSpace::getDefaultColor(GfxColor
*color
) {
3445 //------------------------------------------------------------------------
3447 //------------------------------------------------------------------------
3449 GfxPattern::GfxPattern(int typeA
) {
3453 GfxPattern::~GfxPattern() {
3456 GfxPattern
*GfxPattern::parse(GfxResources
*res
, Object
*obj
, OutputDev
*out
, GfxState
*state
) {
3457 GfxPattern
*pattern
;
3460 if (obj
->isDict()) {
3461 obj
->dictLookup("PatternType", &obj1
);
3462 } else if (obj
->isStream()) {
3463 obj
->streamGetDict()->lookup("PatternType", &obj1
);
3468 if (obj1
.isInt() && obj1
.getInt() == 1) {
3469 pattern
= GfxTilingPattern::parse(obj
);
3470 } else if (obj1
.isInt() && obj1
.getInt() == 2) {
3471 pattern
= GfxShadingPattern::parse(res
, obj
, out
, state
);
3477 //------------------------------------------------------------------------
3479 //------------------------------------------------------------------------
3481 GfxTilingPattern
*GfxTilingPattern::parse(Object
*patObj
) {
3482 GfxTilingPattern
*pat
;
3484 int paintTypeA
, tilingTypeA
;
3485 double bboxA
[4], matrixA
[6];
3486 double xStepA
, yStepA
;
3491 if (!patObj
->isStream()) {
3494 dict
= patObj
->streamGetDict();
3496 if (dict
->lookup("PaintType", &obj1
)->isInt()) {
3497 paintTypeA
= obj1
.getInt();
3500 error(errSyntaxWarning
, -1, "Invalid or missing PaintType in pattern");
3503 if (dict
->lookup("TilingType", &obj1
)->isInt()) {
3504 tilingTypeA
= obj1
.getInt();
3507 error(errSyntaxWarning
, -1, "Invalid or missing TilingType in pattern");
3510 bboxA
[0] = bboxA
[1] = 0;
3511 bboxA
[2] = bboxA
[3] = 1;
3512 if (dict
->lookup("BBox", &obj1
)->isArray() &&
3513 obj1
.arrayGetLength() == 4) {
3514 for (i
= 0; i
< 4; ++i
) {
3515 if (obj1
.arrayGet(i
, &obj2
)->isNum()) {
3516 bboxA
[i
] = obj2
.getNum();
3521 error(errSyntaxWarning
, -1, "Invalid or missing BBox in pattern");
3524 if (dict
->lookup("XStep", &obj1
)->isNum()) {
3525 xStepA
= obj1
.getNum();
3528 error(errSyntaxWarning
, -1, "Invalid or missing XStep in pattern");
3531 if (dict
->lookup("YStep", &obj1
)->isNum()) {
3532 yStepA
= obj1
.getNum();
3535 error(errSyntaxWarning
, -1, "Invalid or missing YStep in pattern");
3538 if (!dict
->lookup("Resources", &resDictA
)->isDict()) {
3540 resDictA
.initNull();
3541 error(errSyntaxWarning
, -1, "Invalid or missing Resources in pattern");
3543 matrixA
[0] = 1; matrixA
[1] = 0;
3544 matrixA
[2] = 0; matrixA
[3] = 1;
3545 matrixA
[4] = 0; matrixA
[5] = 0;
3546 if (dict
->lookup("Matrix", &obj1
)->isArray() &&
3547 obj1
.arrayGetLength() == 6) {
3548 for (i
= 0; i
< 6; ++i
) {
3549 if (obj1
.arrayGet(i
, &obj2
)->isNum()) {
3550 matrixA
[i
] = obj2
.getNum();
3557 pat
= new GfxTilingPattern(paintTypeA
, tilingTypeA
, bboxA
, xStepA
, yStepA
,
3558 &resDictA
, matrixA
, patObj
);
3563 GfxTilingPattern::GfxTilingPattern(int paintTypeA
, int tilingTypeA
,
3564 double *bboxA
, double xStepA
, double yStepA
,
3565 Object
*resDictA
, double *matrixA
,
3566 Object
*contentStreamA
):
3571 paintType
= paintTypeA
;
3572 tilingType
= tilingTypeA
;
3573 for (i
= 0; i
< 4; ++i
) {
3578 resDictA
->copy(&resDict
);
3579 for (i
= 0; i
< 6; ++i
) {
3580 matrix
[i
] = matrixA
[i
];
3582 contentStreamA
->copy(&contentStream
);
3585 GfxTilingPattern::~GfxTilingPattern() {
3587 contentStream
.free();
3590 GfxPattern
*GfxTilingPattern::copy() {
3591 return new GfxTilingPattern(paintType
, tilingType
, bbox
, xStep
, yStep
,
3592 &resDict
, matrix
, &contentStream
);
3595 //------------------------------------------------------------------------
3596 // GfxShadingPattern
3597 //------------------------------------------------------------------------
3599 GfxShadingPattern
*GfxShadingPattern::parse(GfxResources
*res
, Object
*patObj
, OutputDev
*out
, GfxState
*state
) {
3601 GfxShading
*shadingA
;
3606 if (!patObj
->isDict()) {
3609 dict
= patObj
->getDict();
3611 dict
->lookup("Shading", &obj1
);
3612 shadingA
= GfxShading::parse(res
, &obj1
, out
, state
);
3618 matrixA
[0] = 1; matrixA
[1] = 0;
3619 matrixA
[2] = 0; matrixA
[3] = 1;
3620 matrixA
[4] = 0; matrixA
[5] = 0;
3621 if (dict
->lookup("Matrix", &obj1
)->isArray() &&
3622 obj1
.arrayGetLength() == 6) {
3623 for (i
= 0; i
< 6; ++i
) {
3624 if (obj1
.arrayGet(i
, &obj2
)->isNum()) {
3625 matrixA
[i
] = obj2
.getNum();
3632 return new GfxShadingPattern(shadingA
, matrixA
);
3635 GfxShadingPattern::GfxShadingPattern(GfxShading
*shadingA
, double *matrixA
):
3641 for (i
= 0; i
< 6; ++i
) {
3642 matrix
[i
] = matrixA
[i
];
3646 GfxShadingPattern::~GfxShadingPattern() {
3650 GfxPattern
*GfxShadingPattern::copy() {
3651 return new GfxShadingPattern(shading
->copy(), matrix
);
3654 //------------------------------------------------------------------------
3656 //------------------------------------------------------------------------
3658 GfxShading::GfxShading(int typeA
) {
3663 GfxShading::GfxShading(GfxShading
*shading
) {
3666 type
= shading
->type
;
3667 colorSpace
= shading
->colorSpace
->copy();
3668 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
3669 background
.c
[i
] = shading
->background
.c
[i
];
3671 hasBackground
= shading
->hasBackground
;
3672 xMin
= shading
->xMin
;
3673 yMin
= shading
->yMin
;
3674 xMax
= shading
->xMax
;
3675 yMax
= shading
->yMax
;
3676 hasBBox
= shading
->hasBBox
;
3679 GfxShading::~GfxShading() {
3685 GfxShading
*GfxShading::parse(GfxResources
*res
, Object
*obj
, OutputDev
*out
, GfxState
*state
) {
3686 GfxShading
*shading
;
3691 if (obj
->isDict()) {
3692 dict
= obj
->getDict();
3693 } else if (obj
->isStream()) {
3694 dict
= obj
->streamGetDict();
3699 if (!dict
->lookup("ShadingType", &obj1
)->isInt()) {
3700 error(errSyntaxWarning
, -1, "Invalid ShadingType in shading dictionary");
3704 typeA
= obj1
.getInt();
3709 shading
= GfxFunctionShading::parse(res
, dict
, out
, state
);
3712 shading
= GfxAxialShading::parse(res
, dict
, out
, state
);
3715 shading
= GfxRadialShading::parse(res
, dict
, out
, state
);
3718 if (obj
->isStream()) {
3719 shading
= GfxGouraudTriangleShading::parse(res
, 4, dict
, obj
->getStream(), out
, state
);
3721 error(errSyntaxWarning
, -1, "Invalid Type 4 shading object");
3726 if (obj
->isStream()) {
3727 shading
= GfxGouraudTriangleShading::parse(res
, 5, dict
, obj
->getStream(), out
, state
);
3729 error(errSyntaxWarning
, -1, "Invalid Type 5 shading object");
3734 if (obj
->isStream()) {
3735 shading
= GfxPatchMeshShading::parse(res
, 6, dict
, obj
->getStream(), out
, state
);
3737 error(errSyntaxWarning
, -1, "Invalid Type 6 shading object");
3742 if (obj
->isStream()) {
3743 shading
= GfxPatchMeshShading::parse(res
, 7, dict
, obj
->getStream(), out
, state
);
3745 error(errSyntaxWarning
, -1, "Invalid Type 7 shading object");
3750 error(errSyntaxWarning
, -1, "Unimplemented shading type {0:d}", typeA
);
3760 GBool
GfxShading::init(GfxResources
*res
, Dict
*dict
, OutputDev
*out
, GfxState
*state
) {
3764 dict
->lookup("ColorSpace", &obj1
);
3765 if (!(colorSpace
= GfxColorSpace::parse(res
, &obj1
, out
, state
))) {
3766 error(errSyntaxWarning
, -1, "Bad color space in shading dictionary");
3772 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
3773 background
.c
[i
] = 0;
3775 hasBackground
= gFalse
;
3776 if (dict
->lookup("Background", &obj1
)->isArray()) {
3777 if (obj1
.arrayGetLength() == colorSpace
->getNComps()) {
3778 hasBackground
= gTrue
;
3779 for (i
= 0; i
< colorSpace
->getNComps(); ++i
) {
3780 background
.c
[i
] = dblToCol(obj1
.arrayGet(i
, &obj2
)->getNum());
3784 error(errSyntaxWarning
, -1, "Bad Background in shading dictionary");
3789 xMin
= yMin
= xMax
= yMax
= 0;
3791 if (dict
->lookup("BBox", &obj1
)->isArray()) {
3792 if (obj1
.arrayGetLength() == 4) {
3793 Object obj3
, obj4
, obj5
;
3794 obj1
.arrayGet(0, &obj2
);
3795 obj1
.arrayGet(1, &obj3
);
3796 obj1
.arrayGet(2, &obj4
);
3797 obj1
.arrayGet(3, &obj5
);
3798 if (obj2
.isNum() && obj3
.isNum() && obj4
.isNum() && obj5
.isNum())
3801 xMin
= obj2
.getNum();
3802 yMin
= obj3
.getNum();
3803 xMax
= obj4
.getNum();
3804 yMax
= obj5
.getNum();
3806 error(errSyntaxWarning
, -1, "Bad BBox in shading dictionary (Values not numbers)");
3813 error(errSyntaxWarning
, -1, "Bad BBox in shading dictionary");
3821 //------------------------------------------------------------------------
3822 // GfxFunctionShading
3823 //------------------------------------------------------------------------
3825 GfxFunctionShading::GfxFunctionShading(double x0A
, double y0A
,
3826 double x1A
, double y1A
,
3828 Function
**funcsA
, int nFuncsA
):
3837 for (i
= 0; i
< 6; ++i
) {
3838 matrix
[i
] = matrixA
[i
];
3841 for (i
= 0; i
< nFuncs
; ++i
) {
3842 funcs
[i
] = funcsA
[i
];
3846 GfxFunctionShading::GfxFunctionShading(GfxFunctionShading
*shading
):
3855 for (i
= 0; i
< 6; ++i
) {
3856 matrix
[i
] = shading
->matrix
[i
];
3858 nFuncs
= shading
->nFuncs
;
3859 for (i
= 0; i
< nFuncs
; ++i
) {
3860 funcs
[i
] = shading
->funcs
[i
]->copy();
3864 GfxFunctionShading::~GfxFunctionShading() {
3867 for (i
= 0; i
< nFuncs
; ++i
) {
3872 GfxFunctionShading
*GfxFunctionShading::parse(GfxResources
*res
, Dict
*dict
, OutputDev
*out
, GfxState
*state
) {
3873 GfxFunctionShading
*shading
;
3874 double x0A
, y0A
, x1A
, y1A
;
3876 Function
*funcsA
[gfxColorMaxComps
];
3883 if (dict
->lookup("Domain", &obj1
)->isArray() &&
3884 obj1
.arrayGetLength() == 4) {
3885 x0A
= obj1
.arrayGet(0, &obj2
)->getNum();
3887 x1A
= obj1
.arrayGet(1, &obj2
)->getNum();
3889 y0A
= obj1
.arrayGet(2, &obj2
)->getNum();
3891 y1A
= obj1
.arrayGet(3, &obj2
)->getNum();
3896 matrixA
[0] = 1; matrixA
[1] = 0;
3897 matrixA
[2] = 0; matrixA
[3] = 1;
3898 matrixA
[4] = 0; matrixA
[5] = 0;
3899 if (dict
->lookup("Matrix", &obj1
)->isArray() &&
3900 obj1
.arrayGetLength() == 6) {
3901 matrixA
[0] = obj1
.arrayGet(0, &obj2
)->getNum();
3903 matrixA
[1] = obj1
.arrayGet(1, &obj2
)->getNum();
3905 matrixA
[2] = obj1
.arrayGet(2, &obj2
)->getNum();
3907 matrixA
[3] = obj1
.arrayGet(3, &obj2
)->getNum();
3909 matrixA
[4] = obj1
.arrayGet(4, &obj2
)->getNum();
3911 matrixA
[5] = obj1
.arrayGet(5, &obj2
)->getNum();
3916 dict
->lookup("Function", &obj1
);
3917 if (obj1
.isArray()) {
3918 nFuncsA
= obj1
.arrayGetLength();
3919 if (nFuncsA
> gfxColorMaxComps
|| nFuncsA
<= 0) {
3920 error(errSyntaxWarning
, -1, "Invalid Function array in shading dictionary");
3923 for (i
= 0; i
< nFuncsA
; ++i
) {
3924 obj1
.arrayGet(i
, &obj2
);
3925 if (!(funcsA
[i
] = Function::parse(&obj2
))) {
3932 if (!(funcsA
[0] = Function::parse(&obj1
))) {
3938 shading
= new GfxFunctionShading(x0A
, y0A
, x1A
, y1A
, matrixA
,
3940 if (!shading
->init(res
, dict
, out
, state
)) {
3953 GfxShading
*GfxFunctionShading::copy() {
3954 return new GfxFunctionShading(this);
3957 void GfxFunctionShading::getColor(double x
, double y
, GfxColor
*color
) {
3958 double in
[2], out
[gfxColorMaxComps
];
3961 // NB: there can be one function with n outputs or n functions with
3962 // one output each (where n = number of color components)
3963 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
3968 for (i
= 0; i
< nFuncs
; ++i
) {
3969 funcs
[i
]->transform(in
, &out
[i
]);
3971 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
3972 color
->c
[i
] = dblToCol(out
[i
]);
3976 //------------------------------------------------------------------------
3977 // GfxUnivariateShading
3978 //------------------------------------------------------------------------
3980 GfxUnivariateShading::GfxUnivariateShading(int typeA
,
3981 double t0A
, double t1A
,
3982 Function
**funcsA
, int nFuncsA
,
3983 GBool extend0A
, GBool extend1A
):
3991 for (i
= 0; i
< nFuncs
; ++i
) {
3992 funcs
[i
] = funcsA
[i
];
4004 GfxUnivariateShading::GfxUnivariateShading(GfxUnivariateShading
*shading
):
4011 nFuncs
= shading
->nFuncs
;
4012 for (i
= 0; i
< nFuncs
; ++i
) {
4013 funcs
[i
] = shading
->funcs
[i
]->copy();
4015 extend0
= shading
->extend0
;
4016 extend1
= shading
->extend1
;
4025 GfxUnivariateShading::~GfxUnivariateShading() {
4028 for (i
= 0; i
< nFuncs
; ++i
) {
4032 gfree (cacheBounds
);
4035 void GfxUnivariateShading::getColor(double t
, GfxColor
*color
) {
4036 double out
[gfxColorMaxComps
];
4039 // NB: there can be one function with n outputs or n functions with
4040 // one output each (where n = number of color components)
4041 nComps
= nFuncs
* funcs
[0]->getOutputSize();
4043 if (cacheSize
> 0) {
4044 double x
, ix
, *l
, *u
, *upper
;
4046 if (cacheBounds
[lastMatch
- 1] >= t
) {
4047 upper
= std::lower_bound (cacheBounds
, cacheBounds
+ lastMatch
- 1, t
);
4048 lastMatch
= upper
- cacheBounds
;
4049 lastMatch
= std::min
<int>(std::max
<int>(1, lastMatch
), cacheSize
- 1);
4050 } else if (cacheBounds
[lastMatch
] < t
) {
4051 upper
= std::lower_bound (cacheBounds
+ lastMatch
+ 1, cacheBounds
+ cacheSize
, t
);
4052 lastMatch
= upper
- cacheBounds
;
4053 lastMatch
= std::min
<int>(std::max
<int>(1, lastMatch
), cacheSize
- 1);
4056 x
= (t
- cacheBounds
[lastMatch
-1]) * cacheCoeff
[lastMatch
];
4058 u
= cacheValues
+ lastMatch
* nComps
;
4061 for (i
= 0; i
< nComps
; ++i
) {
4062 out
[i
] = ix
* l
[i
] + x
* u
[i
];
4065 for (i
= 0; i
< nComps
; ++i
) {
4068 for (i
= 0; i
< nFuncs
; ++i
) {
4069 if (funcs
[i
]->getInputSize() != 1) {
4070 error(errSyntaxWarning
, -1, "Invalid shading function (input != 1)");
4073 funcs
[i
]->transform(&t
, &out
[i
]);
4077 for (i
= 0; i
< nComps
; ++i
) {
4078 color
->c
[i
] = dblToCol(out
[i
]);
4082 void GfxUnivariateShading::setupCache(const Matrix
*ctm
,
4083 double xMin
, double yMin
,
4084 double xMax
, double yMax
) {
4085 double sMin
, sMax
, tMin
, tMax
, upperBound
;
4086 int i
, j
, nComps
, maxSize
;
4088 gfree (cacheBounds
);
4092 // NB: there can be one function with n outputs or n functions with
4093 // one output each (where n = number of color components)
4094 nComps
= nFuncs
* funcs
[0]->getOutputSize();
4096 getParameterRange(&sMin
, &sMax
, xMin
, yMin
, xMax
, yMax
);
4097 upperBound
= ctm
->norm() * getDistance(sMin
, sMax
);
4098 maxSize
= ceil(upperBound
);
4099 maxSize
= std::max
<int>(maxSize
, 2);
4104 ctm
->transform(xMin
, yMin
, &x
[0], &y
[0]);
4105 ctm
->transform(xMax
, yMin
, &x
[1], &y
[1]);
4106 ctm
->transform(xMin
, yMax
, &x
[2], &y
[2]);
4107 ctm
->transform(xMax
, yMax
, &x
[3], &y
[3]);
4111 for (i
= 1; i
< 4; i
++) {
4112 xMin
= std::min
<double>(xMin
, x
[i
]);
4113 yMin
= std::min
<double>(yMin
, y
[i
]);
4114 xMax
= std::max
<double>(xMax
, x
[i
]);
4115 yMax
= std::max
<double>(yMax
, y
[i
]);
4119 if (maxSize
> (xMax
-xMin
) * (yMax
-yMin
)) {
4124 tMin
= t0
+ sMin
* (t1
- t0
);
4125 tMax
= t0
+ sMax
* (t1
- t0
);
4127 tMin
= t0
+ sMax
* (t1
- t0
);
4128 tMax
= t0
+ sMin
* (t1
- t0
);
4131 cacheBounds
= (double *)gmallocn(maxSize
, sizeof(double) * (nComps
+ 2));
4132 cacheCoeff
= cacheBounds
+ maxSize
;
4133 cacheValues
= cacheCoeff
+ maxSize
;
4135 if (cacheSize
!= 0) {
4136 for (j
= 0; j
< cacheSize
; ++j
) {
4137 cacheCoeff
[j
] = 1 / (cacheBounds
[j
+1] - cacheBounds
[j
]);
4139 } else if (tMax
!= tMin
) {
4140 double step
= (tMax
- tMin
) / (maxSize
- 1);
4141 double coeff
= (maxSize
- 1) / (tMax
- tMin
);
4143 cacheSize
= maxSize
;
4145 for (j
= 0; j
< cacheSize
; ++j
) {
4146 cacheBounds
[j
] = tMin
+ j
* step
;
4147 cacheCoeff
[j
] = coeff
;
4149 for (i
= 0; i
< nComps
; ++i
) {
4150 cacheValues
[j
*nComps
+ i
] = 0;
4152 for (i
= 0; i
< nFuncs
; ++i
) {
4153 funcs
[i
]->transform(&cacheBounds
[j
], &cacheValues
[j
*nComps
+ i
]);
4162 //------------------------------------------------------------------------
4164 //------------------------------------------------------------------------
4166 GfxAxialShading::GfxAxialShading(double x0A
, double y0A
,
4167 double x1A
, double y1A
,
4168 double t0A
, double t1A
,
4169 Function
**funcsA
, int nFuncsA
,
4170 GBool extend0A
, GBool extend1A
):
4171 GfxUnivariateShading(2, t0A
, t1A
, funcsA
, nFuncsA
, extend0A
, extend1A
)
4179 GfxAxialShading::GfxAxialShading(GfxAxialShading
*shading
):
4180 GfxUnivariateShading(shading
)
4188 GfxAxialShading::~GfxAxialShading() {
4191 GfxAxialShading
*GfxAxialShading::parse(GfxResources
*res
, Dict
*dict
, OutputDev
*out
, GfxState
*state
) {
4192 GfxAxialShading
*shading
;
4193 double x0A
, y0A
, x1A
, y1A
;
4195 Function
*funcsA
[gfxColorMaxComps
];
4197 GBool extend0A
, extend1A
;
4201 x0A
= y0A
= x1A
= y1A
= 0;
4202 if (dict
->lookup("Coords", &obj1
)->isArray() &&
4203 obj1
.arrayGetLength() == 4) {
4204 Object obj3
, obj4
, obj5
;
4205 obj1
.arrayGet(0, &obj2
);
4206 obj1
.arrayGet(1, &obj3
);
4207 obj1
.arrayGet(2, &obj4
);
4208 obj1
.arrayGet(3, &obj5
);
4209 if (obj2
.isNum() && obj3
.isNum() && obj4
.isNum() && obj5
.isNum()) {
4210 x0A
= obj2
.getNum();
4211 y0A
= obj3
.getNum();
4212 x1A
= obj4
.getNum();
4213 y1A
= obj5
.getNum();
4220 error(errSyntaxWarning
, -1, "Missing or invalid Coords in shading dictionary");
4227 if (dict
->lookup("Domain", &obj1
)->isArray() &&
4228 obj1
.arrayGetLength() == 2) {
4230 obj1
.arrayGet(0, &obj2
);
4231 obj1
.arrayGet(1, &obj3
);
4232 if (obj2
.isNum() && obj3
.isNum()) {
4233 t0A
= obj2
.getNum();
4234 t1A
= obj3
.getNum();
4241 dict
->lookup("Function", &obj1
);
4242 if (obj1
.isArray()) {
4243 nFuncsA
= obj1
.arrayGetLength();
4244 if (nFuncsA
> gfxColorMaxComps
|| nFuncsA
== 0) {
4245 error(errSyntaxWarning
, -1, "Invalid Function array in shading dictionary");
4248 for (i
= 0; i
< nFuncsA
; ++i
) {
4249 obj1
.arrayGet(i
, &obj2
);
4250 if (!(funcsA
[i
] = Function::parse(&obj2
))) {
4259 if (!(funcsA
[0] = Function::parse(&obj1
))) {
4266 extend0A
= extend1A
= gFalse
;
4267 if (dict
->lookup("Extend", &obj1
)->isArray() &&
4268 obj1
.arrayGetLength() == 2) {
4269 obj1
.arrayGet(0, &obj2
);
4270 if (obj2
.isBool()) {
4271 extend0A
= obj2
.getBool();
4273 error(errSyntaxWarning
, -1, "Invalid axial shading extend (0)");
4276 obj1
.arrayGet(1, &obj2
);
4277 if (obj2
.isBool()) {
4278 extend1A
= obj2
.getBool();
4280 error(errSyntaxWarning
, -1, "Invalid axial shading extend (1)");
4286 shading
= new GfxAxialShading(x0A
, y0A
, x1A
, y1A
, t0A
, t1A
,
4287 funcsA
, nFuncsA
, extend0A
, extend1A
);
4288 if (!shading
->init(res
, dict
, out
, state
)) {
4298 GfxShading
*GfxAxialShading::copy() {
4299 return new GfxAxialShading(this);
4302 double GfxAxialShading::getDistance(double sMin
, double sMax
) {
4303 double xMin
, yMin
, xMax
, yMax
;
4305 xMin
= x0
+ sMin
* (x1
- x0
);
4306 yMin
= y0
+ sMin
* (y1
- y0
);
4307 xMax
= x0
+ sMax
* (x1
- x0
);
4308 yMax
= y0
+ sMax
* (y1
- y0
);
4310 return hypot(xMax
-xMin
, yMax
-yMin
);
4313 void GfxAxialShading::getParameterRange(double *lower
, double *upper
,
4314 double xMin
, double yMin
,
4315 double xMax
, double yMax
) {
4316 double pdx
, pdy
, invsqnorm
, tdx
, tdy
, t
, range
[2];
4318 // Linear gradients are orthogonal to the line passing through their
4319 // extremes. Because of convexity, the parameter range can be
4320 // computed as the convex hull (one the real line) of the parameter
4321 // values of the 4 corners of the box.
4323 // The parameter value t for a point (x,y) can be computed as:
4325 // t = (p2 - p1) . (x,y) / |p2 - p1|^2
4327 // t0 is the t value for the top left corner
4328 // tdx is the difference between left and right corners
4329 // tdy is the difference between top and bottom corners
4333 invsqnorm
= 1.0 / (pdx
* pdx
+ pdy
* pdy
);
4337 t
= (xMin
- x0
) * pdx
+ (yMin
- y0
) * pdy
;
4338 tdx
= (xMax
- xMin
) * pdx
;
4339 tdy
= (yMax
- yMin
) * pdy
;
4341 // Because of the linearity of the t value, tdx can simply be added
4342 // the t0 to move along the top edge. After this, *lower and *upper
4343 // represent the parameter range for the top edge, so extending it
4344 // to include the whole box simply requires adding tdy to the
4347 range
[0] = range
[1] = t
;
4358 *lower
= std::max
<double>(0., std::min
<double>(1., range
[0]));
4359 *upper
= std::max
<double>(0., std::min
<double>(1., range
[1]));
4362 //------------------------------------------------------------------------
4364 //------------------------------------------------------------------------
4366 #ifndef RADIAL_EPSILON
4367 #define RADIAL_EPSILON (1. / 1024 / 1024)
4370 GfxRadialShading::GfxRadialShading(double x0A
, double y0A
, double r0A
,
4371 double x1A
, double y1A
, double r1A
,
4372 double t0A
, double t1A
,
4373 Function
**funcsA
, int nFuncsA
,
4374 GBool extend0A
, GBool extend1A
):
4375 GfxUnivariateShading(3, t0A
, t1A
, funcsA
, nFuncsA
, extend0A
, extend1A
)
4385 GfxRadialShading::GfxRadialShading(GfxRadialShading
*shading
):
4386 GfxUnivariateShading(shading
)
4396 GfxRadialShading::~GfxRadialShading() {
4399 GfxRadialShading
*GfxRadialShading::parse(GfxResources
*res
, Dict
*dict
, OutputDev
*out
, GfxState
*state
) {
4400 GfxRadialShading
*shading
;
4401 double x0A
, y0A
, r0A
, x1A
, y1A
, r1A
;
4403 Function
*funcsA
[gfxColorMaxComps
];
4405 GBool extend0A
, extend1A
;
4409 x0A
= y0A
= r0A
= x1A
= y1A
= r1A
= 0;
4410 if (dict
->lookup("Coords", &obj1
)->isArray() &&
4411 obj1
.arrayGetLength() == 6) {
4412 x0A
= obj1
.arrayGet(0, &obj2
)->getNum();
4414 y0A
= obj1
.arrayGet(1, &obj2
)->getNum();
4416 r0A
= obj1
.arrayGet(2, &obj2
)->getNum();
4418 x1A
= obj1
.arrayGet(3, &obj2
)->getNum();
4420 y1A
= obj1
.arrayGet(4, &obj2
)->getNum();
4422 r1A
= obj1
.arrayGet(5, &obj2
)->getNum();
4425 error(errSyntaxWarning
, -1, "Missing or invalid Coords in shading dictionary");
4432 if (dict
->lookup("Domain", &obj1
)->isArray() &&
4433 obj1
.arrayGetLength() == 2) {
4434 t0A
= obj1
.arrayGet(0, &obj2
)->getNum();
4436 t1A
= obj1
.arrayGet(1, &obj2
)->getNum();
4441 dict
->lookup("Function", &obj1
);
4442 if (obj1
.isArray()) {
4443 nFuncsA
= obj1
.arrayGetLength();
4444 if (nFuncsA
> gfxColorMaxComps
) {
4445 error(errSyntaxWarning
, -1, "Invalid Function array in shading dictionary");
4448 for (i
= 0; i
< nFuncsA
; ++i
) {
4449 obj1
.arrayGet(i
, &obj2
);
4450 if (!(funcsA
[i
] = Function::parse(&obj2
))) {
4459 if (!(funcsA
[0] = Function::parse(&obj1
))) {
4466 extend0A
= extend1A
= gFalse
;
4467 if (dict
->lookup("Extend", &obj1
)->isArray() &&
4468 obj1
.arrayGetLength() == 2) {
4469 extend0A
= obj1
.arrayGet(0, &obj2
)->getBool();
4471 extend1A
= obj1
.arrayGet(1, &obj2
)->getBool();
4476 shading
= new GfxRadialShading(x0A
, y0A
, r0A
, x1A
, y1A
, r1A
, t0A
, t1A
,
4477 funcsA
, nFuncsA
, extend0A
, extend1A
);
4478 if (!shading
->init(res
, dict
, out
, state
)) {
4488 GfxShading
*GfxRadialShading::copy() {
4489 return new GfxRadialShading(this);
4492 double GfxRadialShading::getDistance(double sMin
, double sMax
) {
4493 double xMin
, yMin
, rMin
, xMax
, yMax
, rMax
;
4495 xMin
= x0
+ sMin
* (x1
- x0
);
4496 yMin
= y0
+ sMin
* (y1
- y0
);
4497 rMin
= r0
+ sMin
* (r1
- r0
);
4499 xMax
= x0
+ sMax
* (x1
- x0
);
4500 yMax
= y0
+ sMax
* (y1
- y0
);
4501 rMax
= r0
+ sMax
* (r1
- r0
);
4503 return hypot(xMax
-xMin
, yMax
-yMin
) + fabs(rMax
-rMin
);
4506 // extend range, adapted from cairo, radialExtendRange
4508 radialExtendRange (double range
[2], double value
, GBool valid
)
4511 range
[0] = range
[1] = value
;
4512 else if (value
< range
[0])
4514 else if (value
> range
[1])
4520 inline void radialEdge(double num
, double den
, double delta
, double lower
, double upper
,
4521 double dr
, double mindr
, GBool
&valid
, double *range
)
4523 if (fabs (den
) >= RADIAL_EPSILON
) {
4525 t_edge
= (num
) / (den
);
4526 v
= t_edge
* (delta
);
4527 if (t_edge
* dr
>= mindr
&& (lower
) <= v
&& v
<= (upper
))
4528 valid
= radialExtendRange (range
, t_edge
, valid
);
4532 inline void radialCorner1(double x
, double y
, double &b
, double dx
, double dy
, double cr
,
4533 double dr
, double mindr
, GBool
&valid
, double *range
)
4535 b
= (x
) * dx
+ (y
) * dy
+ cr
* dr
;
4536 if (fabs (b
) >= RADIAL_EPSILON
) {
4538 double x2
= (x
) * (x
);
4539 double y2
= (y
) * (y
);
4540 double cr2
= (cr
) * (cr
);
4541 double c
= x2
+ y2
- cr2
;
4543 t_corner
= 0.5 * c
/ b
;
4544 if (t_corner
* dr
>= mindr
)
4545 valid
= radialExtendRange (range
, t_corner
, valid
);
4549 inline void radialCorner2(double x
, double y
, double a
, double &b
, double &c
, double &d
, double dx
, double dy
, double cr
,
4550 double inva
, double dr
, double mindr
, GBool
&valid
, double *range
)
4552 b
= (x
) * dx
+ (y
) * dy
+ cr
* dr
;
4553 c
= (x
) * (x
) + (y
) * (y
) - cr
* cr
;
4559 t_corner
= (b
+ d
) * inva
;
4560 if (t_corner
* dr
>= mindr
)
4561 valid
= radialExtendRange (range
, t_corner
, valid
);
4562 t_corner
= (b
- d
) * inva
;
4563 if (t_corner
* dr
>= mindr
)
4564 valid
= radialExtendRange (range
, t_corner
, valid
);
4567 void GfxRadialShading::getParameterRange(double *lower
, double *upper
,
4568 double xMin
, double yMin
,
4569 double xMax
, double yMax
) {
4570 double cx
, cy
, cr
, dx
, dy
, dr
;
4571 double a
, x_focus
, y_focus
;
4572 double mindr
, minx
, miny
, maxx
, maxy
;
4576 // A radial pattern is considered degenerate if it can be
4577 // represented as a solid or clear pattern. This corresponds to one
4578 // of the two cases:
4580 // 1) The radii are both very small:
4581 // |dr| < FLT_EPSILON && min (r0, r1) < FLT_EPSILON
4583 // 2) The two circles have about the same radius and are very
4584 // close to each other (approximately a cylinder gradient that
4585 // doesn't move with the parameter):
4586 // |dr| < FLT_EPSILON && max (|dx|, |dy|) < 2 * FLT_EPSILON
4588 if (xMin
>= xMax
|| yMin
>=yMax
||
4589 (fabs (r0
- r1
) < RADIAL_EPSILON
&&
4590 (std::min
<double>(r0
, r1
) < RADIAL_EPSILON
||
4591 std::max
<double>(fabs (x0
- x1
), fabs (y0
- y1
)) < 2 * RADIAL_EPSILON
))) {
4592 *lower
= *upper
= 0;
4596 range
[0] = range
[1] = 0;
4599 x_focus
= y_focus
= 0; // silence gcc
4608 // translate by -(cx, cy) to simplify computations
4614 // enlarge boundaries slightly to avoid rounding problems in the
4615 // parameter range computation
4616 xMin
-= RADIAL_EPSILON
;
4617 yMin
-= RADIAL_EPSILON
;
4618 xMax
+= RADIAL_EPSILON
;
4619 yMax
+= RADIAL_EPSILON
;
4621 // enlarge boundaries even more to avoid rounding problems when
4622 // testing if a point belongs to the box
4623 minx
= xMin
- RADIAL_EPSILON
;
4624 miny
= yMin
- RADIAL_EPSILON
;
4625 maxx
= xMax
+ RADIAL_EPSILON
;
4626 maxy
= yMax
+ RADIAL_EPSILON
;
4628 // we dont' allow negative radiuses, so we will be checking that
4629 // t*dr >= mindr to consider t valid
4630 mindr
= -(cr
+ RADIAL_EPSILON
);
4632 // After the previous transformations, the start circle is centered
4633 // in the origin and has radius cr. A 1-unit change in the t
4634 // parameter corresponds to dx,dy,dr changes in the x,y,r of the
4635 // circle (center coordinates, radius).
4637 // To compute the minimum range needed to correctly draw the
4638 // pattern, we start with an empty range and extend it to include
4639 // the circles touching the bounding box or within it.
4641 // Focus, the point where the circle has radius == 0.
4643 // r = cr + t * dr = 0
4646 // If the radius is constant (dr == 0) there is no focus (the
4647 // gradient represents a cylinder instead of a cone).
4648 if (fabs (dr
) >= RADIAL_EPSILON
) {
4652 x_focus
= t_focus
* dx
;
4653 y_focus
= t_focus
* dy
;
4654 if (minx
<= x_focus
&& x_focus
<= maxx
&&
4655 miny
<= y_focus
&& y_focus
<= maxy
)
4657 valid
= radialExtendRange (range
, t_focus
, valid
);
4661 // Circles externally tangent to box edges.
4663 // All circles have center in (dx, dy) * t
4665 // If the circle is tangent to the line defined by the edge of the
4666 // box, then at least one of the following holds true:
4668 // (dx*t) + (cr + dr*t) == x0 (left edge)
4669 // (dx*t) - (cr + dr*t) == x1 (right edge)
4670 // (dy*t) + (cr + dr*t) == y0 (top edge)
4671 // (dy*t) - (cr + dr*t) == y1 (bottom edge)
4673 // The solution is only valid if the tangent point is actually on
4674 // the edge, i.e. if its y coordinate is in [y0,y1] for left/right
4675 // edges and if its x coordinate is in [x0,x1] for top/bottom edges.
4677 // For the first equation:
4679 // (dx + dr) * t = x0 - cr
4680 // t = (x0 - cr) / (dx + dr)
4683 // in the code this becomes:
4685 // t_edge = (num) / (den)
4686 // v = (delta) * t_edge
4688 // If the denominator in t is 0, the pattern is tangent to a line
4689 // parallel to the edge under examination. The corner-case where the
4690 // boundary line is the same as the edge is handled by the focus
4691 // point case and/or by the a==0 case.
4693 // circles tangent (externally) to left/right/top/bottom edge
4694 radialEdge(xMin
- cr
, dx
+ dr
, dy
, miny
, maxy
, dr
, mindr
, valid
, range
);
4695 radialEdge(xMax
+ cr
, dx
- dr
, dy
, miny
, maxy
, dr
, mindr
, valid
, range
);
4696 radialEdge(yMin
- cr
, dy
+ dr
, dx
, minx
, maxx
, dr
, mindr
, valid
, range
);
4697 radialEdge(yMax
+ cr
, dy
- dr
, dx
, minx
, maxx
, dr
, mindr
, valid
, range
);
4699 // Circles passing through a corner.
4701 // A circle passing through the point (x,y) satisfies:
4703 // (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
4706 // a = dx^2 + dy^2 - dr^2
4707 // b = x*dx + y*dy + cr*dr
4708 // c = x^2 + y^2 - cr^2
4710 // a*t^2 - 2*b*t + c == 0
4712 a
= dx
* dx
+ dy
* dy
- dr
* dr
;
4713 if (fabs (a
) < RADIAL_EPSILON
* RADIAL_EPSILON
) {
4716 // Ensure that gradients with both a and dr small are
4717 // considered degenerate.
4718 // The floating point version of the degeneracy test implemented
4719 // in _radial_pattern_is_degenerate() is:
4721 // 1) The circles are practically the same size:
4722 // |dr| < RADIAL_EPSILON
4724 // 2a) The circles are both very small:
4725 // min (r0, r1) < RADIAL_EPSILON
4727 // 2b) The circles are very close to each other:
4728 // max (|dx|, |dy|) < 2 * RADIAL_EPSILON
4730 // Assuming that the gradient is not degenerate, we want to
4731 // show that |a| < RADIAL_EPSILON^2 implies |dr| >= RADIAL_EPSILON.
4733 // If the gradient is not degenerate yet it has |dr| <
4734 // RADIAL_EPSILON, (2b) is false, thus:
4736 // max (|dx|, |dy|) >= 2*RADIAL_EPSILON
4738 // 4*RADIAL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
4740 // From the definition of a, we get:
4741 // a = dx^2 + dy^2 - dr^2 < RADIAL_EPSILON^2
4742 // dx^2 + dy^2 - RADIAL_EPSILON^2 < dr^2
4743 // 3*RADIAL_EPSILON^2 < dr^2
4745 // which is inconsistent with the hypotheses, thus |dr| <
4746 // RADIAL_EPSILON is false or the gradient is degenerate.
4748 assert (fabs (dr
) >= RADIAL_EPSILON
);
4750 // If a == 0, all the circles are tangent to a line in the
4751 // focus point. If this line is within the box extents, we
4752 // should add the circle with infinite radius, but this would
4753 // make the range unbounded. We will be limiting the range to
4754 // [0,1] anyway, so we simply add the biggest legitimate
4755 // circle (it happens for 0 or for 1).
4757 valid
= radialExtendRange (range
, 0, valid
);
4759 valid
= radialExtendRange (range
, 1, valid
);
4762 // Nondegenerate, nonlimit circles passing through the corners.
4764 // a == 0 && a*t^2 - 2*b*t + c == 0
4768 // The b == 0 case has just been handled, so we only have to
4769 // compute this if b != 0.
4771 // circles touching each corner
4772 radialCorner1(xMin
, yMin
, b
, dx
, dy
, cr
, dr
, mindr
, valid
, range
);
4773 radialCorner1(xMin
, yMax
, b
, dx
, dy
, cr
, dr
, mindr
, valid
, range
);
4774 radialCorner1(xMax
, yMin
, b
, dx
, dy
, cr
, dr
, mindr
, valid
, range
);
4775 radialCorner1(xMax
, yMax
, b
, dx
, dy
, cr
, dr
, mindr
, valid
, range
);
4777 double inva
, b
, c
, d
;
4781 // Nondegenerate, nonlimit circles passing through the corners.
4783 // a != 0 && a*t^2 - 2*b*t + c == 0
4785 // t = (b +- sqrt (b*b - a*c)) / a
4787 // If the argument of sqrt() is negative, then no circle
4788 // passes through the corner.
4790 // circles touching each corner
4791 radialCorner2(xMin
, yMin
, a
, b
, c
, d
, dx
, dy
, cr
, inva
, dr
, mindr
, valid
, range
);
4792 radialCorner2(xMin
, yMax
, a
, b
, c
, d
, dx
, dy
, cr
, inva
, dr
, mindr
, valid
, range
);
4793 radialCorner2(xMax
, yMin
, a
, b
, c
, d
, dx
, dy
, cr
, inva
, dr
, mindr
, valid
, range
);
4794 radialCorner2(xMax
, yMax
, a
, b
, c
, d
, dx
, dy
, cr
, inva
, dr
, mindr
, valid
, range
);
4797 *lower
= std::max
<double>(0., std::min
<double>(1., range
[0]));
4798 *upper
= std::max
<double>(0., std::min
<double>(1., range
[1]));
4801 //------------------------------------------------------------------------
4803 //------------------------------------------------------------------------
4805 class GfxShadingBitBuf
{
4808 GfxShadingBitBuf(Stream
*strA
);
4809 ~GfxShadingBitBuf();
4810 GBool
getBits(int n
, Guint
*val
);
4820 GfxShadingBitBuf::GfxShadingBitBuf(Stream
*strA
) {
4827 GfxShadingBitBuf::~GfxShadingBitBuf() {
4831 GBool
GfxShadingBitBuf::getBits(int n
, Guint
*val
) {
4835 x
= (bitBuf
>> (nBits
- n
)) & ((1 << n
) - 1);
4840 x
= bitBuf
& ((1 << nBits
) - 1);
4845 if ((bitBuf
= str
->getChar()) == EOF
) {
4850 x
= (x
<< 8) | bitBuf
;
4853 x
= (x
<< n
) | (bitBuf
>> (8 - n
));
4863 void GfxShadingBitBuf::flushBits() {
4868 //------------------------------------------------------------------------
4869 // GfxGouraudTriangleShading
4870 //------------------------------------------------------------------------
4872 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4874 GfxGouraudVertex
*verticesA
, int nVerticesA
,
4875 int (*trianglesA
)[3], int nTrianglesA
,
4876 Function
**funcsA
, int nFuncsA
):
4881 vertices
= verticesA
;
4882 nVertices
= nVerticesA
;
4883 triangles
= trianglesA
;
4884 nTriangles
= nTrianglesA
;
4886 for (i
= 0; i
< nFuncs
; ++i
) {
4887 funcs
[i
] = funcsA
[i
];
4891 GfxGouraudTriangleShading::GfxGouraudTriangleShading(
4892 GfxGouraudTriangleShading
*shading
):
4897 nVertices
= shading
->nVertices
;
4898 vertices
= (GfxGouraudVertex
*)gmallocn(nVertices
, sizeof(GfxGouraudVertex
));
4899 memcpy(vertices
, shading
->vertices
, nVertices
* sizeof(GfxGouraudVertex
));
4900 nTriangles
= shading
->nTriangles
;
4901 triangles
= (int (*)[3])gmallocn(nTriangles
* 3, sizeof(int));
4902 memcpy(triangles
, shading
->triangles
, nTriangles
* 3 * sizeof(int));
4903 nFuncs
= shading
->nFuncs
;
4904 for (i
= 0; i
< nFuncs
; ++i
) {
4905 funcs
[i
] = shading
->funcs
[i
]->copy();
4909 GfxGouraudTriangleShading::~GfxGouraudTriangleShading() {
4914 for (i
= 0; i
< nFuncs
; ++i
) {
4919 GfxGouraudTriangleShading
*GfxGouraudTriangleShading::parse(GfxResources
*res
, int typeA
,
4922 OutputDev
*out
, GfxState
*gfxState
) {
4923 GfxGouraudTriangleShading
*shading
;
4924 Function
*funcsA
[gfxColorMaxComps
];
4926 int coordBits
, compBits
, flagBits
, vertsPerRow
, nRows
;
4927 double xMin
, xMax
, yMin
, yMax
;
4928 double cMin
[gfxColorMaxComps
], cMax
[gfxColorMaxComps
];
4930 double cMul
[gfxColorMaxComps
];
4931 GfxGouraudVertex
*verticesA
;
4932 int (*trianglesA
)[3];
4933 int nComps
, nVerticesA
, nTrianglesA
, vertSize
, triSize
;
4935 Guint c
[gfxColorMaxComps
];
4936 GfxShadingBitBuf
*bitBuf
;
4940 if (dict
->lookup("BitsPerCoordinate", &obj1
)->isInt()) {
4941 coordBits
= obj1
.getInt();
4943 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
4947 if (dict
->lookup("BitsPerComponent", &obj1
)->isInt()) {
4948 compBits
= obj1
.getInt();
4950 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerComponent in shading dictionary");
4954 flagBits
= vertsPerRow
= 0; // make gcc happy
4956 if (dict
->lookup("BitsPerFlag", &obj1
)->isInt()) {
4957 flagBits
= obj1
.getInt();
4959 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerFlag in shading dictionary");
4964 if (dict
->lookup("VerticesPerRow", &obj1
)->isInt()) {
4965 vertsPerRow
= obj1
.getInt();
4967 error(errSyntaxWarning
, -1, "Missing or invalid VerticesPerRow in shading dictionary");
4972 if (dict
->lookup("Decode", &obj1
)->isArray() &&
4973 obj1
.arrayGetLength() >= 6) {
4974 xMin
= obj1
.arrayGet(0, &obj2
)->getNum();
4976 xMax
= obj1
.arrayGet(1, &obj2
)->getNum();
4978 xMul
= (xMax
- xMin
) / (pow(2.0, coordBits
) - 1);
4979 yMin
= obj1
.arrayGet(2, &obj2
)->getNum();
4981 yMax
= obj1
.arrayGet(3, &obj2
)->getNum();
4983 yMul
= (yMax
- yMin
) / (pow(2.0, coordBits
) - 1);
4984 for (i
= 0; 5 + 2*i
< obj1
.arrayGetLength() && i
< gfxColorMaxComps
; ++i
) {
4985 cMin
[i
] = obj1
.arrayGet(4 + 2*i
, &obj2
)->getNum();
4987 cMax
[i
] = obj1
.arrayGet(5 + 2*i
, &obj2
)->getNum();
4989 cMul
[i
] = (cMax
[i
] - cMin
[i
]) / (double)((1 << compBits
) - 1);
4993 error(errSyntaxWarning
, -1, "Missing or invalid Decode array in shading dictionary");
4998 if (!dict
->lookup("Function", &obj1
)->isNull()) {
4999 if (obj1
.isArray()) {
5000 nFuncsA
= obj1
.arrayGetLength();
5001 if (nFuncsA
> gfxColorMaxComps
) {
5002 error(errSyntaxWarning
, -1, "Invalid Function array in shading dictionary");
5005 for (i
= 0; i
< nFuncsA
; ++i
) {
5006 obj1
.arrayGet(i
, &obj2
);
5007 if (!(funcsA
[i
] = Function::parse(&obj2
))) {
5016 if (!(funcsA
[0] = Function::parse(&obj1
))) {
5026 nVerticesA
= nTrianglesA
= 0;
5029 vertSize
= triSize
= 0;
5031 flag
= 0; // make gcc happy
5032 bitBuf
= new GfxShadingBitBuf(str
);
5035 if (!bitBuf
->getBits(flagBits
, &flag
)) {
5039 if (!bitBuf
->getBits(coordBits
, &x
) ||
5040 !bitBuf
->getBits(coordBits
, &y
)) {
5043 for (i
= 0; i
< nComps
; ++i
) {
5044 if (!bitBuf
->getBits(compBits
, &c
[i
])) {
5051 if (nVerticesA
== vertSize
) {
5052 int oldVertSize
= vertSize
;
5053 vertSize
= (vertSize
== 0) ? 16 : 2 * vertSize
;
5054 verticesA
= (GfxGouraudVertex
*)
5055 greallocn(verticesA
, vertSize
, sizeof(GfxGouraudVertex
));
5056 memset(verticesA
+ oldVertSize
, 0, (vertSize
- oldVertSize
) * sizeof(GfxGouraudVertex
));
5058 verticesA
[nVerticesA
].x
= xMin
+ xMul
* (double)x
;
5059 verticesA
[nVerticesA
].y
= yMin
+ yMul
* (double)y
;
5060 for (i
= 0; i
< nComps
; ++i
) {
5061 verticesA
[nVerticesA
].color
.c
[i
] =
5062 dblToCol(cMin
[i
] + cMul
[i
] * (double)c
[i
]);
5065 bitBuf
->flushBits();
5067 if (state
== 0 || state
== 1) {
5069 } else if (state
== 2 || flag
> 0) {
5070 if (nTrianglesA
== triSize
) {
5071 triSize
= (triSize
== 0) ? 16 : 2 * triSize
;
5072 trianglesA
= (int (*)[3])
5073 greallocn(trianglesA
, triSize
* 3, sizeof(int));
5076 trianglesA
[nTrianglesA
][0] = nVerticesA
- 3;
5077 trianglesA
[nTrianglesA
][1] = nVerticesA
- 2;
5078 trianglesA
[nTrianglesA
][2] = nVerticesA
- 1;
5080 } else if (flag
== 1) {
5081 trianglesA
[nTrianglesA
][0] = trianglesA
[nTrianglesA
- 1][1];
5082 trianglesA
[nTrianglesA
][1] = trianglesA
[nTrianglesA
- 1][2];
5083 trianglesA
[nTrianglesA
][2] = nVerticesA
- 1;
5084 } else { // flag == 2
5085 trianglesA
[nTrianglesA
][0] = trianglesA
[nTrianglesA
- 1][0];
5086 trianglesA
[nTrianglesA
][1] = trianglesA
[nTrianglesA
- 1][2];
5087 trianglesA
[nTrianglesA
][2] = nVerticesA
- 1;
5090 } else { // state == 3 && flag == 0
5096 if (typeA
== 5 && nVerticesA
> 0) {
5097 nRows
= nVerticesA
/ vertsPerRow
;
5098 nTrianglesA
= (nRows
- 1) * 2 * (vertsPerRow
- 1);
5099 trianglesA
= (int (*)[3])gmallocn(nTrianglesA
* 3, sizeof(int));
5101 for (i
= 0; i
< nRows
- 1; ++i
) {
5102 for (j
= 0; j
< vertsPerRow
- 1; ++j
) {
5103 trianglesA
[k
][0] = i
* vertsPerRow
+ j
;
5104 trianglesA
[k
][1] = i
* vertsPerRow
+ j
+1;
5105 trianglesA
[k
][2] = (i
+1) * vertsPerRow
+ j
;
5107 trianglesA
[k
][0] = i
* vertsPerRow
+ j
+1;
5108 trianglesA
[k
][1] = (i
+1) * vertsPerRow
+ j
;
5109 trianglesA
[k
][2] = (i
+1) * vertsPerRow
+ j
+1;
5115 shading
= new GfxGouraudTriangleShading(typeA
, verticesA
, nVerticesA
,
5116 trianglesA
, nTrianglesA
,
5118 if (!shading
->init(res
, dict
, out
, gfxState
)) {
5130 GfxShading
*GfxGouraudTriangleShading::copy() {
5131 return new GfxGouraudTriangleShading(this);
5134 void GfxGouraudTriangleShading::getTriangle(
5136 double *x0
, double *y0
, GfxColor
*color0
,
5137 double *x1
, double *y1
, GfxColor
*color1
,
5138 double *x2
, double *y2
, GfxColor
*color2
) {
5140 double out
[gfxColorMaxComps
];
5143 assert(!isParameterized());
5145 v
= triangles
[i
][0];
5146 *x0
= vertices
[v
].x
;
5147 *y0
= vertices
[v
].y
;
5149 in
= colToDbl(vertices
[v
].color
.c
[0]);
5150 for (j
= 0; j
< nFuncs
; ++j
) {
5151 funcs
[j
]->transform(&in
, &out
[j
]);
5153 for (j
= 0; j
< gfxColorMaxComps
; ++j
) {
5154 color0
->c
[j
] = dblToCol(out
[j
]);
5157 *color0
= vertices
[v
].color
;
5159 v
= triangles
[i
][1];
5160 *x1
= vertices
[v
].x
;
5161 *y1
= vertices
[v
].y
;
5163 in
= colToDbl(vertices
[v
].color
.c
[0]);
5164 for (j
= 0; j
< nFuncs
; ++j
) {
5165 funcs
[j
]->transform(&in
, &out
[j
]);
5167 for (j
= 0; j
< gfxColorMaxComps
; ++j
) {
5168 color1
->c
[j
] = dblToCol(out
[j
]);
5171 *color1
= vertices
[v
].color
;
5173 v
= triangles
[i
][2];
5174 *x2
= vertices
[v
].x
;
5175 *y2
= vertices
[v
].y
;
5177 in
= colToDbl(vertices
[v
].color
.c
[0]);
5178 for (j
= 0; j
< nFuncs
; ++j
) {
5179 funcs
[j
]->transform(&in
, &out
[j
]);
5181 for (j
= 0; j
< gfxColorMaxComps
; ++j
) {
5182 color2
->c
[j
] = dblToCol(out
[j
]);
5185 *color2
= vertices
[v
].color
;
5189 void GfxGouraudTriangleShading::getParameterizedColor(double t
, GfxColor
*color
) {
5190 double out
[gfxColorMaxComps
];
5192 for (int j
= 0; j
< nFuncs
; ++j
) {
5193 funcs
[j
]->transform(&t
, &out
[j
]);
5195 for (int j
= 0; j
< gfxColorMaxComps
; ++j
) {
5196 color
->c
[j
] = dblToCol(out
[j
]);
5200 void GfxGouraudTriangleShading::getTriangle(int i
,
5201 double *x0
, double *y0
, double *color0
,
5202 double *x1
, double *y1
, double *color1
,
5203 double *x2
, double *y2
, double *color2
) {
5206 assert(isParameterized());
5208 v
= triangles
[i
][0];
5209 if (likely(v
>= 0 && v
< nVertices
)) {
5210 *x0
= vertices
[v
].x
;
5211 *y0
= vertices
[v
].y
;
5212 *color0
= colToDbl(vertices
[v
].color
.c
[0]);
5214 v
= triangles
[i
][1];
5215 if (likely(v
>= 0 && v
< nVertices
)) {
5216 *x1
= vertices
[v
].x
;
5217 *y1
= vertices
[v
].y
;
5218 *color1
= colToDbl(vertices
[v
].color
.c
[0]);
5220 v
= triangles
[i
][2];
5221 if (likely(v
>= 0 && v
< nVertices
)) {
5222 *x2
= vertices
[v
].x
;
5223 *y2
= vertices
[v
].y
;
5224 *color2
= colToDbl(vertices
[v
].color
.c
[0]);
5228 //------------------------------------------------------------------------
5229 // GfxPatchMeshShading
5230 //------------------------------------------------------------------------
5232 GfxPatchMeshShading::GfxPatchMeshShading(int typeA
,
5233 GfxPatch
*patchesA
, int nPatchesA
,
5234 Function
**funcsA
, int nFuncsA
):
5240 nPatches
= nPatchesA
;
5242 for (i
= 0; i
< nFuncs
; ++i
) {
5243 funcs
[i
] = funcsA
[i
];
5247 GfxPatchMeshShading::GfxPatchMeshShading(GfxPatchMeshShading
*shading
):
5252 nPatches
= shading
->nPatches
;
5253 patches
= (GfxPatch
*)gmallocn(nPatches
, sizeof(GfxPatch
));
5254 memcpy(patches
, shading
->patches
, nPatches
* sizeof(GfxPatch
));
5255 nFuncs
= shading
->nFuncs
;
5256 for (i
= 0; i
< nFuncs
; ++i
) {
5257 funcs
[i
] = shading
->funcs
[i
]->copy();
5261 GfxPatchMeshShading::~GfxPatchMeshShading() {
5265 for (i
= 0; i
< nFuncs
; ++i
) {
5270 GfxPatchMeshShading
*GfxPatchMeshShading::parse(GfxResources
*res
, int typeA
, Dict
*dict
,
5271 Stream
*str
, OutputDev
*out
, GfxState
*state
) {
5272 GfxPatchMeshShading
*shading
;
5273 Function
*funcsA
[gfxColorMaxComps
];
5275 int coordBits
, compBits
, flagBits
;
5276 double xMin
, xMax
, yMin
, yMax
;
5277 double cMin
[gfxColorMaxComps
], cMax
[gfxColorMaxComps
];
5279 double cMul
[gfxColorMaxComps
];
5280 GfxPatch
*patchesA
, *p
;
5281 int nComps
, nPatchesA
, patchesSize
, nPts
, nColors
;
5283 double x
[16], y
[16];
5285 double c
[4][gfxColorMaxComps
];
5287 GfxShadingBitBuf
*bitBuf
;
5291 if (dict
->lookup("BitsPerCoordinate", &obj1
)->isInt()) {
5292 coordBits
= obj1
.getInt();
5294 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerCoordinate in shading dictionary");
5298 if (dict
->lookup("BitsPerComponent", &obj1
)->isInt()) {
5299 compBits
= obj1
.getInt();
5301 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerComponent in shading dictionary");
5305 if (dict
->lookup("BitsPerFlag", &obj1
)->isInt()) {
5306 flagBits
= obj1
.getInt();
5308 error(errSyntaxWarning
, -1, "Missing or invalid BitsPerFlag in shading dictionary");
5312 if (dict
->lookup("Decode", &obj1
)->isArray() &&
5313 obj1
.arrayGetLength() >= 6) {
5314 xMin
= obj1
.arrayGet(0, &obj2
)->getNum();
5316 xMax
= obj1
.arrayGet(1, &obj2
)->getNum();
5318 xMul
= (xMax
- xMin
) / (pow(2.0, coordBits
) - 1);
5319 yMin
= obj1
.arrayGet(2, &obj2
)->getNum();
5321 yMax
= obj1
.arrayGet(3, &obj2
)->getNum();
5323 yMul
= (yMax
- yMin
) / (pow(2.0, coordBits
) - 1);
5324 for (i
= 0; 5 + 2*i
< obj1
.arrayGetLength() && i
< gfxColorMaxComps
; ++i
) {
5325 cMin
[i
] = obj1
.arrayGet(4 + 2*i
, &obj2
)->getNum();
5327 cMax
[i
] = obj1
.arrayGet(5 + 2*i
, &obj2
)->getNum();
5329 cMul
[i
] = (cMax
[i
] - cMin
[i
]) / (double)((1 << compBits
) - 1);
5333 error(errSyntaxWarning
, -1, "Missing or invalid Decode array in shading dictionary");
5338 if (!dict
->lookup("Function", &obj1
)->isNull()) {
5339 if (obj1
.isArray()) {
5340 nFuncsA
= obj1
.arrayGetLength();
5341 if (nFuncsA
> gfxColorMaxComps
) {
5342 error(errSyntaxWarning
, -1, "Invalid Function array in shading dictionary");
5345 for (i
= 0; i
< nFuncsA
; ++i
) {
5346 obj1
.arrayGet(i
, &obj2
);
5347 if (!(funcsA
[i
] = Function::parse(&obj2
))) {
5356 if (!(funcsA
[0] = Function::parse(&obj1
))) {
5369 bitBuf
= new GfxShadingBitBuf(str
);
5371 if (!bitBuf
->getBits(flagBits
, &flag
)) {
5376 case 0: nPts
= 12; nColors
= 4; break;
5380 default: nPts
= 8; nColors
= 2; break;
5384 case 0: nPts
= 16; nColors
= 4; break;
5388 default: nPts
= 12; nColors
= 2; break;
5391 for (i
= 0; i
< nPts
; ++i
) {
5392 if (!bitBuf
->getBits(coordBits
, &xi
) ||
5393 !bitBuf
->getBits(coordBits
, &yi
)) {
5396 x
[i
] = xMin
+ xMul
* (double)xi
;
5397 y
[i
] = yMin
+ yMul
* (double)yi
;
5402 for (i
= 0; i
< nColors
; ++i
) {
5403 for (j
= 0; j
< nComps
; ++j
) {
5404 if (!bitBuf
->getBits(compBits
, &ci
)) {
5407 c
[i
][j
] = cMin
[j
] + cMul
[j
] * (double)ci
;
5408 if( nFuncsA
== 0 ) {
5409 // ... and colorspace values can also be stored into doubles.
5410 // They will be casted later.
5411 c
[i
][j
] = dblToCol(c
[i
][j
]);
5421 if (nPatchesA
== patchesSize
) {
5422 int oldPatchesSize
= patchesSize
;
5423 patchesSize
= (patchesSize
== 0) ? 16 : 2 * patchesSize
;
5424 patchesA
= (GfxPatch
*)greallocn(patchesA
,
5425 patchesSize
, sizeof(GfxPatch
));
5426 memset(patchesA
+ oldPatchesSize
, 0, (patchesSize
- oldPatchesSize
) * sizeof(GfxPatch
));
5428 p
= &patchesA
[nPatchesA
];
5456 for (j
= 0; j
< nComps
; ++j
) {
5457 p
->color
[0][0].c
[j
] = c
[0][j
];
5458 p
->color
[0][1].c
[j
] = c
[1][j
];
5459 p
->color
[1][1].c
[j
] = c
[2][j
];
5460 p
->color
[1][0].c
[j
] = c
[3][j
];
5464 if (nPatchesA
== 0) {
5467 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[0][3];
5468 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[0][3];
5469 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[1][3];
5470 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[1][3];
5471 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[2][3];
5472 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[2][3];
5473 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[3][3];
5474 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[3][3];
5491 for (j
= 0; j
< nComps
; ++j
) {
5492 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[0][1].c
[j
];
5493 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[1][1].c
[j
];
5494 p
->color
[1][1].c
[j
] = c
[0][j
];
5495 p
->color
[1][0].c
[j
] = c
[1][j
];
5499 if (nPatchesA
== 0) {
5502 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[3][3];
5503 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[3][3];
5504 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[3][2];
5505 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[3][2];
5506 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[3][1];
5507 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[3][1];
5508 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[3][0];
5509 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[3][0];
5526 for (j
= 0; j
< nComps
; ++j
) {
5527 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[1][1].c
[j
];
5528 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[1][0].c
[j
];
5529 p
->color
[1][1].c
[j
] = c
[0][j
];
5530 p
->color
[1][0].c
[j
] = c
[1][j
];
5534 if (nPatchesA
== 0) {
5537 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[3][0];
5538 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[3][0];
5539 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[2][0];
5540 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[2][0];
5541 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[1][0];
5542 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[1][0];
5543 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[0][0];
5544 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[0][0];
5561 for (j
= 0; j
< nComps
; ++j
) {
5562 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[1][0].c
[j
];
5563 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[0][0].c
[j
];
5564 p
->color
[1][1].c
[j
] = c
[0][j
];
5565 p
->color
[1][0].c
[j
] = c
[1][j
];
5604 for (j
= 0; j
< nComps
; ++j
) {
5605 p
->color
[0][0].c
[j
] = c
[0][j
];
5606 p
->color
[0][1].c
[j
] = c
[1][j
];
5607 p
->color
[1][1].c
[j
] = c
[2][j
];
5608 p
->color
[1][0].c
[j
] = c
[3][j
];
5612 if (nPatchesA
== 0) {
5615 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[0][3];
5616 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[0][3];
5617 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[1][3];
5618 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[1][3];
5619 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[2][3];
5620 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[2][3];
5621 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[3][3];
5622 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[3][3];
5647 for (j
= 0; j
< nComps
; ++j
) {
5648 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[0][1].c
[j
];
5649 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[1][1].c
[j
];
5650 p
->color
[1][1].c
[j
] = c
[0][j
];
5651 p
->color
[1][0].c
[j
] = c
[1][j
];
5655 if (nPatchesA
== 0) {
5658 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[3][3];
5659 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[3][3];
5660 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[3][2];
5661 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[3][2];
5662 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[3][1];
5663 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[3][1];
5664 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[3][0];
5665 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[3][0];
5690 for (j
= 0; j
< nComps
; ++j
) {
5691 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[1][1].c
[j
];
5692 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[1][0].c
[j
];
5693 p
->color
[1][1].c
[j
] = c
[0][j
];
5694 p
->color
[1][0].c
[j
] = c
[1][j
];
5698 if (nPatchesA
== 0) {
5701 p
->x
[0][0] = patchesA
[nPatchesA
-1].x
[3][0];
5702 p
->y
[0][0] = patchesA
[nPatchesA
-1].y
[3][0];
5703 p
->x
[0][1] = patchesA
[nPatchesA
-1].x
[2][0];
5704 p
->y
[0][1] = patchesA
[nPatchesA
-1].y
[2][0];
5705 p
->x
[0][2] = patchesA
[nPatchesA
-1].x
[1][0];
5706 p
->y
[0][2] = patchesA
[nPatchesA
-1].y
[1][0];
5707 p
->x
[0][3] = patchesA
[nPatchesA
-1].x
[0][0];
5708 p
->y
[0][3] = patchesA
[nPatchesA
-1].y
[0][0];
5733 for (j
= 0; j
< nComps
; ++j
) {
5734 p
->color
[0][0].c
[j
] = patchesA
[nPatchesA
-1].color
[1][0].c
[j
];
5735 p
->color
[0][1].c
[j
] = patchesA
[nPatchesA
-1].color
[0][0].c
[j
];
5736 p
->color
[1][1].c
[j
] = c
[0][j
];
5737 p
->color
[1][0].c
[j
] = c
[1][j
];
5743 bitBuf
->flushBits();
5748 for (i
= 0; i
< nPatchesA
; ++i
) {
5750 p
->x
[1][1] = (-4 * p
->x
[0][0]
5751 +6 * (p
->x
[0][1] + p
->x
[1][0])
5752 -2 * (p
->x
[0][3] + p
->x
[3][0])
5753 +3 * (p
->x
[3][1] + p
->x
[1][3])
5755 p
->y
[1][1] = (-4 * p
->y
[0][0]
5756 +6 * (p
->y
[0][1] + p
->y
[1][0])
5757 -2 * (p
->y
[0][3] + p
->y
[3][0])
5758 +3 * (p
->y
[3][1] + p
->y
[1][3])
5760 p
->x
[1][2] = (-4 * p
->x
[0][3]
5761 +6 * (p
->x
[0][2] + p
->x
[1][3])
5762 -2 * (p
->x
[0][0] + p
->x
[3][3])
5763 +3 * (p
->x
[3][2] + p
->x
[1][0])
5765 p
->y
[1][2] = (-4 * p
->y
[0][3]
5766 +6 * (p
->y
[0][2] + p
->y
[1][3])
5767 -2 * (p
->y
[0][0] + p
->y
[3][3])
5768 +3 * (p
->y
[3][2] + p
->y
[1][0])
5770 p
->x
[2][1] = (-4 * p
->x
[3][0]
5771 +6 * (p
->x
[3][1] + p
->x
[2][0])
5772 -2 * (p
->x
[3][3] + p
->x
[0][0])
5773 +3 * (p
->x
[0][1] + p
->x
[2][3])
5775 p
->y
[2][1] = (-4 * p
->y
[3][0]
5776 +6 * (p
->y
[3][1] + p
->y
[2][0])
5777 -2 * (p
->y
[3][3] + p
->y
[0][0])
5778 +3 * (p
->y
[0][1] + p
->y
[2][3])
5780 p
->x
[2][2] = (-4 * p
->x
[3][3]
5781 +6 * (p
->x
[3][2] + p
->x
[2][3])
5782 -2 * (p
->x
[3][0] + p
->x
[0][3])
5783 +3 * (p
->x
[0][2] + p
->x
[2][0])
5785 p
->y
[2][2] = (-4 * p
->y
[3][3]
5786 +6 * (p
->y
[3][2] + p
->y
[2][3])
5787 -2 * (p
->y
[3][0] + p
->y
[0][3])
5788 +3 * (p
->y
[0][2] + p
->y
[2][0])
5793 shading
= new GfxPatchMeshShading(typeA
, patchesA
, nPatchesA
,
5795 if (!shading
->init(res
, dict
, out
, state
)) {
5807 void GfxPatchMeshShading::getParameterizedColor(double t
, GfxColor
*color
) {
5808 double out
[gfxColorMaxComps
];
5810 for (int j
= 0; j
< nFuncs
; ++j
) {
5811 funcs
[j
]->transform(&t
, &out
[j
]);
5813 for (int j
= 0; j
< gfxColorMaxComps
; ++j
) {
5814 color
->c
[j
] = dblToCol(out
[j
]);
5818 GfxShading
*GfxPatchMeshShading::copy() {
5819 return new GfxPatchMeshShading(this);
5822 //------------------------------------------------------------------------
5824 //------------------------------------------------------------------------
5826 GfxImageColorMap::GfxImageColorMap(int bitsA
, Object
*decode
,
5827 GfxColorSpace
*colorSpaceA
) {
5828 GfxIndexedColorSpace
*indexedCS
;
5829 GfxSeparationColorSpace
*sepCS
;
5830 int maxPixel
, indexHigh
;
5831 Guchar
*indexedLookup
;
5834 double x
[gfxColorMaxComps
];
5835 double y
[gfxColorMaxComps
];
5838 GBool useByteLookup
;
5843 // bits per component and color space
5845 maxPixel
= (1 << bits
) - 1;
5846 colorSpace
= colorSpaceA
;
5848 // this is a hack to support 16 bits images, everywhere
5849 // we assume a component fits in 8 bits, with this hack
5850 // we treat 16 bit images as 8 bit ones until it's fixed correctly.
5851 // The hack has another part on ImageStream::getLine
5852 if (maxPixel
> 255) maxPixel
= 255;
5855 for (k
= 0; k
< gfxColorMaxComps
; ++k
) {
5862 if (decode
->isNull()) {
5863 nComps
= colorSpace
->getNComps();
5864 colorSpace
->getDefaultRanges(decodeLow
, decodeRange
, maxPixel
);
5865 } else if (decode
->isArray()) {
5866 nComps
= decode
->arrayGetLength() / 2;
5867 if (nComps
< colorSpace
->getNComps()) {
5870 if (nComps
> colorSpace
->getNComps()) {
5871 error(errSyntaxWarning
, -1, "Too many elements in Decode array");
5872 nComps
= colorSpace
->getNComps();
5874 for (i
= 0; i
< nComps
; ++i
) {
5875 decode
->arrayGet(2*i
, &obj
);
5879 decodeLow
[i
] = obj
.getNum();
5881 decode
->arrayGet(2*i
+1, &obj
);
5885 decodeRange
[i
] = obj
.getNum() - decodeLow
[i
];
5892 // Construct a lookup table -- this stores pre-computed decoded
5893 // values for each component, i.e., the result of applying the
5894 // decode mapping to each possible image pixel component value.
5895 for (k
= 0; k
< nComps
; ++k
) {
5896 lookup
[k
] = (GfxColorComp
*)gmallocn(maxPixel
+ 1,
5897 sizeof(GfxColorComp
));
5898 for (i
= 0; i
<= maxPixel
; ++i
) {
5899 lookup
[k
][i
] = dblToCol(decodeLow
[k
] +
5900 (i
* decodeRange
[k
]) / maxPixel
);
5904 // Optimization: for Indexed and Separation color spaces (which have
5905 // only one component), we pre-compute a second lookup table with
5909 useByteLookup
= gFalse
;
5910 switch (colorSpace
->getMode()) {
5912 // Note that indexHigh may not be the same as maxPixel --
5913 // Distiller will remove unused palette entries, resulting in
5914 // indexHigh < maxPixel.
5915 indexedCS
= (GfxIndexedColorSpace
*)colorSpace
;
5916 colorSpace2
= indexedCS
->getBase();
5917 indexHigh
= indexedCS
->getIndexHigh();
5918 nComps2
= colorSpace2
->getNComps();
5919 indexedLookup
= indexedCS
->getLookup();
5920 colorSpace2
->getDefaultRanges(x
, y
, indexHigh
);
5921 if (colorSpace2
->useGetGrayLine() || colorSpace2
->useGetRGBLine() || colorSpace2
->useGetCMYKLine() || colorSpace2
->useGetDeviceNLine()) {
5922 byte_lookup
= (Guchar
*)gmallocn ((maxPixel
+ 1), nComps2
);
5923 useByteLookup
= gTrue
;
5925 for (k
= 0; k
< nComps2
; ++k
) {
5926 lookup2
[k
] = (GfxColorComp
*)gmallocn(maxPixel
+ 1,
5927 sizeof(GfxColorComp
));
5928 for (i
= 0; i
<= maxPixel
; ++i
) {
5929 j
= (int)(decodeLow
[0] + (i
* decodeRange
[0]) / maxPixel
+ 0.5);
5932 } else if (j
> indexHigh
) {
5936 mapped
= x
[k
] + (indexedLookup
[j
*nComps2
+ k
] / 255.0) * y
[k
];
5937 lookup2
[k
][i
] = dblToCol(mapped
);
5939 byte_lookup
[i
* nComps2
+ k
] = (Guchar
) (mapped
* 255);
5944 sepCS
= (GfxSeparationColorSpace
*)colorSpace
;
5945 colorSpace2
= sepCS
->getAlt();
5946 nComps2
= colorSpace2
->getNComps();
5947 sepFunc
= sepCS
->getFunc();
5948 if (colorSpace2
->useGetGrayLine() || colorSpace2
->useGetRGBLine() || colorSpace2
->useGetCMYKLine() || colorSpace2
->useGetDeviceNLine()) {
5949 byte_lookup
= (Guchar
*)gmallocn ((maxPixel
+ 1), nComps2
);
5950 useByteLookup
= gTrue
;
5952 for (k
= 0; k
< nComps2
; ++k
) {
5953 lookup2
[k
] = (GfxColorComp
*)gmallocn(maxPixel
+ 1,
5954 sizeof(GfxColorComp
));
5955 for (i
= 0; i
<= maxPixel
; ++i
) {
5956 x
[0] = decodeLow
[0] + (i
* decodeRange
[0]) / maxPixel
;
5957 sepFunc
->transform(x
, y
);
5958 lookup2
[k
][i
] = dblToCol(y
[k
]);
5960 byte_lookup
[i
*nComps2
+ k
] = (Guchar
) (y
[k
] * 255);
5965 if (colorSpace
->useGetGrayLine() || colorSpace
->useGetRGBLine() || colorSpace
->useGetCMYKLine() || colorSpace
->useGetDeviceNLine()) {
5966 byte_lookup
= (Guchar
*)gmallocn ((maxPixel
+ 1), nComps
);
5967 useByteLookup
= gTrue
;
5969 for (k
= 0; k
< nComps
; ++k
) {
5970 lookup2
[k
] = (GfxColorComp
*)gmallocn(maxPixel
+ 1,
5971 sizeof(GfxColorComp
));
5972 for (i
= 0; i
<= maxPixel
; ++i
) {
5973 mapped
= decodeLow
[k
] + (i
* decodeRange
[k
]) / maxPixel
;
5974 lookup2
[k
][i
] = dblToCol(mapped
);
5975 if (useByteLookup
) {
5978 byte
= (int) (mapped
* 255.0 + 0.5);
5981 else if (byte
> 255)
5983 byte_lookup
[i
* nComps
+ k
] = byte
;
5997 GfxImageColorMap::GfxImageColorMap(GfxImageColorMap
*colorMap
) {
6000 colorSpace
= colorMap
->colorSpace
->copy();
6001 bits
= colorMap
->bits
;
6002 nComps
= colorMap
->nComps
;
6003 nComps2
= colorMap
->nComps2
;
6004 useMatte
= colorMap
->useMatte
;
6005 matteColor
= colorMap
->matteColor
;
6007 for (k
= 0; k
< gfxColorMaxComps
; ++k
) {
6011 if (colorSpace
->getMode() == csIndexed
) {
6012 colorSpace2
= ((GfxIndexedColorSpace
*)colorSpace
)->getBase();
6013 for (k
= 0; k
< nComps2
; ++k
) {
6014 lookup
[k
] = (GfxColorComp
*)gmallocn(n
, sizeof(GfxColorComp
));
6015 memcpy(lookup
[k
], colorMap
->lookup
[k
], n
* sizeof(GfxColorComp
));
6017 } else if (colorSpace
->getMode() == csSeparation
) {
6018 colorSpace2
= ((GfxSeparationColorSpace
*)colorSpace
)->getAlt();
6019 for (k
= 0; k
< nComps2
; ++k
) {
6020 lookup
[k
] = (GfxColorComp
*)gmallocn(n
, sizeof(GfxColorComp
));
6021 memcpy(lookup
[k
], colorMap
->lookup
[k
], n
* sizeof(GfxColorComp
));
6024 for (k
= 0; k
< nComps
; ++k
) {
6025 lookup
[k
] = (GfxColorComp
*)gmallocn(n
, sizeof(GfxColorComp
));
6026 memcpy(lookup
[k
], colorMap
->lookup
[k
], n
* sizeof(GfxColorComp
));
6029 if (colorMap
->byte_lookup
) {
6030 int nc
= colorSpace2
? nComps2
: nComps
;
6032 byte_lookup
= (Guchar
*)gmallocn (n
, nc
);
6033 memcpy(byte_lookup
, colorMap
->byte_lookup
, n
* nc
);
6035 for (i
= 0; i
< nComps
; ++i
) {
6036 decodeLow
[i
] = colorMap
->decodeLow
[i
];
6037 decodeRange
[i
] = colorMap
->decodeRange
[i
];
6042 GfxImageColorMap::~GfxImageColorMap() {
6046 for (i
= 0; i
< gfxColorMaxComps
; ++i
) {
6053 void GfxImageColorMap::getGray(Guchar
*x
, GfxGray
*gray
) {
6058 for (i
= 0; i
< nComps2
; ++i
) {
6059 color
.c
[i
] = lookup2
[i
][x
[0]];
6061 colorSpace2
->getGray(&color
, gray
);
6063 for (i
= 0; i
< nComps
; ++i
) {
6064 color
.c
[i
] = lookup2
[i
][x
[i
]];
6066 colorSpace
->getGray(&color
, gray
);
6070 void GfxImageColorMap::getRGB(Guchar
*x
, GfxRGB
*rgb
) {
6075 for (i
= 0; i
< nComps2
; ++i
) {
6076 color
.c
[i
] = lookup2
[i
][x
[0]];
6078 colorSpace2
->getRGB(&color
, rgb
);
6080 for (i
= 0; i
< nComps
; ++i
) {
6081 color
.c
[i
] = lookup2
[i
][x
[i
]];
6083 colorSpace
->getRGB(&color
, rgb
);
6087 void GfxImageColorMap::getGrayLine(Guchar
*in
, Guchar
*out
, int length
) {
6089 Guchar
*inp
, *tmp_line
;
6091 if ((colorSpace2
&& !colorSpace2
->useGetGrayLine ()) ||
6092 (!colorSpace2
&& !colorSpace
->useGetGrayLine ())) {
6096 for (i
= 0; i
< length
; i
++) {
6097 getGray (inp
, &gray
);
6098 out
[i
] = colToByte(gray
);
6104 switch (colorSpace
->getMode()) {
6107 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6108 for (i
= 0; i
< length
; i
++) {
6109 for (j
= 0; j
< nComps2
; j
++) {
6110 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6113 colorSpace2
->getGrayLine(tmp_line
, out
, length
);
6119 for (j
= 0; j
< length
; j
++)
6120 for (i
= 0; i
< nComps
; i
++) {
6121 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6124 colorSpace
->getGrayLine(in
, out
, length
);
6130 void GfxImageColorMap::getRGBLine(Guchar
*in
, unsigned int *out
, int length
) {
6132 Guchar
*inp
, *tmp_line
;
6134 if (!useRGBLine()) {
6138 for (i
= 0; i
< length
; i
++) {
6141 ((int) colToByte(rgb
.r
) << 16) |
6142 ((int) colToByte(rgb
.g
) << 8) |
6143 ((int) colToByte(rgb
.b
) << 0);
6149 switch (colorSpace
->getMode()) {
6152 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6153 for (i
= 0; i
< length
; i
++) {
6154 for (j
= 0; j
< nComps2
; j
++) {
6155 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6158 colorSpace2
->getRGBLine(tmp_line
, out
, length
);
6164 for (j
= 0; j
< length
; j
++)
6165 for (i
= 0; i
< nComps
; i
++) {
6166 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6169 colorSpace
->getRGBLine(in
, out
, length
);
6175 void GfxImageColorMap::getRGBLine(Guchar
*in
, Guchar
*out
, int length
) {
6177 Guchar
*inp
, *tmp_line
;
6179 if (!useRGBLine()) {
6183 for (i
= 0; i
< length
; i
++) {
6185 *out
++ = colToByte(rgb
.r
);
6186 *out
++ = colToByte(rgb
.g
);
6187 *out
++ = colToByte(rgb
.b
);
6193 switch (colorSpace
->getMode()) {
6196 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6197 for (i
= 0; i
< length
; i
++) {
6198 for (j
= 0; j
< nComps2
; j
++) {
6199 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6202 colorSpace2
->getRGBLine(tmp_line
, out
, length
);
6208 for (j
= 0; j
< length
; j
++)
6209 for (i
= 0; i
< nComps
; i
++) {
6210 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6213 colorSpace
->getRGBLine(in
, out
, length
);
6219 void GfxImageColorMap::getRGBXLine(Guchar
*in
, Guchar
*out
, int length
) {
6221 Guchar
*inp
, *tmp_line
;
6223 if (!useRGBLine()) {
6227 for (i
= 0; i
< length
; i
++) {
6229 *out
++ = colToByte(rgb
.r
);
6230 *out
++ = colToByte(rgb
.g
);
6231 *out
++ = colToByte(rgb
.b
);
6238 switch (colorSpace
->getMode()) {
6241 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6242 for (i
= 0; i
< length
; i
++) {
6243 for (j
= 0; j
< nComps2
; j
++) {
6244 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6247 colorSpace2
->getRGBXLine(tmp_line
, out
, length
);
6253 for (j
= 0; j
< length
; j
++)
6254 for (i
= 0; i
< nComps
; i
++) {
6255 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6258 colorSpace
->getRGBXLine(in
, out
, length
);
6264 void GfxImageColorMap::getCMYKLine(Guchar
*in
, Guchar
*out
, int length
) {
6266 Guchar
*inp
, *tmp_line
;
6268 if (!useCMYKLine()) {
6272 for (i
= 0; i
< length
; i
++) {
6273 getCMYK (inp
, &cmyk
);
6274 *out
++ = colToByte(cmyk
.c
);
6275 *out
++ = colToByte(cmyk
.m
);
6276 *out
++ = colToByte(cmyk
.y
);
6277 *out
++ = colToByte(cmyk
.k
);
6283 switch (colorSpace
->getMode()) {
6286 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6287 for (i
= 0; i
< length
; i
++) {
6288 for (j
= 0; j
< nComps2
; j
++) {
6289 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6292 colorSpace2
->getCMYKLine(tmp_line
, out
, length
);
6298 for (j
= 0; j
< length
; j
++)
6299 for (i
= 0; i
< nComps
; i
++) {
6300 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6303 colorSpace
->getCMYKLine(in
, out
, length
);
6309 void GfxImageColorMap::getDeviceNLine(Guchar
*in
, Guchar
*out
, int length
) {
6311 Guchar
*inp
, *tmp_line
;
6313 if (!useDeviceNLine()) {
6317 for (i
= 0; i
< length
; i
++) {
6318 getDeviceN (inp
, &deviceN
);
6319 for (int j
= 0; j
< SPOT_NCOMPS
+4; j
++)
6320 *out
++ = deviceN
.c
[j
];
6326 switch (colorSpace
->getMode()) {
6329 tmp_line
= (Guchar
*) gmallocn (length
, nComps2
);
6330 for (i
= 0; i
< length
; i
++) {
6331 for (j
= 0; j
< nComps2
; j
++) {
6332 tmp_line
[i
* nComps2
+ j
] = byte_lookup
[in
[i
] * nComps2
+ j
];
6335 colorSpace2
->getDeviceNLine(tmp_line
, out
, length
);
6341 for (j
= 0; j
< length
; j
++)
6342 for (i
= 0; i
< nComps
; i
++) {
6343 *inp
= byte_lookup
[*inp
* nComps
+ i
];
6346 colorSpace
->getDeviceNLine(in
, out
, length
);
6352 void GfxImageColorMap::getCMYK(Guchar
*x
, GfxCMYK
*cmyk
) {
6357 for (i
= 0; i
< nComps2
; ++i
) {
6358 color
.c
[i
] = lookup2
[i
][x
[0]];
6360 colorSpace2
->getCMYK(&color
, cmyk
);
6362 for (i
= 0; i
< nComps
; ++i
) {
6363 color
.c
[i
] = lookup
[i
][x
[i
]];
6365 colorSpace
->getCMYK(&color
, cmyk
);
6369 void GfxImageColorMap::getDeviceN(Guchar
*x
, GfxColor
*deviceN
) {
6374 for (i
= 0; i
< nComps2
; ++i
) {
6375 color
.c
[i
] = lookup2
[i
][x
[0]];
6377 colorSpace2
->getDeviceN(&color
, deviceN
);
6379 for (i
= 0; i
< nComps
; ++i
) {
6380 color
.c
[i
] = lookup
[i
][x
[i
]];
6382 colorSpace
->getDeviceN(&color
, deviceN
);
6386 void GfxImageColorMap::getColor(Guchar
*x
, GfxColor
*color
) {
6389 maxPixel
= (1 << bits
) - 1;
6390 for (i
= 0; i
< nComps
; ++i
) {
6391 color
->c
[i
] = dblToCol(decodeLow
[i
] + (x
[i
] * decodeRange
[i
]) / maxPixel
);
6395 //------------------------------------------------------------------------
6396 // GfxSubpath and GfxPath
6397 //------------------------------------------------------------------------
6399 GfxSubpath::GfxSubpath(double x1
, double y1
) {
6401 x
= (double *)gmallocn(size
, sizeof(double));
6402 y
= (double *)gmallocn(size
, sizeof(double));
6403 curve
= (GBool
*)gmallocn(size
, sizeof(GBool
));
6411 GfxSubpath::~GfxSubpath() {
6418 GfxSubpath::GfxSubpath(GfxSubpath
*subpath
) {
6419 size
= subpath
->size
;
6421 x
= (double *)gmallocn(size
, sizeof(double));
6422 y
= (double *)gmallocn(size
, sizeof(double));
6423 curve
= (GBool
*)gmallocn(size
, sizeof(GBool
));
6424 memcpy(x
, subpath
->x
, n
* sizeof(double));
6425 memcpy(y
, subpath
->y
, n
* sizeof(double));
6426 memcpy(curve
, subpath
->curve
, n
* sizeof(GBool
));
6427 closed
= subpath
->closed
;
6430 void GfxSubpath::lineTo(double x1
, double y1
) {
6433 x
= (double *)greallocn(x
, size
, sizeof(double));
6434 y
= (double *)greallocn(y
, size
, sizeof(double));
6435 curve
= (GBool
*)greallocn(curve
, size
, sizeof(GBool
));
6443 void GfxSubpath::curveTo(double x1
, double y1
, double x2
, double y2
,
6444 double x3
, double y3
) {
6447 x
= (double *)greallocn(x
, size
, sizeof(double));
6448 y
= (double *)greallocn(y
, size
, sizeof(double));
6449 curve
= (GBool
*)greallocn(curve
, size
, sizeof(GBool
));
6457 curve
[n
] = curve
[n
+1] = gTrue
;
6458 curve
[n
+2] = gFalse
;
6462 void GfxSubpath::close() {
6463 if (x
[n
-1] != x
[0] || y
[n
-1] != y
[0]) {
6469 void GfxSubpath::offset(double dx
, double dy
) {
6472 for (i
= 0; i
< n
; ++i
) {
6478 GfxPath::GfxPath() {
6482 firstX
= firstY
= 0;
6483 subpaths
= (GfxSubpath
**)gmallocn(size
, sizeof(GfxSubpath
*));
6486 GfxPath::~GfxPath() {
6489 for (i
= 0; i
< n
; ++i
)
6495 GfxPath::GfxPath(GBool justMoved1
, double firstX1
, double firstY1
,
6496 GfxSubpath
**subpaths1
, int n1
, int size1
) {
6499 justMoved
= justMoved1
;
6504 subpaths
= (GfxSubpath
**)gmallocn(size
, sizeof(GfxSubpath
*));
6505 for (i
= 0; i
< n
; ++i
)
6506 subpaths
[i
] = subpaths1
[i
]->copy();
6509 void GfxPath::moveTo(double x
, double y
) {
6515 void GfxPath::lineTo(double x
, double y
) {
6516 if (justMoved
|| (n
> 0 && subpaths
[n
-1]->isClosed())) {
6519 subpaths
= (GfxSubpath
**)
6520 greallocn(subpaths
, size
, sizeof(GfxSubpath
*));
6523 subpaths
[n
] = new GfxSubpath(firstX
, firstY
);
6525 subpaths
[n
] = new GfxSubpath(subpaths
[n
-1]->getLastX(),
6526 subpaths
[n
-1]->getLastY());
6531 subpaths
[n
-1]->lineTo(x
, y
);
6534 void GfxPath::curveTo(double x1
, double y1
, double x2
, double y2
,
6535 double x3
, double y3
) {
6536 if (justMoved
|| (n
> 0 && subpaths
[n
-1]->isClosed())) {
6539 subpaths
= (GfxSubpath
**)
6540 greallocn(subpaths
, size
, sizeof(GfxSubpath
*));
6543 subpaths
[n
] = new GfxSubpath(firstX
, firstY
);
6545 subpaths
[n
] = new GfxSubpath(subpaths
[n
-1]->getLastX(),
6546 subpaths
[n
-1]->getLastY());
6551 subpaths
[n
-1]->curveTo(x1
, y1
, x2
, y2
, x3
, y3
);
6554 void GfxPath::close() {
6555 // this is necessary to handle the pathological case of
6556 // moveto/closepath/clip, which defines an empty clipping region
6560 subpaths
= (GfxSubpath
**)
6561 greallocn(subpaths
, size
, sizeof(GfxSubpath
*));
6563 subpaths
[n
] = new GfxSubpath(firstX
, firstY
);
6567 subpaths
[n
-1]->close();
6570 void GfxPath::append(GfxPath
*path
) {
6573 if (n
+ path
->n
> size
) {
6575 subpaths
= (GfxSubpath
**)
6576 greallocn(subpaths
, size
, sizeof(GfxSubpath
*));
6578 for (i
= 0; i
< path
->n
; ++i
) {
6579 subpaths
[n
++] = path
->subpaths
[i
]->copy();
6584 void GfxPath::offset(double dx
, double dy
) {
6587 for (i
= 0; i
< n
; ++i
) {
6588 subpaths
[i
]->offset(dx
, dy
);
6592 //------------------------------------------------------------------------
6594 //------------------------------------------------------------------------
6595 GfxState::ReusablePathIterator::ReusablePathIterator(GfxPath
*path
)
6602 if( path
->getNumSubpaths() ) {
6603 curSubPath
= path
->getSubpath(subPathOff
);
6604 numCoords
= curSubPath
->getNumPoints();
6608 bool GfxState::ReusablePathIterator::isEnd() const {
6609 return coordOff
>= numCoords
;
6612 void GfxState::ReusablePathIterator::next() {
6614 if (coordOff
== numCoords
) {
6616 if (subPathOff
< path
->getNumSubpaths()) {
6618 curSubPath
= path
->getSubpath(subPathOff
);
6619 numCoords
= curSubPath
->getNumPoints();
6624 void GfxState::ReusablePathIterator::setCoord(double x
, double y
) {
6625 curSubPath
->setX(coordOff
, x
);
6626 curSubPath
->setY(coordOff
, y
);
6629 void GfxState::ReusablePathIterator::reset() {
6632 curSubPath
= path
->getSubpath(0);
6633 numCoords
= curSubPath
->getNumPoints();
6636 GfxState::GfxState(double hDPIA
, double vDPIA
, PDFRectangle
*pageBox
,
6637 int rotateA
, GBool upsideDown
) {
6651 ctm
[1] = upsideDown
? ky
: -ky
;
6655 ctm
[5] = ky
* (upsideDown
? -px1
: px2
);
6656 pageWidth
= kx
* (py2
- py1
);
6657 pageHeight
= ky
* (px2
- px1
);
6658 } else if (rotate
== 180) {
6662 ctm
[3] = upsideDown
? ky
: -ky
;
6664 ctm
[5] = ky
* (upsideDown
? -py1
: py2
);
6665 pageWidth
= kx
* (px2
- px1
);
6666 pageHeight
= ky
* (py2
- py1
);
6667 } else if (rotate
== 270) {
6669 ctm
[1] = upsideDown
? -ky
: ky
;
6673 ctm
[5] = ky
* (upsideDown
? px2
: -px1
);
6674 pageWidth
= kx
* (py2
- py1
);
6675 pageHeight
= ky
* (px2
- px1
);
6680 ctm
[3] = upsideDown
? -ky
: ky
;
6682 ctm
[5] = ky
* (upsideDown
? py2
: -py1
);
6683 pageWidth
= kx
* (px2
- px1
);
6684 pageHeight
= ky
* (py2
- py1
);
6687 fillColorSpace
= new GfxDeviceGrayColorSpace();
6688 strokeColorSpace
= new GfxDeviceGrayColorSpace();
6690 strokeColor
.c
[0] = 0;
6692 strokePattern
= NULL
;
6693 blendMode
= gfxBlendNormal
;
6696 fillOverprint
= gFalse
;
6697 strokeOverprint
= gFalse
;
6699 transfer
[0] = transfer
[1] = transfer
[2] = transfer
[3] = NULL
;
6709 strokeAdjust
= gFalse
;
6710 alphaIsShape
= gFalse
;
6711 textKnockout
= gFalse
;
6715 textMat
[0] = 1; textMat
[1] = 0;
6716 textMat
[2] = 0; textMat
[3] = 1;
6717 textMat
[4] = 0; textMat
[5] = 0;
6725 path
= new GfxPath();
6731 clipXMax
= pageWidth
;
6732 clipYMax
= pageHeight
;
6734 renderingIntent
[0] = 0;
6738 GfxColorSpace::setupColorProfiles();
6739 XYZ2DisplayTransformRelCol
= NULL
;
6740 XYZ2DisplayTransformAbsCol
= NULL
;
6741 XYZ2DisplayTransformSat
= NULL
;
6742 XYZ2DisplayTransformPerc
= NULL
;
6743 localDisplayProfile
= NULL
;
6744 displayProfileRef
= 0;
6748 GfxState::~GfxState() {
6751 if (fillColorSpace
) {
6752 delete fillColorSpace
;
6754 if (strokeColorSpace
) {
6755 delete strokeColorSpace
;
6760 if (strokePattern
) {
6761 delete strokePattern
;
6763 for (i
= 0; i
< 4; ++i
) {
6770 // this gets set to NULL by restore()
6777 if (XYZ2DisplayTransformRelCol
) {
6778 if (XYZ2DisplayTransformRelCol
->unref() == 0)
6779 delete XYZ2DisplayTransformRelCol
;
6781 if (XYZ2DisplayTransformAbsCol
) {
6782 if (XYZ2DisplayTransformAbsCol
->unref() == 0)
6783 delete XYZ2DisplayTransformAbsCol
;
6785 if (XYZ2DisplayTransformSat
) {
6786 if (XYZ2DisplayTransformSat
->unref() == 0)
6787 delete XYZ2DisplayTransformSat
;
6789 if (XYZ2DisplayTransformPerc
) {
6790 if (XYZ2DisplayTransformPerc
->unref() == 0)
6791 delete XYZ2DisplayTransformPerc
;
6793 if (--displayProfileRef
== 0 && localDisplayProfile
!= NULL
) {
6794 cmsCloseProfile(localDisplayProfile
);
6800 GfxState::GfxState(GfxState
*state
, GBool copyPath
) {
6803 memcpy(this, state
, sizeof(GfxState
));
6804 if (fillColorSpace
) {
6805 fillColorSpace
= state
->fillColorSpace
->copy();
6807 if (strokeColorSpace
) {
6808 strokeColorSpace
= state
->strokeColorSpace
->copy();
6811 fillPattern
= state
->fillPattern
->copy();
6813 if (strokePattern
) {
6814 strokePattern
= state
->strokePattern
->copy();
6816 for (i
= 0; i
< 4; ++i
) {
6818 transfer
[i
] = state
->transfer
[i
]->copy();
6821 if (lineDashLength
> 0) {
6822 lineDash
= (double *)gmallocn(lineDashLength
, sizeof(double));
6823 memcpy(lineDash
, state
->lineDash
, lineDashLength
* sizeof(double));
6829 path
= state
->path
->copy();
6833 if (XYZ2DisplayTransformRelCol
) {
6834 XYZ2DisplayTransformRelCol
->ref();
6836 if (XYZ2DisplayTransformAbsCol
) {
6837 XYZ2DisplayTransformAbsCol
->ref();
6839 if (XYZ2DisplayTransformSat
) {
6840 XYZ2DisplayTransformSat
->ref();
6842 if (XYZ2DisplayTransformPerc
) {
6843 XYZ2DisplayTransformPerc
->ref();
6845 if (localDisplayProfile
) {
6846 displayProfileRef
++;
6852 void GfxState::setDisplayProfile(cmsHPROFILE localDisplayProfileA
) {
6853 if (localDisplayProfile
!= NULL
) {
6854 cmsCloseProfile(localDisplayProfile
);
6856 localDisplayProfile
= localDisplayProfileA
;
6857 if (localDisplayProfileA
!= NULL
) {
6858 cmsHTRANSFORM transform
;
6859 unsigned int nChannels
;
6860 unsigned int localDisplayPixelType
;
6862 localDisplayPixelType
= getCMSColorSpaceType(cmsGetColorSpace(localDisplayProfile
));
6863 nChannels
= getCMSNChannels(cmsGetColorSpace(localDisplayProfile
));
6864 displayProfileRef
= 1;
6865 // create transform from XYZ
6866 cmsHPROFILE XYZProfile
= cmsCreateXYZProfile();
6867 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
6868 localDisplayProfile
,
6869 COLORSPACE_SH(localDisplayPixelType
) |
6870 CHANNELS_SH(nChannels
) | BYTES_SH(1),
6871 INTENT_RELATIVE_COLORIMETRIC
,LCMS_FLAGS
)) == 0) {
6872 error(errSyntaxWarning
, -1, "Can't create Lab transform");
6874 XYZ2DisplayTransformRelCol
= new GfxColorTransform(transform
, INTENT_RELATIVE_COLORIMETRIC
, PT_XYZ
, localDisplayPixelType
);
6876 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
6877 localDisplayProfile
,
6878 COLORSPACE_SH(localDisplayPixelType
) |
6879 CHANNELS_SH(nChannels
) | BYTES_SH(1),
6880 INTENT_ABSOLUTE_COLORIMETRIC
,LCMS_FLAGS
)) == 0) {
6881 error(errSyntaxWarning
, -1, "Can't create Lab transform");
6883 XYZ2DisplayTransformAbsCol
= new GfxColorTransform(transform
, INTENT_ABSOLUTE_COLORIMETRIC
, PT_XYZ
, localDisplayPixelType
);
6885 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
6886 localDisplayProfile
,
6887 COLORSPACE_SH(localDisplayPixelType
) |
6888 CHANNELS_SH(nChannels
) | BYTES_SH(1),
6889 INTENT_SATURATION
,LCMS_FLAGS
)) == 0) {
6890 error(errSyntaxWarning
, -1, "Can't create Lab transform");
6892 XYZ2DisplayTransformSat
= new GfxColorTransform(transform
, INTENT_SATURATION
, PT_XYZ
, localDisplayPixelType
);
6894 if ((transform
= cmsCreateTransform(XYZProfile
, TYPE_XYZ_DBL
,
6895 localDisplayProfile
,
6896 COLORSPACE_SH(localDisplayPixelType
) |
6897 CHANNELS_SH(nChannels
) | BYTES_SH(1),
6898 INTENT_PERCEPTUAL
,LCMS_FLAGS
)) == 0) {
6899 error(errSyntaxWarning
, -1, "Can't create Lab transform");
6901 XYZ2DisplayTransformPerc
= new GfxColorTransform(transform
, INTENT_PERCEPTUAL
, PT_XYZ
, localDisplayPixelType
);
6903 cmsCloseProfile(XYZProfile
);
6907 GfxColorTransform
*GfxState::getXYZ2DisplayTransform() {
6908 GfxColorTransform
*transform
;
6910 transform
= XYZ2DisplayTransformRelCol
;
6911 if (strcmp(renderingIntent
, "AbsoluteColorimetric") == 0) {
6912 transform
= XYZ2DisplayTransformAbsCol
;
6913 } else if (strcmp(renderingIntent
, "Saturation") == 0) {
6914 transform
= XYZ2DisplayTransformSat
;
6915 } else if (strcmp(renderingIntent
, "Perceptual") == 0) {
6916 transform
= XYZ2DisplayTransformPerc
;
6918 if (transform
== NULL
) {
6919 transform
= XYZ2DisplayTransform
;
6926 void GfxState::setPath(GfxPath
*pathA
) {
6931 void GfxState::getUserClipBBox(double *xMin
, double *yMin
,
6932 double *xMax
, double *yMax
) {
6934 double xMin1
, yMin1
, xMax1
, yMax1
, det
, tx
, ty
;
6937 det
= 1 / (ctm
[0] * ctm
[3] - ctm
[1] * ctm
[2]);
6938 ictm
[0] = ctm
[3] * det
;
6939 ictm
[1] = -ctm
[1] * det
;
6940 ictm
[2] = -ctm
[2] * det
;
6941 ictm
[3] = ctm
[0] * det
;
6942 ictm
[4] = (ctm
[2] * ctm
[5] - ctm
[3] * ctm
[4]) * det
;
6943 ictm
[5] = (ctm
[1] * ctm
[4] - ctm
[0] * ctm
[5]) * det
;
6945 // transform all four corners of the clip bbox; find the min and max
6947 xMin1
= xMax1
= clipXMin
* ictm
[0] + clipYMin
* ictm
[2] + ictm
[4];
6948 yMin1
= yMax1
= clipXMin
* ictm
[1] + clipYMin
* ictm
[3] + ictm
[5];
6949 tx
= clipXMin
* ictm
[0] + clipYMax
* ictm
[2] + ictm
[4];
6950 ty
= clipXMin
* ictm
[1] + clipYMax
* ictm
[3] + ictm
[5];
6953 } else if (tx
> xMax1
) {
6958 } else if (ty
> yMax1
) {
6961 tx
= clipXMax
* ictm
[0] + clipYMin
* ictm
[2] + ictm
[4];
6962 ty
= clipXMax
* ictm
[1] + clipYMin
* ictm
[3] + ictm
[5];
6965 } else if (tx
> xMax1
) {
6970 } else if (ty
> yMax1
) {
6973 tx
= clipXMax
* ictm
[0] + clipYMax
* ictm
[2] + ictm
[4];
6974 ty
= clipXMax
* ictm
[1] + clipYMax
* ictm
[3] + ictm
[5];
6977 } else if (tx
> xMax1
) {
6982 } else if (ty
> yMax1
) {
6992 double GfxState::transformWidth(double w
) {
6995 x
= ctm
[0] + ctm
[2];
6996 y
= ctm
[1] + ctm
[3];
6997 return w
* sqrt(0.5 * (x
* x
+ y
* y
));
7000 double GfxState::getTransformedFontSize() {
7001 double x1
, y1
, x2
, y2
;
7003 x1
= textMat
[2] * fontSize
;
7004 y1
= textMat
[3] * fontSize
;
7005 x2
= ctm
[0] * x1
+ ctm
[2] * y1
;
7006 y2
= ctm
[1] * x1
+ ctm
[3] * y1
;
7007 return sqrt(x2
* x2
+ y2
* y2
);
7010 void GfxState::getFontTransMat(double *m11
, double *m12
,
7011 double *m21
, double *m22
) {
7012 *m11
= (textMat
[0] * ctm
[0] + textMat
[1] * ctm
[2]) * fontSize
;
7013 *m12
= (textMat
[0] * ctm
[1] + textMat
[1] * ctm
[3]) * fontSize
;
7014 *m21
= (textMat
[2] * ctm
[0] + textMat
[3] * ctm
[2]) * fontSize
;
7015 *m22
= (textMat
[2] * ctm
[1] + textMat
[3] * ctm
[3]) * fontSize
;
7018 void GfxState::setCTM(double a
, double b
, double c
,
7019 double d
, double e
, double f
) {
7028 void GfxState::concatCTM(double a
, double b
, double c
,
7029 double d
, double e
, double f
) {
7035 ctm
[0] = a
* a1
+ b
* c1
;
7036 ctm
[1] = a
* b1
+ b
* d1
;
7037 ctm
[2] = c
* a1
+ d
* c1
;
7038 ctm
[3] = c
* b1
+ d
* d1
;
7039 ctm
[4] = e
* a1
+ f
* c1
+ ctm
[4];
7040 ctm
[5] = e
* b1
+ f
* d1
+ ctm
[5];
7043 void GfxState::shiftCTMAndClip(double tx
, double ty
) {
7052 void GfxState::setFillColorSpace(GfxColorSpace
*colorSpace
) {
7053 if (fillColorSpace
) {
7054 delete fillColorSpace
;
7056 fillColorSpace
= colorSpace
;
7059 void GfxState::setStrokeColorSpace(GfxColorSpace
*colorSpace
) {
7060 if (strokeColorSpace
) {
7061 delete strokeColorSpace
;
7063 strokeColorSpace
= colorSpace
;
7066 void GfxState::setFillPattern(GfxPattern
*pattern
) {
7070 fillPattern
= pattern
;
7073 void GfxState::setStrokePattern(GfxPattern
*pattern
) {
7074 if (strokePattern
) {
7075 delete strokePattern
;
7077 strokePattern
= pattern
;
7080 void GfxState::setFont(GfxFont
*fontA
, double fontSizeA
) {
7085 fontSize
= fontSizeA
;
7088 void GfxState::setTransfer(Function
**funcs
) {
7091 for (i
= 0; i
< 4; ++i
) {
7095 transfer
[i
] = funcs
[i
];
7099 void GfxState::setLineDash(double *dash
, int length
, double start
) {
7103 lineDashLength
= length
;
7104 lineDashStart
= start
;
7107 void GfxState::clearPath() {
7109 path
= new GfxPath();
7112 void GfxState::clip() {
7113 double xMin
, yMin
, xMax
, yMax
, x
, y
;
7114 GfxSubpath
*subpath
;
7117 xMin
= xMax
= yMin
= yMax
= 0; // make gcc happy
7118 for (i
= 0; i
< path
->getNumSubpaths(); ++i
) {
7119 subpath
= path
->getSubpath(i
);
7120 for (j
= 0; j
< subpath
->getNumPoints(); ++j
) {
7121 transform(subpath
->getX(j
), subpath
->getY(j
), &x
, &y
);
7122 if (i
== 0 && j
== 0) {
7128 } else if (x
> xMax
) {
7133 } else if (y
> yMax
) {
7139 if (xMin
> clipXMin
) {
7142 if (yMin
> clipYMin
) {
7145 if (xMax
< clipXMax
) {
7148 if (yMax
< clipYMax
) {
7153 void GfxState::clipToStrokePath() {
7154 double xMin
, yMin
, xMax
, yMax
, x
, y
, t0
, t1
;
7155 GfxSubpath
*subpath
;
7158 xMin
= xMax
= yMin
= yMax
= 0; // make gcc happy
7159 for (i
= 0; i
< path
->getNumSubpaths(); ++i
) {
7160 subpath
= path
->getSubpath(i
);
7161 for (j
= 0; j
< subpath
->getNumPoints(); ++j
) {
7162 transform(subpath
->getX(j
), subpath
->getY(j
), &x
, &y
);
7163 if (i
== 0 && j
== 0) {
7169 } else if (x
> xMax
) {
7174 } else if (y
> yMax
) {
7181 // allow for the line width
7182 //~ miter joins can extend farther than this
7186 xMin
-= 0.5 * lineWidth
* t0
;
7187 xMax
+= 0.5 * lineWidth
* t0
;
7189 xMin
-= 0.5 * lineWidth
* t1
;
7190 xMax
+= 0.5 * lineWidth
* t1
;
7195 yMin
-= 0.5 * lineWidth
* t0
;
7196 yMax
+= 0.5 * lineWidth
* t0
;
7198 yMin
-= 0.5 * lineWidth
* t1
;
7199 yMax
+= 0.5 * lineWidth
* t1
;
7202 if (xMin
> clipXMin
) {
7205 if (yMin
> clipYMin
) {
7208 if (xMax
< clipXMax
) {
7211 if (yMax
< clipYMax
) {
7216 void GfxState::clipToRect(double xMin
, double yMin
, double xMax
, double yMax
) {
7217 double x
, y
, xMin1
, yMin1
, xMax1
, yMax1
;
7219 transform(xMin
, yMin
, &x
, &y
);
7222 transform(xMax
, yMin
, &x
, &y
);
7225 } else if (x
> xMax1
) {
7230 } else if (y
> yMax1
) {
7233 transform(xMax
, yMax
, &x
, &y
);
7236 } else if (x
> xMax1
) {
7241 } else if (y
> yMax1
) {
7244 transform(xMin
, yMax
, &x
, &y
);
7247 } else if (x
> xMax1
) {
7252 } else if (y
> yMax1
) {
7256 if (xMin1
> clipXMin
) {
7259 if (yMin1
> clipYMin
) {
7262 if (xMax1
< clipXMax
) {
7265 if (yMax1
< clipYMax
) {
7270 void GfxState::textShift(double tx
, double ty
) {
7273 textTransformDelta(tx
, ty
, &dx
, &dy
);
7278 void GfxState::shift(double dx
, double dy
) {
7283 GfxState
*GfxState::save() {
7287 newState
->saved
= this;
7291 GfxState
*GfxState::restore() {
7297 // these attributes aren't saved/restored by the q/Q operators
7298 oldState
->path
= path
;
7299 oldState
->curX
= curX
;
7300 oldState
->curY
= curY
;
7301 oldState
->lineX
= lineX
;
7302 oldState
->lineY
= lineY
;
7315 GBool
GfxState::parseBlendMode(Object
*obj
, GfxBlendMode
*mode
) {
7319 if (obj
->isName()) {
7320 for (i
= 0; i
< nGfxBlendModeNames
; ++i
) {
7321 if (!strcmp(obj
->getName(), gfxBlendModeNames
[i
].name
)) {
7322 *mode
= gfxBlendModeNames
[i
].mode
;
7327 } else if (obj
->isArray()) {
7328 for (i
= 0; i
< obj
->arrayGetLength(); ++i
) {
7329 obj
->arrayGet(i
, &obj2
);
7330 if (!obj2
.isName()) {
7334 for (j
= 0; j
< nGfxBlendModeNames
; ++j
) {
7335 if (!strcmp(obj2
.getName(), gfxBlendModeNames
[j
].name
)) {
7337 *mode
= gfxBlendModeNames
[j
].mode
;
7343 *mode
= gfxBlendNormal
;