| //======================================================================== |
| // |
| // GfxState.cc |
| // |
| // Copyright 1996-2003 Glyph & Cog, LLC |
| // |
| //======================================================================== |
| |
| //======================================================================== |
| // |
| // Modified under the Poppler project - http://poppler.freedesktop.org |
| // |
| // All changes made under the Poppler project to this file are licensed |
| // under GPL version 2 or later |
| // |
| // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com> |
| // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> |
| // Copyright (C) 2006, 2010 Carlos Garcia Campos <carlosgc@gnome.org> |
| // Copyright (C) 2006-2011 Albert Astals Cid <aacid@kde.org> |
| // Copyright (C) 2009 Koji Otani <sho@bbr.jp> |
| // Copyright (C) 2009, 2011 Thomas Freitag <Thomas.Freitag@alfa.de> |
| // Copyright (C) 2009 Christian Persch <chpe@gnome.org> |
| // Copyright (C) 2010 Paweł Wiejacha <pawel.wiejacha@gmail.com> |
| // Copyright (C) 2010 Christian Feuersänger <cfeuersaenger@googlemail.com> |
| // Copyright (C) 2011 Andrea Canciani <ranma42@gmail.com> |
| // |
| // To see a description of the changes please see the Changelog file that |
| // came with your tarball or type make ChangeLog if you are building from git |
| // |
| //======================================================================== |
| |
| #include <config.h> |
| |
| #ifdef USE_GCC_PRAGMAS |
| #pragma implementation |
| #endif |
| |
| #include <algorithm> |
| #include <stddef.h> |
| #include <math.h> |
| #include <string.h> |
| #include "goo/gmem.h" |
| #include "Error.h" |
| #include "Object.h" |
| #include "Array.h" |
| #include "Page.h" |
| #include "Gfx.h" |
| #include "GfxState.h" |
| #include "GfxState_helpers.h" |
| #include "GfxFont.h" |
| #include "GlobalParams.h" |
| #include "PopplerCache.h" |
| |
| //------------------------------------------------------------------------ |
| |
| GBool Matrix::invertTo(Matrix *other) const |
| { |
| double det; |
| |
| det = 1 / determinant(); |
| other->m[0] = m[3] * det; |
| other->m[1] = -m[1] * det; |
| other->m[2] = -m[2] * det; |
| other->m[3] = m[0] * det; |
| other->m[4] = (m[2] * m[5] - m[3] * m[4]) * det; |
| other->m[5] = (m[1] * m[4] - m[0] * m[5]) * det; |
| |
| return gTrue; |
| } |
| |
| void Matrix::transform(double x, double y, double *tx, double *ty) const |
| { |
| double temp_x, temp_y; |
| |
| temp_x = m[0] * x + m[2] * y + m[4]; |
| temp_y = m[1] * x + m[3] * y + m[5]; |
| |
| *tx = temp_x; |
| *ty = temp_y; |
| } |
| |
| // Matrix norm, taken from _cairo_matrix_transformed_circle_major_axis |
| double Matrix::norm() const |
| { |
| double f, g, h, i, j; |
| |
| i = m[0]*m[0] + m[1]*m[1]; |
| j = m[2]*m[2] + m[3]*m[3]; |
| |
| f = 0.5 * (i + j); |
| g = 0.5 * (i - j); |
| h = m[0]*m[2] + m[1]*m[3]; |
| |
| return sqrt (f + hypot (g, h)); |
| } |
| |
| //------------------------------------------------------------------------ |
| |
| struct GfxBlendModeInfo { |
| char *name; |
| GfxBlendMode mode; |
| }; |
| |
| static const GfxBlendModeInfo gfxBlendModeNames[] = { |
| { "Normal", gfxBlendNormal }, |
| { "Compatible", gfxBlendNormal }, |
| { "Multiply", gfxBlendMultiply }, |
| { "Screen", gfxBlendScreen }, |
| { "Overlay", gfxBlendOverlay }, |
| { "Darken", gfxBlendDarken }, |
| { "Lighten", gfxBlendLighten }, |
| { "ColorDodge", gfxBlendColorDodge }, |
| { "ColorBurn", gfxBlendColorBurn }, |
| { "HardLight", gfxBlendHardLight }, |
| { "SoftLight", gfxBlendSoftLight }, |
| { "Difference", gfxBlendDifference }, |
| { "Exclusion", gfxBlendExclusion }, |
| { "Hue", gfxBlendHue }, |
| { "Saturation", gfxBlendSaturation }, |
| { "Color", gfxBlendColor }, |
| { "Luminosity", gfxBlendLuminosity } |
| }; |
| |
| #define nGfxBlendModeNames \ |
| ((int)((sizeof(gfxBlendModeNames) / sizeof(GfxBlendModeInfo)))) |
| |
| //------------------------------------------------------------------------ |
| // |
| // NB: This must match the GfxColorSpaceMode enum defined in |
| // GfxState.h |
| static char *gfxColorSpaceModeNames[] = { |
| "DeviceGray", |
| "CalGray", |
| "DeviceRGB", |
| "CalRGB", |
| "DeviceCMYK", |
| "Lab", |
| "ICCBased", |
| "Indexed", |
| "Separation", |
| "DeviceN", |
| "Pattern" |
| }; |
| |
| #define nGfxColorSpaceModes ((sizeof(gfxColorSpaceModeNames) / sizeof(char *))) |
| |
| #ifdef USE_CMS |
| |
| #include <lcms.h> |
| |
| #define COLOR_PROFILE_DIR "/ColorProfiles/" |
| #define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR |
| |
| void GfxColorTransform::doTransform(void *in, void *out, unsigned int size) { |
| cmsDoTransform(transform, in, out, size); |
| } |
| |
| // transformA should be a cmsHTRANSFORM |
| GfxColorTransform::GfxColorTransform(void *transformA) { |
| transform = transformA; |
| refCount = 1; |
| } |
| |
| GfxColorTransform::~GfxColorTransform() { |
| cmsDeleteTransform(transform); |
| } |
| |
| void GfxColorTransform::ref() { |
| refCount++; |
| } |
| |
| unsigned int GfxColorTransform::unref() { |
| return --refCount; |
| } |
| |
| static cmsHPROFILE RGBProfile = NULL; |
| static GooString *displayProfileName = NULL; // display profile file Name |
| static cmsHPROFILE displayProfile = NULL; // display profile |
| static unsigned int displayPixelType = 0; |
| static GfxColorTransform *XYZ2DisplayTransform = NULL; |
| |
| // convert color space signature to cmsColor type |
| static unsigned int getCMSColorSpaceType(icColorSpaceSignature cs); |
| static unsigned int getCMSNChannels(icColorSpaceSignature cs); |
| static cmsHPROFILE loadColorProfile(const char *fileName); |
| |
| void GfxColorSpace::setDisplayProfile(void *displayProfileA) { |
| displayProfile = displayProfileA; |
| } |
| |
| void GfxColorSpace::setDisplayProfileName(GooString *name) { |
| displayProfileName = name->copy(); |
| } |
| |
| cmsHPROFILE GfxColorSpace::getRGBProfile() { |
| return RGBProfile; |
| } |
| |
| cmsHPROFILE GfxColorSpace::getDisplayProfile() { |
| return displayProfile; |
| } |
| |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // GfxColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxColorSpace::GfxColorSpace() { |
| } |
| |
| GfxColorSpace::~GfxColorSpace() { |
| } |
| |
| GfxColorSpace *GfxColorSpace::parse(Object *csObj, Gfx *gfx) { |
| GfxColorSpace *cs; |
| Object obj1; |
| |
| cs = NULL; |
| if (csObj->isName()) { |
| if (csObj->isName("DeviceGray") || csObj->isName("G")) { |
| cs = new GfxDeviceGrayColorSpace(); |
| } else if (csObj->isName("DeviceRGB") || csObj->isName("RGB")) { |
| cs = new GfxDeviceRGBColorSpace(); |
| } else if (csObj->isName("DeviceCMYK") || csObj->isName("CMYK")) { |
| cs = new GfxDeviceCMYKColorSpace(); |
| } else if (csObj->isName("Pattern")) { |
| cs = new GfxPatternColorSpace(NULL); |
| } else { |
| error(-1, "Bad color space '%s'", csObj->getName()); |
| } |
| } else if (csObj->isArray()) { |
| csObj->arrayGet(0, &obj1); |
| if (obj1.isName("DeviceGray") || obj1.isName("G")) { |
| cs = new GfxDeviceGrayColorSpace(); |
| } else if (obj1.isName("DeviceRGB") || obj1.isName("RGB")) { |
| cs = new GfxDeviceRGBColorSpace(); |
| } else if (obj1.isName("DeviceCMYK") || obj1.isName("CMYK")) { |
| cs = new GfxDeviceCMYKColorSpace(); |
| } else if (obj1.isName("CalGray")) { |
| cs = GfxCalGrayColorSpace::parse(csObj->getArray()); |
| } else if (obj1.isName("CalRGB")) { |
| cs = GfxCalRGBColorSpace::parse(csObj->getArray()); |
| } else if (obj1.isName("Lab")) { |
| cs = GfxLabColorSpace::parse(csObj->getArray()); |
| } else if (obj1.isName("ICCBased")) { |
| cs = GfxICCBasedColorSpace::parse(csObj->getArray(), gfx); |
| } else if (obj1.isName("Indexed") || obj1.isName("I")) { |
| cs = GfxIndexedColorSpace::parse(csObj->getArray(), gfx); |
| } else if (obj1.isName("Separation")) { |
| cs = GfxSeparationColorSpace::parse(csObj->getArray(), gfx); |
| } else if (obj1.isName("DeviceN")) { |
| cs = GfxDeviceNColorSpace::parse(csObj->getArray(), gfx); |
| } else if (obj1.isName("Pattern")) { |
| cs = GfxPatternColorSpace::parse(csObj->getArray(), gfx); |
| } else { |
| error(-1, "Bad color space"); |
| } |
| obj1.free(); |
| } else if (csObj->isDict()) { |
| csObj->dictLookup("ColorSpace", &obj1); |
| if (obj1.isName("DeviceGray")) { |
| cs = new GfxDeviceGrayColorSpace(); |
| } else if (obj1.isName("DeviceRGB")) { |
| cs = new GfxDeviceRGBColorSpace(); |
| } else if (obj1.isName("DeviceCMYK")) { |
| cs = new GfxDeviceCMYKColorSpace(); |
| } else { |
| error(-1, "Bad color space '%s'", csObj->getName()); |
| } |
| obj1.free(); |
| } else { |
| error(-1, "Bad color space - expected name, array or dict"); |
| } |
| return cs; |
| } |
| |
| void GfxColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, |
| int maxImgPixel) { |
| int i; |
| |
| for (i = 0; i < getNComps(); ++i) { |
| decodeLow[i] = 0; |
| decodeRange[i] = 1; |
| } |
| } |
| |
| int GfxColorSpace::getNumColorSpaceModes() { |
| return nGfxColorSpaceModes; |
| } |
| |
| char *GfxColorSpace::getColorSpaceModeName(int idx) { |
| return gfxColorSpaceModeNames[idx]; |
| } |
| |
| #ifdef USE_CMS |
| cmsHPROFILE loadColorProfile(const char *fileName) |
| { |
| cmsHPROFILE hp = NULL; |
| FILE *fp; |
| |
| if (fileName[0] == '/') { |
| // full path |
| // check if open the file |
| if ((fp = fopen(fileName,"r")) != NULL) { |
| fclose(fp); |
| hp = cmsOpenProfileFromFile(fileName,"r"); |
| } |
| return hp; |
| } |
| // try to load from user directory |
| GooString *path = globalParams->getBaseDir(); |
| path->append(COLOR_PROFILE_DIR); |
| path->append(fileName); |
| // check if open the file |
| if ((fp = fopen(path->getCString(),"r")) != NULL) { |
| fclose(fp); |
| hp = cmsOpenProfileFromFile(path->getCString(),"r"); |
| } |
| delete path; |
| if (hp == NULL) { |
| // load from global directory |
| path = new GooString(GLOBAL_COLOR_PROFILE_DIR); |
| path->append(fileName); |
| // check if open the file |
| if ((fp = fopen(path->getCString(),"r")) != NULL) { |
| fclose(fp); |
| hp = cmsOpenProfileFromFile(path->getCString(),"r"); |
| } |
| delete path; |
| } |
| return hp; |
| } |
| |
| static int CMSError(int ecode, const char *msg) |
| { |
| error(-1, "%s", msg); |
| return 1; |
| } |
| |
| int GfxColorSpace::setupColorProfiles() |
| { |
| static GBool initialized = gFalse; |
| cmsHTRANSFORM transform; |
| unsigned int nChannels; |
| |
| // do only once |
| if (initialized) return 0; |
| initialized = gTrue; |
| |
| // set error handlor |
| cmsSetErrorHandler(CMSError); |
| |
| if (displayProfile == NULL) { |
| // load display profile if it was not already loaded. |
| if (displayProfileName == NULL) { |
| displayProfile = loadColorProfile("display.icc"); |
| } else if (displayProfileName->getLength() > 0) { |
| displayProfile = loadColorProfile(displayProfileName->getCString()); |
| } |
| } |
| // load RGB profile |
| RGBProfile = loadColorProfile("RGB.icc"); |
| if (RGBProfile == NULL) { |
| /* use built in sRGB profile */ |
| RGBProfile = cmsCreate_sRGBProfile(); |
| } |
| // create transforms |
| if (displayProfile != NULL) { |
| displayPixelType = getCMSColorSpaceType(cmsGetColorSpace(displayProfile)); |
| nChannels = getCMSNChannels(cmsGetColorSpace(displayProfile)); |
| // create transform from XYZ |
| cmsHPROFILE XYZProfile = cmsCreateXYZProfile(); |
| if ((transform = cmsCreateTransform(XYZProfile, TYPE_XYZ_DBL, |
| displayProfile, |
| COLORSPACE_SH(displayPixelType) | |
| CHANNELS_SH(nChannels) | BYTES_SH(1), |
| INTENT_RELATIVE_COLORIMETRIC,0)) == 0) { |
| error(-1, "Can't create Lab transform"); |
| } else { |
| XYZ2DisplayTransform = new GfxColorTransform(transform); |
| } |
| cmsCloseProfile(XYZProfile); |
| } |
| return 0; |
| } |
| |
| unsigned int getCMSColorSpaceType(icColorSpaceSignature cs) |
| { |
| switch (cs) { |
| case icSigXYZData: |
| return PT_XYZ; |
| break; |
| case icSigLabData: |
| return PT_Lab; |
| break; |
| case icSigLuvData: |
| return PT_YUV; |
| break; |
| case icSigYCbCrData: |
| return PT_YCbCr; |
| break; |
| case icSigYxyData: |
| return PT_Yxy; |
| break; |
| case icSigRgbData: |
| return PT_RGB; |
| break; |
| case icSigGrayData: |
| return PT_GRAY; |
| break; |
| case icSigHsvData: |
| return PT_HSV; |
| break; |
| case icSigHlsData: |
| return PT_HLS; |
| break; |
| case icSigCmykData: |
| return PT_CMYK; |
| break; |
| case icSigCmyData: |
| return PT_CMY; |
| break; |
| case icSig2colorData: |
| case icSig3colorData: |
| case icSig4colorData: |
| case icSig5colorData: |
| case icSig6colorData: |
| case icSig7colorData: |
| case icSig8colorData: |
| case icSig9colorData: |
| case icSig10colorData: |
| case icSig11colorData: |
| case icSig12colorData: |
| case icSig13colorData: |
| case icSig14colorData: |
| case icSig15colorData: |
| default: |
| break; |
| } |
| return PT_RGB; |
| } |
| |
| unsigned int getCMSNChannels(icColorSpaceSignature cs) |
| { |
| switch (cs) { |
| case icSigXYZData: |
| case icSigLuvData: |
| case icSigLabData: |
| case icSigYCbCrData: |
| case icSigYxyData: |
| case icSigRgbData: |
| case icSigHsvData: |
| case icSigHlsData: |
| case icSigCmyData: |
| case icSig3colorData: |
| return 3; |
| break; |
| case icSigGrayData: |
| return 1; |
| break; |
| case icSigCmykData: |
| case icSig4colorData: |
| return 4; |
| break; |
| case icSig2colorData: |
| return 2; |
| break; |
| case icSig5colorData: |
| return 5; |
| break; |
| case icSig6colorData: |
| return 6; |
| break; |
| case icSig7colorData: |
| return 7; |
| break; |
| case icSig8colorData: |
| return 8; |
| break; |
| case icSig9colorData: |
| return 9; |
| break; |
| case icSig10colorData: |
| return 10; |
| break; |
| case icSig11colorData: |
| return 11; |
| break; |
| case icSig12colorData: |
| return 12; |
| break; |
| case icSig13colorData: |
| return 13; |
| break; |
| case icSig14colorData: |
| return 14; |
| break; |
| case icSig15colorData: |
| return 15; |
| default: |
| break; |
| } |
| return 3; |
| } |
| |
| #endif |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceGrayColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceGrayColorSpace::GfxDeviceGrayColorSpace() { |
| } |
| |
| GfxDeviceGrayColorSpace::~GfxDeviceGrayColorSpace() { |
| } |
| |
| GfxColorSpace *GfxDeviceGrayColorSpace::copy() { |
| return new GfxDeviceGrayColorSpace(); |
| } |
| |
| void GfxDeviceGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| *gray = clip01(color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) { |
| memcpy (out, in, length); |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| rgb->r = rgb->g = rgb->b = clip01(color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, unsigned int *out, |
| int length) { |
| int i; |
| |
| for (i = 0; i < length; i++) |
| out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0); |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) { |
| for (int i = 0; i < length; i++) { |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = in[i]; |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) { |
| for (int i = 0; i < length; i++) { |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = in[i]; |
| *out++ = 255; |
| } |
| } |
| |
| void GfxDeviceGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| cmyk->c = cmyk->m = cmyk->y = 0; |
| cmyk->k = clip01(gfxColorComp1 - color->c[0]); |
| } |
| |
| void GfxDeviceGrayColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxCalGrayColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxCalGrayColorSpace::GfxCalGrayColorSpace() { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| gamma = 1; |
| } |
| |
| GfxCalGrayColorSpace::~GfxCalGrayColorSpace() { |
| } |
| |
| GfxColorSpace *GfxCalGrayColorSpace::copy() { |
| GfxCalGrayColorSpace *cs; |
| |
| cs = new GfxCalGrayColorSpace(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->gamma = gamma; |
| return cs; |
| } |
| |
| // This is the inverse of MatrixLMN in Example 4.10 from the PostScript |
| // Language Reference, Third Edition. |
| static const double xyzrgb[3][3] = { |
| { 3.240449, -1.537136, -0.498531 }, |
| { -0.969265, 1.876011, 0.041556 }, |
| { 0.055643, -0.204026, 1.057229 } |
| }; |
| |
| GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) { |
| GfxCalGrayColorSpace *cs; |
| Object obj1, obj2, obj3; |
| |
| arr->get(1, &obj1); |
| if (!obj1.isDict()) { |
| error(-1, "Bad CalGray color space"); |
| obj1.free(); |
| return NULL; |
| } |
| cs = new GfxCalGrayColorSpace(); |
| if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("Gamma", &obj2)->isNum()) { |
| cs->gamma = obj2.getNum(); |
| } |
| obj2.free(); |
| obj1.free(); |
| |
| cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX + |
| xyzrgb[0][1] * cs->whiteY + |
| xyzrgb[0][2] * cs->whiteZ); |
| cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX + |
| xyzrgb[1][1] * cs->whiteY + |
| xyzrgb[1][2] * cs->whiteZ); |
| cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX + |
| xyzrgb[2][1] * cs->whiteY + |
| xyzrgb[2][2] * cs->whiteZ); |
| |
| return cs; |
| } |
| |
| // convert CalGray to media XYZ color space |
| // (not multiply by the white point) |
| void GfxCalGrayColorSpace::getXYZ(GfxColor *color, |
| double *pX, double *pY, double *pZ) { |
| const double A = colToDbl(color->c[0]); |
| const double xyzColor = pow(A,gamma); |
| *pX = xyzColor; |
| *pY = xyzColor; |
| *pZ = xyzColor; |
| } |
| |
| void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color,&X,&Y,&Z); |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + |
| 0.587 * rgb.g + |
| 0.114 * rgb.b + 0.5)); |
| } |
| |
| void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double X, Y, Z; |
| double r, g, b; |
| |
| getXYZ(color,&X,&Y,&Z); |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| return; |
| } |
| #endif |
| X *= whiteX; |
| Y *= whiteY; |
| Z *= whiteZ; |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(sqrt(clip01(r * kr))); |
| rgb->g = dblToCol(sqrt(clip01(g * kg))); |
| rgb->b = dblToCol(sqrt(clip01(b * kb))); |
| } |
| |
| void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color,&X,&Y,&Z); |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxCalGrayColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceRGBColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceRGBColorSpace::GfxDeviceRGBColorSpace() { |
| } |
| |
| GfxDeviceRGBColorSpace::~GfxDeviceRGBColorSpace() { |
| } |
| |
| GfxColorSpace *GfxDeviceRGBColorSpace::copy() { |
| return new GfxDeviceRGBColorSpace(); |
| } |
| |
| void GfxDeviceRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| *gray = clip01((GfxColorComp)(0.3 * color->c[0] + |
| 0.59 * color->c[1] + |
| 0.11 * color->c[2] + 0.5)); |
| } |
| |
| void GfxDeviceRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) { |
| int i; |
| |
| for (i = 0; i < length; i++) { |
| out[i] = |
| (in[i * 3 + 0] * 19595 + |
| in[i * 3 + 1] * 38469 + |
| in[i * 3 + 2] * 7472) / 65536; |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| rgb->r = clip01(color->c[0]); |
| rgb->g = clip01(color->c[1]); |
| rgb->b = clip01(color->c[2]); |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out, |
| int length) { |
| Guchar *p; |
| int i; |
| |
| for (i = 0, p = in; i < length; i++, p += 3) |
| out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0); |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) { |
| for (int i = 0; i < length; i++) { |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| } |
| } |
| |
| void GfxDeviceRGBColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) { |
| for (int i = 0; i < length; i++) { |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = *in++; |
| *out++ = 255; |
| } |
| } |
| void GfxDeviceRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| GfxColorComp c, m, y, k; |
| |
| c = clip01(gfxColorComp1 - color->c[0]); |
| m = clip01(gfxColorComp1 - color->c[1]); |
| y = clip01(gfxColorComp1 - color->c[2]); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxDeviceRGBColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxCalRGBColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxCalRGBColorSpace::GfxCalRGBColorSpace() { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| gammaR = gammaG = gammaB = 1; |
| mat[0] = 1; mat[1] = 0; mat[2] = 0; |
| mat[3] = 0; mat[4] = 1; mat[5] = 0; |
| mat[6] = 0; mat[7] = 0; mat[8] = 1; |
| } |
| |
| GfxCalRGBColorSpace::~GfxCalRGBColorSpace() { |
| } |
| |
| GfxColorSpace *GfxCalRGBColorSpace::copy() { |
| GfxCalRGBColorSpace *cs; |
| int i; |
| |
| cs = new GfxCalRGBColorSpace(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->gammaR = gammaR; |
| cs->gammaG = gammaG; |
| cs->gammaB = gammaB; |
| for (i = 0; i < 9; ++i) { |
| cs->mat[i] = mat[i]; |
| } |
| return cs; |
| } |
| |
| GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) { |
| GfxCalRGBColorSpace *cs; |
| Object obj1, obj2, obj3; |
| int i; |
| |
| arr->get(1, &obj1); |
| if (!obj1.isDict()) { |
| error(-1, "Bad CalRGB color space"); |
| obj1.free(); |
| return NULL; |
| } |
| cs = new GfxCalRGBColorSpace(); |
| if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| if (likely(obj3.isNum())) |
| cs->whiteZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| if (likely(obj3.isNum())) |
| cs->blackZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("Gamma", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| if (likely(obj3.isNum())) |
| cs->gammaR = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| if (likely(obj3.isNum())) |
| cs->gammaG = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| if (likely(obj3.isNum())) |
| cs->gammaB = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("Matrix", &obj2)->isArray() && |
| obj2.arrayGetLength() == 9) { |
| for (i = 0; i < 9; ++i) { |
| obj2.arrayGet(i, &obj3); |
| if (likely(obj3.isNum())) |
| cs->mat[i] = obj3.getNum(); |
| obj3.free(); |
| } |
| } |
| obj2.free(); |
| obj1.free(); |
| |
| cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX + |
| xyzrgb[0][1] * cs->whiteY + |
| xyzrgb[0][2] * cs->whiteZ); |
| cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX + |
| xyzrgb[1][1] * cs->whiteY + |
| xyzrgb[1][2] * cs->whiteZ); |
| cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX + |
| xyzrgb[2][1] * cs->whiteY + |
| xyzrgb[2][2] * cs->whiteZ); |
| |
| return cs; |
| } |
| |
| // convert CalRGB to XYZ color space |
| void GfxCalRGBColorSpace::getXYZ(GfxColor *color, |
| double *pX, double *pY, double *pZ) { |
| double A, B, C; |
| |
| A = pow(colToDbl(color->c[0]), gammaR); |
| B = pow(colToDbl(color->c[1]), gammaG); |
| C = pow(colToDbl(color->c[2]), gammaB); |
| *pX = mat[0] * A + mat[3] * B + mat[6] * C; |
| *pY = mat[1] * A + mat[4] * B + mat[7] * C; |
| *pZ = mat[2] * A + mat[5] * B + mat[8] * C; |
| } |
| |
| void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color,&X,&Y,&Z); |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + |
| 0.587 * rgb.g + |
| 0.114 * rgb.b + 0.5)); |
| } |
| |
| void GfxCalRGBColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double X, Y, Z; |
| double r, g, b; |
| |
| getXYZ(color,&X,&Y,&Z); |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| in[0] = clip01(X/whiteX); |
| in[1] = clip01(Y/whiteY); |
| in[2] = clip01(Z/whiteZ); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| return; |
| } |
| #endif |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(sqrt(clip01(r))); |
| rgb->g = dblToCol(sqrt(clip01(g))); |
| rgb->b = dblToCol(sqrt(clip01(b))); |
| } |
| |
| void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| double X, Y, Z; |
| |
| getXYZ(color,&X,&Y,&Z); |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxCalRGBColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceCMYKColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceCMYKColorSpace::GfxDeviceCMYKColorSpace() { |
| } |
| |
| GfxDeviceCMYKColorSpace::~GfxDeviceCMYKColorSpace() { |
| } |
| |
| GfxColorSpace *GfxDeviceCMYKColorSpace::copy() { |
| return new GfxDeviceCMYKColorSpace(); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| *gray = clip01((GfxColorComp)(gfxColorComp1 - color->c[3] |
| - 0.3 * color->c[0] |
| - 0.59 * color->c[1] |
| - 0.11 * color->c[2] + 0.5)); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double c, m, y, k, c1, m1, y1, k1, r, g, b; |
| |
| c = colToDbl(color->c[0]); |
| m = colToDbl(color->c[1]); |
| y = colToDbl(color->c[2]); |
| k = colToDbl(color->c[3]); |
| c1 = 1 - c; |
| m1 = 1 - m; |
| y1 = 1 - y; |
| k1 = 1 - k; |
| cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
| rgb->r = clip01(dblToCol(r)); |
| rgb->g = clip01(dblToCol(g)); |
| rgb->b = clip01(dblToCol(b)); |
| } |
| |
| static inline void GfxDeviceCMYKColorSpacegetRGBLineHelper(Guchar *&in, double &r, double &g, double &b) |
| { |
| double c, m, y, k, c1, m1, y1, k1; |
| |
| c = byteToDbl(*in++); |
| m = byteToDbl(*in++); |
| y = byteToDbl(*in++); |
| k = byteToDbl(*in++); |
| c1 = 1 - c; |
| m1 = 1 - m; |
| y1 = 1 - y; |
| k1 = 1 - k; |
| cmykToRGBMatrixMultiplication(c, m, y, k, c1, m1, y1, k1, r, g, b); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) |
| { |
| double r, g, b; |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = (dblToByte(clip01(r)) << 16) | (dblToByte(clip01(g)) << 8) | dblToByte(clip01(b)); |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) |
| { |
| double r, g, b; |
| |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = dblToByte(clip01(r)); |
| *out++ = dblToByte(clip01(g)); |
| *out++ = dblToByte(clip01(b)); |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) |
| { |
| double r, g, b; |
| |
| for (int i = 0; i < length; i++) { |
| GfxDeviceCMYKColorSpacegetRGBLineHelper(in, r, g, b); |
| *out++ = dblToByte(clip01(r)); |
| *out++ = dblToByte(clip01(g)); |
| *out++ = dblToByte(clip01(b)); |
| *out++ = 255; |
| } |
| } |
| |
| void GfxDeviceCMYKColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| cmyk->c = clip01(color->c[0]); |
| cmyk->m = clip01(color->c[1]); |
| cmyk->y = clip01(color->c[2]); |
| cmyk->k = clip01(color->c[3]); |
| } |
| |
| void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| color->c[1] = 0; |
| color->c[2] = 0; |
| color->c[3] = gfxColorComp1; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxLabColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxLabColorSpace::GfxLabColorSpace() { |
| whiteX = whiteY = whiteZ = 1; |
| blackX = blackY = blackZ = 0; |
| aMin = bMin = -100; |
| aMax = bMax = 100; |
| } |
| |
| GfxLabColorSpace::~GfxLabColorSpace() { |
| } |
| |
| GfxColorSpace *GfxLabColorSpace::copy() { |
| GfxLabColorSpace *cs; |
| |
| cs = new GfxLabColorSpace(); |
| cs->whiteX = whiteX; |
| cs->whiteY = whiteY; |
| cs->whiteZ = whiteZ; |
| cs->blackX = blackX; |
| cs->blackY = blackY; |
| cs->blackZ = blackZ; |
| cs->aMin = aMin; |
| cs->aMax = aMax; |
| cs->bMin = bMin; |
| cs->bMax = bMax; |
| cs->kr = kr; |
| cs->kg = kg; |
| cs->kb = kb; |
| return cs; |
| } |
| |
| GfxColorSpace *GfxLabColorSpace::parse(Array *arr) { |
| GfxLabColorSpace *cs; |
| Object obj1, obj2, obj3; |
| |
| arr->get(1, &obj1); |
| if (!obj1.isDict()) { |
| error(-1, "Bad Lab color space"); |
| obj1.free(); |
| return NULL; |
| } |
| cs = new GfxLabColorSpace(); |
| if (obj1.dictLookup("WhitePoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| cs->whiteX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| cs->whiteY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| cs->whiteZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("BlackPoint", &obj2)->isArray() && |
| obj2.arrayGetLength() == 3) { |
| obj2.arrayGet(0, &obj3); |
| cs->blackX = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| cs->blackY = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| cs->blackZ = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| if (obj1.dictLookup("Range", &obj2)->isArray() && |
| obj2.arrayGetLength() == 4) { |
| obj2.arrayGet(0, &obj3); |
| cs->aMin = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(1, &obj3); |
| cs->aMax = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(2, &obj3); |
| cs->bMin = obj3.getNum(); |
| obj3.free(); |
| obj2.arrayGet(3, &obj3); |
| cs->bMax = obj3.getNum(); |
| obj3.free(); |
| } |
| obj2.free(); |
| obj1.free(); |
| |
| cs->kr = 1 / (xyzrgb[0][0] * cs->whiteX + |
| xyzrgb[0][1] * cs->whiteY + |
| xyzrgb[0][2] * cs->whiteZ); |
| cs->kg = 1 / (xyzrgb[1][0] * cs->whiteX + |
| xyzrgb[1][1] * cs->whiteY + |
| xyzrgb[1][2] * cs->whiteZ); |
| cs->kb = 1 / (xyzrgb[2][0] * cs->whiteX + |
| xyzrgb[2][1] * cs->whiteY + |
| xyzrgb[2][2] * cs->whiteZ); |
| |
| return cs; |
| } |
| |
| void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| GfxRGB rgb; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| getXYZ(color, &in[0], &in[1], &in[2]); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| *gray = byteToCol(out[0]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| *gray = clip01((GfxColorComp)(0.299 * rgb.r + |
| 0.587 * rgb.g + |
| 0.114 * rgb.b + 0.5)); |
| } |
| |
| // convert L*a*b* to media XYZ color space |
| // (not multiply by the white point) |
| void GfxLabColorSpace::getXYZ(GfxColor *color, |
| double *pX, double *pY, double *pZ) { |
| double X, Y, Z; |
| double t1, t2; |
| |
| t1 = (colToDbl(color->c[0]) + 16) / 116; |
| t2 = t1 + colToDbl(color->c[1]) / 500; |
| if (t2 >= (6.0 / 29.0)) { |
| X = t2 * t2 * t2; |
| } else { |
| X = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); |
| } |
| if (t1 >= (6.0 / 29.0)) { |
| Y = t1 * t1 * t1; |
| } else { |
| Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0)); |
| } |
| t2 = t1 - colToDbl(color->c[2]) / 200; |
| if (t2 >= (6.0 / 29.0)) { |
| Z = t2 * t2 * t2; |
| } else { |
| Z = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); |
| } |
| *pX = X; |
| *pY = Y; |
| *pZ = Z; |
| } |
| |
| void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double X, Y, Z; |
| double r, g, b; |
| |
| getXYZ(color, &X, &Y, &Z); |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) { |
| Guchar out[gfxColorMaxComps]; |
| double in[gfxColorMaxComps]; |
| |
| in[0] = clip01(X); |
| in[1] = clip01(Y); |
| in[2] = clip01(Z); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| return; |
| } |
| #endif |
| X *= whiteX; |
| Y *= whiteY; |
| Z *= whiteZ; |
| // convert XYZ to RGB, including gamut mapping and gamma correction |
| r = xyzrgb[0][0] * X + xyzrgb[0][1] * Y + xyzrgb[0][2] * Z; |
| g = xyzrgb[1][0] * X + xyzrgb[1][1] * Y + xyzrgb[1][2] * Z; |
| b = xyzrgb[2][0] * X + xyzrgb[2][1] * Y + xyzrgb[2][2] * Z; |
| rgb->r = dblToCol(sqrt(clip01(r * kr))); |
| rgb->g = dblToCol(sqrt(clip01(g * kg))); |
| rgb->b = dblToCol(sqrt(clip01(b * kb))); |
| } |
| |
| void GfxLabColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| #ifdef USE_CMS |
| if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) { |
| double in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| |
| getXYZ(color, &in[0], &in[1], &in[2]); |
| XYZ2DisplayTransform->doTransform(in,out,1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| return; |
| } |
| #endif |
| getRGB(color, &rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| |
| void GfxLabColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| if (aMin > 0) { |
| color->c[1] = dblToCol(aMin); |
| } else if (aMax < 0) { |
| color->c[1] = dblToCol(aMax); |
| } else { |
| color->c[1] = 0; |
| } |
| if (bMin > 0) { |
| color->c[2] = dblToCol(bMin); |
| } else if (bMax < 0) { |
| color->c[2] = dblToCol(bMax); |
| } else { |
| color->c[2] = 0; |
| } |
| } |
| |
| void GfxLabColorSpace::getDefaultRanges(double *decodeLow, double *decodeRange, |
| int maxImgPixel) { |
| decodeLow[0] = 0; |
| decodeRange[0] = 100; |
| decodeLow[1] = aMin; |
| decodeRange[1] = aMax - aMin; |
| decodeLow[2] = bMin; |
| decodeRange[2] = bMax - bMin; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxICCBasedColorSpace |
| //------------------------------------------------------------------------ |
| |
| class GfxICCBasedColorSpaceKey : public PopplerCacheKey |
| { |
| public: |
| GfxICCBasedColorSpaceKey(int numA, int genA) : num(numA), gen(genA) |
| { |
| } |
| |
| bool operator==(const PopplerCacheKey &key) const |
| { |
| const GfxICCBasedColorSpaceKey *k = static_cast<const GfxICCBasedColorSpaceKey*>(&key); |
| return k->num == num && k->gen == gen; |
| } |
| |
| int num, gen; |
| }; |
| |
| class GfxICCBasedColorSpaceItem : public PopplerCacheItem |
| { |
| public: |
| GfxICCBasedColorSpaceItem(GfxICCBasedColorSpace *csA) |
| { |
| cs = static_cast<GfxICCBasedColorSpace*>(csA->copy()); |
| } |
| |
| ~GfxICCBasedColorSpaceItem() |
| { |
| delete cs; |
| } |
| |
| GfxICCBasedColorSpace *cs; |
| }; |
| |
| GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *altA, |
| Ref *iccProfileStreamA) { |
| nComps = nCompsA; |
| alt = altA; |
| iccProfileStream = *iccProfileStreamA; |
| rangeMin[0] = rangeMin[1] = rangeMin[2] = rangeMin[3] = 0; |
| rangeMax[0] = rangeMax[1] = rangeMax[2] = rangeMax[3] = 1; |
| #ifdef USE_CMS |
| transform = NULL; |
| lineTransform = NULL; |
| #endif |
| } |
| |
| GfxICCBasedColorSpace::~GfxICCBasedColorSpace() { |
| delete alt; |
| #ifdef USE_CMS |
| if (transform != NULL) { |
| if (transform->unref() == 0) delete transform; |
| } |
| if (lineTransform != NULL) { |
| if (lineTransform->unref() == 0) delete lineTransform; |
| } |
| #endif |
| } |
| |
| GfxColorSpace *GfxICCBasedColorSpace::copy() { |
| GfxICCBasedColorSpace *cs; |
| int i; |
| |
| cs = new GfxICCBasedColorSpace(nComps, alt->copy(), &iccProfileStream); |
| for (i = 0; i < 4; ++i) { |
| cs->rangeMin[i] = rangeMin[i]; |
| cs->rangeMax[i] = rangeMax[i]; |
| } |
| #ifdef USE_CMS |
| cs->transform = transform; |
| if (transform != NULL) transform->ref(); |
| cs->lineTransform = lineTransform; |
| if (lineTransform != NULL) lineTransform->ref(); |
| #endif |
| return cs; |
| } |
| |
| GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr, Gfx *gfx) { |
| GfxICCBasedColorSpace *cs; |
| Ref iccProfileStreamA; |
| int nCompsA; |
| GfxColorSpace *altA; |
| Dict *dict; |
| Object obj1, obj2, obj3; |
| int i; |
| |
| arr->getNF(1, &obj1); |
| if (obj1.isRef()) { |
| iccProfileStreamA = obj1.getRef(); |
| } else { |
| iccProfileStreamA.num = 0; |
| iccProfileStreamA.gen = 0; |
| } |
| obj1.free(); |
| #ifdef USE_CMS |
| // check cache |
| if (gfx && iccProfileStreamA.num > 0) { |
| GfxICCBasedColorSpaceKey k(iccProfileStreamA.num, iccProfileStreamA.gen); |
| GfxICCBasedColorSpaceItem *item = static_cast<GfxICCBasedColorSpaceItem *>(gfx->getIccColorSpaceCache()->lookup(k)); |
| if (item != NULL) |
| { |
| cs = static_cast<GfxICCBasedColorSpace*>(item->cs->copy()); |
| return cs; |
| } |
| } |
| #endif |
| arr->get(1, &obj1); |
| if (!obj1.isStream()) { |
| error(-1, "Bad ICCBased color space (stream)"); |
| obj1.free(); |
| return NULL; |
| } |
| dict = obj1.streamGetDict(); |
| if (!dict->lookup("N", &obj2)->isInt()) { |
| error(-1, "Bad ICCBased color space (N)"); |
| obj2.free(); |
| obj1.free(); |
| return NULL; |
| } |
| nCompsA = obj2.getInt(); |
| obj2.free(); |
| if (nCompsA > gfxColorMaxComps) { |
| error(-1, "ICCBased color space with too many (%d > %d) components", |
| nCompsA, gfxColorMaxComps); |
| nCompsA = gfxColorMaxComps; |
| } |
| if (dict->lookup("Alternate", &obj2)->isNull() || |
| !(altA = GfxColorSpace::parse(&obj2, gfx))) { |
| switch (nCompsA) { |
| case 1: |
| altA = new GfxDeviceGrayColorSpace(); |
| break; |
| case 3: |
| altA = new GfxDeviceRGBColorSpace(); |
| break; |
| case 4: |
| altA = new GfxDeviceCMYKColorSpace(); |
| break; |
| default: |
| error(-1, "Bad ICCBased color space - invalid N"); |
| obj2.free(); |
| obj1.free(); |
| return NULL; |
| } |
| } |
| obj2.free(); |
| cs = new GfxICCBasedColorSpace(nCompsA, altA, &iccProfileStreamA); |
| if (dict->lookup("Range", &obj2)->isArray() && |
| obj2.arrayGetLength() == 2 * nCompsA) { |
| Object obj4; |
| for (i = 0; i < nCompsA; ++i) { |
| obj2.arrayGet(2*i, &obj3); |
| obj2.arrayGet(2*i+1, &obj4); |
| if (obj3.isNum() && obj4.isNum()) { |
| cs->rangeMin[i] = obj3.getNum(); |
| cs->rangeMax[i] = obj4.getNum(); |
| } |
| obj3.free(); |
| obj4.free(); |
| } |
| } |
| obj2.free(); |
| obj1.free(); |
| |
| #ifdef USE_CMS |
| arr->get(1, &obj1); |
| dict = obj1.streamGetDict(); |
| Guchar *profBuf; |
| Stream *iccStream = obj1.getStream(); |
| int length = 0; |
| |
| profBuf = iccStream->toUnsignedChars(&length, 65536, 65536); |
| cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,length); |
| gfree(profBuf); |
| if (hp == 0) { |
| error(-1, "read ICCBased color space profile error"); |
| } else { |
| cmsHPROFILE dhp = displayProfile; |
| if (dhp == NULL) dhp = RGBProfile; |
| unsigned int cst = getCMSColorSpaceType(cmsGetColorSpace(hp)); |
| unsigned int dNChannels = getCMSNChannels(cmsGetColorSpace(dhp)); |
| unsigned int dcst = getCMSColorSpaceType(cmsGetColorSpace(dhp)); |
| cmsHTRANSFORM transform; |
| if ((transform = cmsCreateTransform(hp, |
| COLORSPACE_SH(cst) |CHANNELS_SH(nCompsA) | BYTES_SH(1), |
| dhp, |
| COLORSPACE_SH(dcst) | |
| CHANNELS_SH(dNChannels) | BYTES_SH(1), |
| INTENT_RELATIVE_COLORIMETRIC,0)) == 0) { |
| error(-1, "Can't create transform"); |
| cs->transform = NULL; |
| } else { |
| cs->transform = new GfxColorTransform(transform); |
| } |
| if (dcst == PT_RGB) { |
| // create line transform only when the display is RGB type color space |
| if ((transform = cmsCreateTransform(hp, |
| CHANNELS_SH(nCompsA) | BYTES_SH(1),dhp, |
| TYPE_RGB_8,INTENT_RELATIVE_COLORIMETRIC,0)) == 0) { |
| error(-1, "Can't create transform"); |
| cs->lineTransform = NULL; |
| } else { |
| cs->lineTransform = new GfxColorTransform(transform); |
| } |
| } |
| cmsCloseProfile(hp); |
| } |
| obj1.free(); |
| // put this colorSpace into cache |
| if (gfx && iccProfileStreamA.num > 0) { |
| GfxICCBasedColorSpaceKey *k = new GfxICCBasedColorSpaceKey(iccProfileStreamA.num, iccProfileStreamA.gen); |
| GfxICCBasedColorSpaceItem *item = new GfxICCBasedColorSpaceItem(cs); |
| gfx->getIccColorSpaceCache()->put(k, item); |
| } |
| #endif |
| return cs; |
| } |
| |
| void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| #ifdef USE_CMS |
| if (transform != 0 && displayPixelType == PT_GRAY) { |
| Guchar in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| |
| for (int i = 0;i < nComps;i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| transform->doTransform(in,out,1); |
| *gray = byteToCol(out[0]); |
| } else { |
| GfxRGB rgb; |
| getRGB(color,&rgb); |
| *gray = clip01((GfxColorComp)(0.3 * rgb.r + |
| 0.59 * rgb.g + |
| 0.11 * rgb.b + 0.5)); |
| } |
| #else |
| alt->getGray(color, gray); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| #ifdef USE_CMS |
| if (transform != 0 |
| && (displayProfile == NULL || displayPixelType == PT_RGB)) { |
| Guchar in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| |
| for (int i = 0;i < nComps;i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| transform->doTransform(in,out,1); |
| rgb->r = byteToCol(out[0]); |
| rgb->g = byteToCol(out[1]); |
| rgb->b = byteToCol(out[2]); |
| } else { |
| alt->getRGB(color, rgb); |
| } |
| #else |
| alt->getRGB(color, rgb); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBLine(Guchar *in, unsigned int *out, |
| int length) { |
| #ifdef USE_CMS |
| if (lineTransform != 0) { |
| Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar)); |
| lineTransform->doTransform(in, tmp, length); |
| for (int i = 0; i < length; ++i) { |
| Guchar *current = tmp + (i * 3); |
| out[i] = (current[0] << 16) | (current[1] << 8) | current[2]; |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBLine(in, out, length); |
| } |
| #else |
| alt->getRGBLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) { |
| #ifdef USE_CMS |
| if (lineTransform != 0) { |
| Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar)); |
| lineTransform->doTransform(in, tmp, length); |
| Guchar *current = tmp; |
| for (int i = 0; i < length; ++i) { |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = *current++; |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBLine(in, out, length); |
| } |
| #else |
| alt->getRGBLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) { |
| #ifdef USE_CMS |
| if (lineTransform != 0) { |
| Guchar* tmp = (Guchar *)gmallocn(3 * length, sizeof(Guchar)); |
| lineTransform->doTransform(in, tmp, length); |
| Guchar *current = tmp; |
| for (int i = 0; i < length; ++i) { |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = *current++; |
| *out++ = 255; |
| } |
| gfree(tmp); |
| } else { |
| alt->getRGBXLine(in, out, length); |
| } |
| #else |
| alt->getRGBXLine(in, out, length); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| #ifdef USE_CMS |
| if (transform != NULL && displayPixelType == PT_CMYK) { |
| Guchar in[gfxColorMaxComps]; |
| Guchar out[gfxColorMaxComps]; |
| |
| for (int i = 0;i < nComps;i++) { |
| in[i] = colToByte(color->c[i]); |
| } |
| transform->doTransform(in,out,1); |
| cmyk->c = byteToCol(out[0]); |
| cmyk->m = byteToCol(out[1]); |
| cmyk->y = byteToCol(out[2]); |
| cmyk->k = byteToCol(out[3]); |
| } else { |
| GfxRGB rgb; |
| GfxColorComp c, m, y, k; |
| |
| getRGB(color,&rgb); |
| c = clip01(gfxColorComp1 - rgb.r); |
| m = clip01(gfxColorComp1 - rgb.g); |
| y = clip01(gfxColorComp1 - rgb.b); |
| k = c; |
| if (m < k) { |
| k = m; |
| } |
| if (y < k) { |
| k = y; |
| } |
| cmyk->c = c - k; |
| cmyk->m = m - k; |
| cmyk->y = y - k; |
| cmyk->k = k; |
| } |
| #else |
| alt->getCMYK(color, cmyk); |
| #endif |
| } |
| |
| GBool GfxICCBasedColorSpace::useGetRGBLine() { |
| #ifdef USE_CMS |
| return lineTransform != NULL || alt->useGetRGBLine(); |
| #else |
| return alt->useGetRGBLine(); |
| #endif |
| } |
| |
| void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) { |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| if (rangeMin[i] > 0) { |
| color->c[i] = dblToCol(rangeMin[i]); |
| } else if (rangeMax[i] < 0) { |
| color->c[i] = dblToCol(rangeMax[i]); |
| } else { |
| color->c[i] = 0; |
| } |
| } |
| } |
| |
| void GfxICCBasedColorSpace::getDefaultRanges(double *decodeLow, |
| double *decodeRange, |
| int maxImgPixel) { |
| alt->getDefaultRanges(decodeLow, decodeRange, maxImgPixel); |
| |
| #if 0 |
| // this is nominally correct, but some PDF files don't set the |
| // correct ranges in the ICCBased dict |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| decodeLow[i] = rangeMin[i]; |
| decodeRange[i] = rangeMax[i] - rangeMin[i]; |
| } |
| #endif |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxIndexedColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxIndexedColorSpace::GfxIndexedColorSpace(GfxColorSpace *baseA, |
| int indexHighA) { |
| base = baseA; |
| indexHigh = indexHighA; |
| lookup = (Guchar *)gmallocn((indexHigh + 1) * base->getNComps(), |
| sizeof(Guchar)); |
| } |
| |
| GfxIndexedColorSpace::~GfxIndexedColorSpace() { |
| delete base; |
| gfree(lookup); |
| } |
| |
| GfxColorSpace *GfxIndexedColorSpace::copy() { |
| GfxIndexedColorSpace *cs; |
| |
| cs = new GfxIndexedColorSpace(base->copy(), indexHigh); |
| memcpy(cs->lookup, lookup, |
| (indexHigh + 1) * base->getNComps() * sizeof(Guchar)); |
| return cs; |
| } |
| |
| GfxColorSpace *GfxIndexedColorSpace::parse(Array *arr, Gfx *gfx) { |
| GfxIndexedColorSpace *cs; |
| GfxColorSpace *baseA; |
| int indexHighA; |
| Object obj1; |
| char *s; |
| int n, i, j; |
| |
| if (arr->getLength() != 4) { |
| error(-1, "Bad Indexed color space"); |
| goto err1; |
| } |
| arr->get(1, &obj1); |
| if (!(baseA = GfxColorSpace::parse(&obj1, gfx))) { |
| error(-1, "Bad Indexed color space (base color space)"); |
| goto err2; |
| } |
| obj1.free(); |
| if (!arr->get(2, &obj1)->isInt()) { |
| error(-1, "Bad Indexed color space (hival)"); |
| delete baseA; |
| goto err2; |
| } |
| indexHighA = obj1.getInt(); |
| if (indexHighA < 0 || indexHighA > 255) { |
| // the PDF spec requires indexHigh to be in [0,255] -- allowing |
| // values larger than 255 creates a security hole: if nComps * |
| // indexHigh is greater than 2^31, the loop below may overwrite |
| // past the end of the array |
| int previousValue = indexHighA; |
| if (indexHighA < 0) indexHighA = 0; |
| else indexHighA = 255; |
| error(-1, "Bad Indexed color space (invalid indexHigh value, was %d using %d to try to recover)", previousValue, indexHighA); |
| } |
| obj1.free(); |
| cs = new GfxIndexedColorSpace(baseA, indexHighA); |
| arr->get(3, &obj1); |
| n = baseA->getNComps(); |
| if (obj1.isStream()) { |
| obj1.streamReset(); |
| for (i = 0; i <= indexHighA; ++i) { |
| const int readChars = obj1.streamGetChars(n, &cs->lookup[i*n]); |
| for (j = readChars; j < n; ++j) { |
| error(-1, "Bad Indexed color space (lookup table stream too short) padding with zeroes"); |
| cs->lookup[i*n + j] = 0; |
| } |
| } |
| obj1.streamClose(); |
| } else if (obj1.isString()) { |
| if (obj1.getString()->getLength() < (indexHighA + 1) * n) { |
| error(-1, "Bad Indexed color space (lookup table string too short)"); |
| goto err3; |
| } |
| s = obj1.getString()->getCString(); |
| for (i = 0; i <= indexHighA; ++i) { |
| for (j = 0; j < n; ++j) { |
| cs->lookup[i*n + j] = (Guchar)*s++; |
| } |
| } |
| } else { |
| error(-1, "Bad Indexed color space (lookup table)"); |
| goto err3; |
| } |
| obj1.free(); |
| return cs; |
| |
| err3: |
| delete cs; |
| err2: |
| obj1.free(); |
| err1: |
| return NULL; |
| } |
| |
| GfxColor *GfxIndexedColorSpace::mapColorToBase(GfxColor *color, |
| GfxColor *baseColor) { |
| Guchar *p; |
| double low[gfxColorMaxComps], range[gfxColorMaxComps]; |
| int n, i; |
| |
| n = base->getNComps(); |
| base->getDefaultRanges(low, range, indexHigh); |
| p = &lookup[(int)(colToDbl(color->c[0]) + 0.5) * n]; |
| for (i = 0; i < n; ++i) { |
| baseColor->c[i] = dblToCol(low[i] + (p[i] / 255.0) * range[i]); |
| } |
| return baseColor; |
| } |
| |
| void GfxIndexedColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| GfxColor color2; |
| |
| base->getGray(mapColorToBase(color, &color2), gray); |
| } |
| |
| void GfxIndexedColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| GfxColor color2; |
| |
| base->getRGB(mapColorToBase(color, &color2), rgb); |
| } |
| |
| void GfxIndexedColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) { |
| Guchar *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (Guchar *) gmallocn (length, n); |
| for (i = 0; i < length; i++) |
| for (j = 0; j < n; j++) |
| line[i * n + j] = lookup[in[i] * n + j]; |
| |
| base->getRGBLine(line, out, length); |
| |
| gfree (line); |
| } |
| |
| void GfxIndexedColorSpace::getRGBLine(Guchar *in, Guchar *out, int length) |
| { |
| Guchar *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (Guchar *) gmallocn (length, n); |
| for (i = 0; i < length; i++) |
| for (j = 0; j < n; j++) |
| line[i * n + j] = lookup[in[i] * n + j]; |
| |
| base->getRGBLine(line, out, length); |
| |
| gfree (line); |
| } |
| |
| void GfxIndexedColorSpace::getRGBXLine(Guchar *in, Guchar *out, int length) |
| { |
| Guchar *line; |
| int i, j, n; |
| |
| n = base->getNComps(); |
| line = (Guchar *) gmallocn (length, n); |
| for (i = 0; i < length; i++) |
| for (j = 0; j < n; j++) |
| line[i * n + j] = lookup[in[i] * n + j]; |
| |
| base->getRGBXLine(line, out, length); |
| |
| gfree (line); |
| } |
| |
| void GfxIndexedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| GfxColor color2; |
| |
| base->getCMYK(mapColorToBase(color, &color2), cmyk); |
| } |
| |
| void GfxIndexedColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = 0; |
| } |
| |
| void GfxIndexedColorSpace::getDefaultRanges(double *decodeLow, |
| double *decodeRange, |
| int maxImgPixel) { |
| decodeLow[0] = 0; |
| decodeRange[0] = maxImgPixel; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxSeparationColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxSeparationColorSpace::GfxSeparationColorSpace(GooString *nameA, |
| GfxColorSpace *altA, |
| Function *funcA) { |
| name = nameA; |
| alt = altA; |
| func = funcA; |
| nonMarking = !name->cmp("None"); |
| } |
| |
| GfxSeparationColorSpace::~GfxSeparationColorSpace() { |
| delete name; |
| delete alt; |
| delete func; |
| } |
| |
| GfxColorSpace *GfxSeparationColorSpace::copy() { |
| return new GfxSeparationColorSpace(name->copy(), alt->copy(), func->copy()); |
| } |
| |
| //~ handle the 'All' and 'None' colorants |
| GfxColorSpace *GfxSeparationColorSpace::parse(Array *arr, Gfx *gfx) { |
| GfxSeparationColorSpace *cs; |
| GooString *nameA; |
| GfxColorSpace *altA; |
| Function *funcA; |
| Object obj1; |
| |
| if (arr->getLength() != 4) { |
| error(-1, "Bad Separation color space"); |
| goto err1; |
| } |
| if (!arr->get(1, &obj1)->isName()) { |
| error(-1, "Bad Separation color space (name)"); |
| goto err2; |
| } |
| nameA = new GooString(obj1.getName()); |
| obj1.free(); |
| arr->get(2, &obj1); |
| if (!(altA = GfxColorSpace::parse(&obj1, gfx))) { |
| error(-1, "Bad Separation color space (alternate color space)"); |
| goto err3; |
| } |
| obj1.free(); |
| arr->get(3, &obj1); |
| if (!(funcA = Function::parse(&obj1))) { |
| goto err4; |
| } |
| obj1.free(); |
| cs = new GfxSeparationColorSpace(nameA, altA, funcA); |
| return cs; |
| |
| err4: |
| delete altA; |
| err3: |
| delete nameA; |
| err2: |
| obj1.free(); |
| err1: |
| return NULL; |
| } |
| |
| void GfxSeparationColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| double x; |
| double c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| x = colToDbl(color->c[0]); |
| func->transform(&x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getGray(&color2, gray); |
| } |
| |
| void GfxSeparationColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double x; |
| double c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| x = colToDbl(color->c[0]); |
| func->transform(&x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getRGB(&color2, rgb); |
| } |
| |
| void GfxSeparationColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| double x; |
| double c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| x = colToDbl(color->c[0]); |
| func->transform(&x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getCMYK(&color2, cmyk); |
| } |
| |
| void GfxSeparationColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0] = gfxColorComp1; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxDeviceNColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxDeviceNColorSpace::GfxDeviceNColorSpace(int nCompsA, |
| GfxColorSpace *altA, |
| Function *funcA) { |
| nComps = nCompsA; |
| alt = altA; |
| func = funcA; |
| nonMarking = gFalse; |
| } |
| |
| GfxDeviceNColorSpace::~GfxDeviceNColorSpace() { |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| delete names[i]; |
| } |
| delete alt; |
| delete func; |
| } |
| |
| GfxColorSpace *GfxDeviceNColorSpace::copy() { |
| GfxDeviceNColorSpace *cs; |
| int i; |
| |
| cs = new GfxDeviceNColorSpace(nComps, alt->copy(), func->copy()); |
| for (i = 0; i < nComps; ++i) { |
| cs->names[i] = names[i]->copy(); |
| } |
| cs->nonMarking = nonMarking; |
| return cs; |
| } |
| |
| //~ handle the 'None' colorant |
| GfxColorSpace *GfxDeviceNColorSpace::parse(Array *arr, Gfx *gfx) { |
| GfxDeviceNColorSpace *cs; |
| int nCompsA; |
| GooString *namesA[gfxColorMaxComps]; |
| GfxColorSpace *altA; |
| Function *funcA; |
| Object obj1, obj2; |
| int i; |
| |
| if (arr->getLength() != 4 && arr->getLength() != 5) { |
| error(-1, "Bad DeviceN color space"); |
| goto err1; |
| } |
| if (!arr->get(1, &obj1)->isArray()) { |
| error(-1, "Bad DeviceN color space (names)"); |
| goto err2; |
| } |
| nCompsA = obj1.arrayGetLength(); |
| if (nCompsA > gfxColorMaxComps) { |
| error(-1, "DeviceN color space with too many (%d > %d) components", |
| nCompsA, gfxColorMaxComps); |
| nCompsA = gfxColorMaxComps; |
| } |
| for (i = 0; i < nCompsA; ++i) { |
| if (!obj1.arrayGet(i, &obj2)->isName()) { |
| error(-1, "Bad DeviceN color space (names)"); |
| obj2.free(); |
| goto err2; |
| } |
| namesA[i] = new GooString(obj2.getName()); |
| obj2.free(); |
| } |
| obj1.free(); |
| arr->get(2, &obj1); |
| if (!(altA = GfxColorSpace::parse(&obj1, gfx))) { |
| error(-1, "Bad DeviceN color space (alternate color space)"); |
| goto err3; |
| } |
| obj1.free(); |
| arr->get(3, &obj1); |
| if (!(funcA = Function::parse(&obj1))) { |
| goto err4; |
| } |
| obj1.free(); |
| cs = new GfxDeviceNColorSpace(nCompsA, altA, funcA); |
| cs->nonMarking = gTrue; |
| for (i = 0; i < nCompsA; ++i) { |
| cs->names[i] = namesA[i]; |
| if (namesA[i]->cmp("None")) { |
| cs->nonMarking = gFalse; |
| } |
| } |
| return cs; |
| |
| err4: |
| delete altA; |
| err3: |
| for (i = 0; i < nCompsA; ++i) { |
| delete namesA[i]; |
| } |
| err2: |
| obj1.free(); |
| err1: |
| return NULL; |
| } |
| |
| void GfxDeviceNColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| double x[gfxColorMaxComps], c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| x[i] = colToDbl(color->c[i]); |
| } |
| func->transform(x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getGray(&color2, gray); |
| } |
| |
| void GfxDeviceNColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| double x[gfxColorMaxComps], c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| x[i] = colToDbl(color->c[i]); |
| } |
| func->transform(x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getRGB(&color2, rgb); |
| } |
| |
| void GfxDeviceNColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| double x[gfxColorMaxComps], c[gfxColorMaxComps]; |
| GfxColor color2; |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| x[i] = colToDbl(color->c[i]); |
| } |
| func->transform(x, c); |
| for (i = 0; i < alt->getNComps(); ++i) { |
| color2.c[i] = dblToCol(c[i]); |
| } |
| alt->getCMYK(&color2, cmyk); |
| } |
| |
| void GfxDeviceNColorSpace::getDefaultColor(GfxColor *color) { |
| int i; |
| |
| for (i = 0; i < nComps; ++i) { |
| color->c[i] = gfxColorComp1; |
| } |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxPatternColorSpace |
| //------------------------------------------------------------------------ |
| |
| GfxPatternColorSpace::GfxPatternColorSpace(GfxColorSpace *underA) { |
| under = underA; |
| } |
| |
| GfxPatternColorSpace::~GfxPatternColorSpace() { |
| if (under) { |
| delete under; |
| } |
| } |
| |
| GfxColorSpace *GfxPatternColorSpace::copy() { |
| return new GfxPatternColorSpace(under ? under->copy() : |
| (GfxColorSpace *)NULL); |
| } |
| |
| GfxColorSpace *GfxPatternColorSpace::parse(Array *arr, Gfx *gfx) { |
| GfxPatternColorSpace *cs; |
| GfxColorSpace *underA; |
| Object obj1; |
| |
| if (arr->getLength() != 1 && arr->getLength() != 2) { |
| error(-1, "Bad Pattern color space"); |
| return NULL; |
| } |
| underA = NULL; |
| if (arr->getLength() == 2) { |
| arr->get(1, &obj1); |
| if (!(underA = GfxColorSpace::parse(&obj1, gfx))) { |
| error(-1, "Bad Pattern color space (underlying color space)"); |
| obj1.free(); |
| return NULL; |
| } |
| obj1.free(); |
| } |
| cs = new GfxPatternColorSpace(underA); |
| return cs; |
| } |
| |
| void GfxPatternColorSpace::getGray(GfxColor *color, GfxGray *gray) { |
| *gray = 0; |
| } |
| |
| void GfxPatternColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { |
| rgb->r = rgb->g = rgb->b = 0; |
| } |
| |
| void GfxPatternColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { |
| cmyk->c = cmyk->m = cmyk->y = 0; |
| cmyk->k = 1; |
| } |
| |
| void GfxPatternColorSpace::getDefaultColor(GfxColor *color) { |
| color->c[0]=0; |
| } |
| |
| //------------------------------------------------------------------------ |
| // Pattern |
| //------------------------------------------------------------------------ |
| |
| GfxPattern::GfxPattern(int typeA) { |
| type = typeA; |
| } |
| |
| GfxPattern::~GfxPattern() { |
| } |
| |
| GfxPattern *GfxPattern::parse(Object *obj, Gfx *gfx) { |
| GfxPattern *pattern; |
| Object obj1; |
| |
| if (obj->isDict()) { |
| obj->dictLookup("PatternType", &obj1); |
| } else if (obj->isStream()) { |
| obj->streamGetDict()->lookup("PatternType", &obj1); |
| } else { |
| return NULL; |
| } |
| pattern = NULL; |
| if (obj1.isInt() && obj1.getInt() == 1) { |
| pattern = GfxTilingPattern::parse(obj); |
| } else if (obj1.isInt() && obj1.getInt() == 2) { |
| pattern = GfxShadingPattern::parse(obj, gfx); |
| } |
| obj1.free(); |
| return pattern; |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxTilingPattern |
| //------------------------------------------------------------------------ |
| |
| GfxTilingPattern *GfxTilingPattern::parse(Object *patObj) { |
| GfxTilingPattern *pat; |
| Dict *dict; |
| int paintTypeA, tilingTypeA; |
| double bboxA[4], matrixA[6]; |
| double xStepA, yStepA; |
| Object resDictA; |
| Object obj1, obj2; |
| int i; |
| |
| if (!patObj->isStream()) { |
| return NULL; |
| } |
| dict = patObj->streamGetDict(); |
| |
| if (dict->lookup("PaintType", &obj1)->isInt()) { |
| paintTypeA = obj1.getInt(); |
| } else { |
| paintTypeA = 1; |
| error(-1, "Invalid or missing PaintType in pattern"); |
| } |
| obj1.free(); |
| if (dict->lookup("TilingType", &obj1)->isInt()) { |
| tilingTypeA = obj1.getInt(); |
| } else { |
| tilingTypeA = 1; |
| error(-1, "Invalid or missing TilingType in pattern"); |
| } |
| obj1.free(); |
| bboxA[0] = bboxA[1] = 0; |
| bboxA[2] = bboxA[3] = 1; |
| if (dict->lookup("BBox", &obj1)->isArray() && |
| obj1.arrayGetLength() == 4) { |
| for (i = 0; i < 4; ++i) { |
| if (obj1.arrayGet(i, &obj2)->isNum()) { |
| bboxA[i] = obj2.getNum(); |
| } |
| obj2.free(); |
| } |
| } else { |
| error(-1, "Invalid or missing BBox in pattern"); |
| } |
| obj1.free(); |
| if (dict->lookup("XStep", &obj1)->isNum()) { |
| xStepA = obj1.getNum(); |
| } else { |
| xStepA = 1; |
| error(-1, "Invalid or missing XStep in pattern"); |
| } |
| obj1.free(); |
| if (dict->lookup("YStep", &obj1)->isNum()) { |
| yStepA = obj1.getNum(); |
| } else { |
| yStepA = 1; |
| error(-1, "Invalid or missing YStep in pattern"); |
| } |
| obj1.free(); |
| if (!dict->lookup("Resources", &resDictA)->isDict()) { |
| resDictA.free(); |
| resDictA.initNull(); |
| error(-1, "Invalid or missing Resources in pattern"); |
| } |
| matrixA[0] = 1; matrixA[1] = 0; |
| matrixA[2] = 0; matrixA[3] = 1; |
| matrixA[4] = 0; matrixA[5] = 0; |
| if (dict->lookup("Matrix", &obj1)->isArray() && |
| obj1.arrayGetLength() == 6) { |
| for (i = 0; i < 6; ++i) { |
| if (obj1.arrayGet(i, &obj2)->isNum()) { |
| matrixA[i] = obj2.getNum(); |
| } |
| obj2.free(); |
| } |
| } |
| obj1.free(); |
| |
| pat = new GfxTilingPattern(paintTypeA, tilingTypeA, bboxA, xStepA, yStepA, |
| &resDictA, matrixA, patObj); |
| resDictA.free(); |
| return pat; |
| } |
| |
| GfxTilingPattern::GfxTilingPattern(int paintTypeA, int tilingTypeA, |
| double *bboxA, double xStepA, double yStepA, |
| Object *resDictA, double *matrixA, |
| Object *contentStreamA): |
| GfxPattern(1) |
| { |
| int i; |
| |
| paintType = paintTypeA; |
| tilingType = tilingTypeA; |
| for (i = 0; i < 4; ++i) { |
| bbox[i] = bboxA[i]; |
| } |
| xStep = xStepA; |
| yStep = yStepA; |
| resDictA->copy(&resDict); |
| for (i = 0; i < 6; ++i) { |
| matrix[i] = matrixA[i]; |
| } |
| contentStreamA->copy(&contentStream); |
| } |
| |
| GfxTilingPattern::~GfxTilingPattern() { |
| resDict.free(); |
| contentStream.free(); |
| } |
| |
| GfxPattern *GfxTilingPattern::copy() { |
| return new GfxTilingPattern(paintType, tilingType, bbox, xStep, yStep, |
| &resDict, matrix, &contentStream); |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxShadingPattern |
| //------------------------------------------------------------------------ |
| |
| GfxShadingPattern *GfxShadingPattern::parse(Object *patObj, Gfx *gfx) { |
| Dict *dict; |
| GfxShading *shadingA; |
| double matrixA[6]; |
| Object obj1, obj2; |
| int i; |
| |
| if (!patObj->isDict()) { |
| return NULL; |
| } |
| dict = patObj->getDict(); |
| |
| dict->lookup("Shading", &obj1); |
| shadingA = GfxShading::parse(&obj1, gfx); |
| obj1.free(); |
| if (!shadingA) { |
| return NULL; |
| } |
| |
| matrixA[0] = 1; matrixA[1] = 0; |
| matrixA[2] = 0; matrixA[3] = 1; |
| matrixA[4] = 0; matrixA[5] = 0; |
| if (dict->lookup("Matrix", &obj1)->isArray() && |
| obj1.arrayGetLength() == 6) { |
| for (i = 0; i < 6; ++i) { |
| if (obj1.arrayGet(i, &obj2)->isNum()) { |
| matrixA[i] = obj2.getNum(); |
| } |
| obj2.free(); |
| } |
| } |
| obj1.free(); |
| |
| return new GfxShadingPattern(shadingA, matrixA); |
| } |
| |
| GfxShadingPattern::GfxShadingPattern(GfxShading *shadingA, double *matrixA): |
| GfxPattern(2) |
| { |
| int i; |
| |
| shading = shadingA; |
| for (i = 0; i < 6; ++i) { |
| matrix[i] = matrixA[i]; |
| } |
| } |
| |
| GfxShadingPattern::~GfxShadingPattern() { |
| delete shading; |
| } |
| |
| GfxPattern *GfxShadingPattern::copy() { |
| return new GfxShadingPattern(shading->copy(), matrix); |
| } |
| |
| //------------------------------------------------------------------------ |
| // GfxShading |
| //------------------------------------------------------------------------ |
| |
| GfxShading::GfxShading(int typeA) { |
| type = typeA; |
| colorSpace = NULL; |
| } |
| |
| GfxShading::GfxShading(GfxShading *shading) { |
| int i; |
| |
| type = shading->type; |
| colorSpace = shading->colorSpace->copy(); |
| for (i = 0; i < gfxColorMaxComps; ++i) { |
| background.c[i] = shading->background.c[i]; |
| } |
| hasBackground = shading->hasBackground; |
| xMin = shading->xMin; |
| yMin = shading->yMin; |
| xMax = shading->xMax; |
| yMax = shading->yMax; |
| hasBBox = shading->hasBBox; |
| } |
| |
| GfxShading::~GfxShading() { |
| if (colorSpace) { |
| delete colorSpace; |
| } |
| } |
| |
| GfxShading *GfxShading::parse(Object *obj, Gfx *gfx) { |
| GfxShading *shading; |
| Dict *dict; |
| int typeA; |
| Object obj1; |
| |
| if (obj->isDict()) { |
| dict = obj->getDict(); |
| } else if (obj->isStream()) { |
| dict = obj->streamGetDict(); |
| } else { |
| return NULL; |
| } |
| |
| if (!dict->lookup("ShadingType", &obj1)->isInt()) { |
| error(-1, "Invalid ShadingType in shading dictionary"); |
| obj1.free(); |
| return NULL; |
| } |
| typeA = obj1.getInt(); |
| obj1.free(); |
| |
| switch (typeA) { |
| case 1: |
| shading = GfxFunctionShading::parse(dict, gfx); |
| break; |
| case 2: |
| shading = GfxAxialShading::parse(dict, gfx); |
| break; |
| case 3: |
| shading = GfxRadialShading::parse(dict, gfx); |
| break; |
| case 4: |
| if (obj->isStream()) { |
| shading = GfxGouraudTriangleShading::parse(4, dict, obj->getStream(), gfx); |
| } else { |
| error(-1, "Invalid Type 4 shading object"); |
| goto err1; |
| } |
| break; |
| case 5: |
| if (obj->isStream()) { |
| shading = GfxGouraudTriangleShading::parse(5, dict, obj->getStream(), gfx); |
| } else { |
| error(-1, "Invalid Type 5 shading object"); |
| goto err1; |
| } |
| break; |
| case 6: |
| if (obj->isStream()) { |
| shading = GfxPatchMeshShading::parse(6, dict, obj->getStream(), gfx); |
| } else { |
| error(-1, "Invalid Type 6 shading object"); |
| goto err1; |
| } |
| break; |
| case 7: |
| if (obj->isStream()) { |
| shading = GfxPatchMeshShading::parse(7, dict, obj->getStream(), gfx); |
| } else { |
| error(-1, "Invalid Type 7 shading object"); |
| goto err1; |
| } |
| break; |
| default: |
| error(-1, "Unimplemented shading type %d", typeA); |
| goto err1; |
| } |
| |
| return shading; |
| |
| err1: |
| return NULL; |
| } |
| |
| GBool GfxShading::init(Dict *dict, Gfx *gfx) { |
| Object obj1, obj2; |
| int i; |
| |
| dict->lookup("ColorSpace", &obj1); |
| if (!(colorSpace = GfxColorSpace::parse(&obj1, gfx))) { |
| error(-1, "Bad color space in shading dictionary"); |
| obj1.free(); |
| return gFalse; |
| } |
| obj1.free(); |
| |
| for (i = 0; i < gfxColorMaxComps; ++i) { |
| background.c[i] = 0; |
| } |
| hasBackground = gFalse; |
| if (dict->lookup("Background", &obj1)->isArray()) { |
| if (obj1.arrayGetLength() == colorSpace->getNComps()) { |
| hasBackground = gTrue; |
| for (i = 0; i < colorSpace->getNComps(); ++i) { |
| background.c[i] = dblToCol(obj1.arrayGet(i, &obj2)->getNum()); |
| obj2.free(); |
| } |
| } else { |
| error(-1, "Bad Background in shading dictionary"); |
| } |
| } |
| obj1.free(); |
| |
| xMin = yMin = xMax = yMax = 0; |
| hasBBox = gFalse; |
| if (dict->lookup("BBox", &obj1)->isArray()) { |
| if (obj1.arrayGetLength() == 4) { |
| Object obj3, obj4, obj5; |
| obj1.arrayGet(0, &obj2); |
| obj1.arrayGet(1, &obj3); |
| obj1.arrayGet(2, &obj4); |
| obj1.arrayGet(3, &obj5); |
| if (obj2.isNum() && obj3.isNum() && obj4.isNum() && obj5.isNum()) |
| { |
| hasBBox = gTrue; |
| |