diff options
author | Koji Otani <sho@bbr.jp> | 2009-01-07 17:43:44 +0100 |
---|---|---|
committer | Albert Astals Cid <aacid@kde.org> | 2009-01-07 17:59:51 +0100 |
commit | 140b8ed97416f9c2ec02eb749ca45ca50bd651a8 (patch) | |
tree | 1c606a2009fa8a0f43a063f6a7c6867b26280d6e | |
parent | bdc76dc811a6e4d5fd929bbdc8cd3300aeaea31f (diff) |
Add initial support for color management
-rw-r--r-- | configure.ac | 19 | ||||
-rw-r--r-- | poppler/GfxState.cc | 697 | ||||
-rw-r--r-- | poppler/GfxState.h | 74 | ||||
-rw-r--r-- | poppler/Makefile.am | 5 |
4 files changed, 738 insertions, 57 deletions
diff --git a/configure.ac b/configure.ac index 010b77ec..3b919c54 100644 --- a/configure.ac +++ b/configure.ac @@ -444,6 +444,24 @@ AC_ARG_ENABLE(compile-warnings, [Turn on compiler warnings.]),, [enable_compile_warnings="yes"]) +dnl +dnl Color Management +dnl + +AC_ARG_ENABLE(cms, + AC_HELP_STRING([--disable-cms], + [Don't use color management system.]), + enable_cms=$enableval, + enable_cms="yes") +if test x$enable_cms = xyes; then + AC_CHECK_LIB([lcms],cmsOpenProfileFromFile,, + AC_MSG_ERROR("*** lcms library not found ***")) + AC_CHECK_HEADERS([lcms.h],, + AC_MSG_ERROR("*** lcms headers not found ***")) + AC_DEFINE(USE_CMS, 1, [Defines if use cms]) +fi +AM_CONDITIONAL(USE_CMS, test x$enable_cms = xyes) + if test "x$GCC" != xyes; then enable_compile_warnings=no fi @@ -509,6 +527,7 @@ echo " use libjpeg: $enable_libjpeg" echo " use libpng: $enable_libpng" echo " use zlib: $enable_zlib" echo " use libopenjpeg: $enable_libopenjpeg" +echo " use cms: $enable_cms" echo " command line utils: $enable_utils" echo "" diff --git a/poppler/GfxState.cc b/poppler/GfxState.cc index 7bb06671..ce0e6f6c 100644 --- a/poppler/GfxState.cc +++ b/poppler/GfxState.cc @@ -17,6 +17,7 @@ // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> // Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org> // Copyright (C) 2006-2008 Albert Astals Cid <aacid@kde.org> +// Copyright (C) 2009 Koji Otani <sho@bbr.jp> // // 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 @@ -39,6 +40,7 @@ #include "Page.h" #include "GfxState.h" #include "GfxFont.h" +#include "GlobalParams.h" //------------------------------------------------------------------------ @@ -224,6 +226,225 @@ void GfxColorSpace::getRGBLine(Guchar *in, unsigned int *out, int length) { } } +#ifdef USE_CMS +cmsHPROFILE GfxColorSpace::RGBProfile = NULL; +cmsHPROFILE GfxColorSpace::displayProfile = NULL; +GooString *GfxColorSpace::displayProfileName = NULL; +unsigned int GfxColorSpace::displayPixelType = 0; +GfxColorTransform *GfxColorSpace::XYZ2DisplayTransform = NULL; + +cmsHPROFILE GfxColorSpace::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,const_cast<char *>(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(0), + INTENT_RELATIVE_COLORIMETRIC,0)) == 0) { + error(-1, "Can't create Lab transform"); + } else { + XYZ2DisplayTransform = new GfxColorTransform(transform); + } + cmsCloseProfile(XYZProfile); + } + return 0; +} + +unsigned int GfxColorSpace::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 GfxColorSpace::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 + void GfxColorSpace::getGrayLine(Guchar *in, unsigned char *out, int length) { int i, j, n; GfxColor color; @@ -311,6 +532,14 @@ GfxColorSpace *GfxCalGrayColorSpace::copy() { 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; @@ -353,32 +582,127 @@ GfxColorSpace *GfxCalGrayColorSpace::parse(Array *arr) { } 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 GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { - *gray = clip01(color->c[0]); -} +// convert CalGray to media XYZ color space +// (not multiply by the white point) +void GfxCalGrayColorSpace::getXYZ(GfxColor *color, + double *pX, double *pY, double *pZ) { + double A; -void GfxCalGrayColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) { - memcpy (out, in, length); + A = colToDbl(color->c[0]); + *pX = pow(A,gamma); + *pY = pow(A,gamma); + *pZ = pow(A,gamma); } -void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { - rgb->r = rgb->g = rgb->b = clip01(color->c[0]); +void GfxCalGrayColorSpace::getGray(GfxColor *color, GfxGray *gray) { + GfxRGB rgb; + +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { + double 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 = dblToCol(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::getRGBLine(Guchar *in, unsigned int *out, - int length) { - int i; +void GfxCalGrayColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { + double X, Y, Z; + double r, g, b; - for (i = 0; i < length; i++) - out[i] = (in[i] << 16) | (in[i] << 8) | (in[i] << 0); + getXYZ(color,&X,&Y,&Z); +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) { + double out[gfxColorMaxComps]; + double in[gfxColorMaxComps]; + + in[0] = clip01(X); + in[1] = clip01(Y); + in[2] = clip01(Z); + XYZ2DisplayTransform->doTransform(in,out,1); + rgb->r = dblToCol(out[0]); + rgb->g = dblToCol(out[1]); + rgb->b = dblToCol(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(pow(clip01(r * kr), 0.5)); + rgb->g = dblToCol(pow(clip01(g * kg), 0.5)); + rgb->b = dblToCol(pow(clip01(b * kb), 0.5)); + rgb->r = rgb->g = rgb->b = clip01(color->c[0]); } void GfxCalGrayColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { - cmyk->c = cmyk->m = cmyk->y = 0; - cmyk->k = clip01(gfxColorComp1 - color->c[0]); + GfxRGB rgb; + GfxColorComp c, m, y, k; + +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) { + double in[gfxColorMaxComps]; + double 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 = dblToCol(out[0]); + cmyk->m = dblToCol(out[1]); + cmyk->y = dblToCol(out[2]); + cmyk->k = dblToCol(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) { @@ -553,47 +877,112 @@ GfxColorSpace *GfxCalRGBColorSpace::parse(Array *arr) { } 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 GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) { - *gray = clip01((GfxColorComp)(0.299 * color->c[0] + - 0.587 * color->c[1] + - 0.114 * color->c[2] + 0.5)); +// convert CalRGB to XYZ color space +void GfxCalRGBColorSpace::getXYZ(GfxColor *color, + double *pX, double *pY, double *pZ) { + double A, B, C; + + A = colToDbl(color->c[0]); + B = colToDbl(color->c[1]); + C = colToDbl(color->c[2]); + *pX = mat[0]*pow(A,gammaR)+mat[3]*pow(B,gammaG)+mat[6]*pow(C,gammaB); + *pY = mat[1]*pow(A,gammaR)+mat[4]*pow(B,gammaG)+mat[7]*pow(C,gammaB); + *pZ = mat[2]*pow(A,gammaR)+mat[5]*pow(B,gammaG)+mat[8]*pow(C,gammaB); } -void GfxCalRGBColorSpace::getGrayLine(Guchar *in, Guchar *out, int length) { - int i; +void GfxCalRGBColorSpace::getGray(GfxColor *color, GfxGray *gray) { + GfxRGB rgb; - for (i = 0; i < length; i++) { - out[i] = - (in[i * 3 + 0] * 19595 + - in[i * 3 + 1] * 38469 + - in[i * 3 + 2] * 7472) / 65536; +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { + double 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 = dblToCol(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) { - rgb->r = clip01(color->c[0]); - rgb->g = clip01(color->c[1]); - rgb->b = clip01(color->c[2]); -} - -void GfxCalRGBColorSpace::getRGBLine(Guchar *in, unsigned int *out, - int length) { - Guchar *p; - int i; + double X, Y, Z; + double r, g, b; - for (i = 0, p = in; i < length; i++, p += 3) - out[i] = (p[0] << 16) | (p[1] << 8) | (p[2] << 0); + getXYZ(color,&X,&Y,&Z); +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_RGB) { + double 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 = dblToCol(out[0]); + rgb->g = dblToCol(out[1]); + rgb->b = dblToCol(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(pow(clip01(r), 0.5)); + rgb->g = dblToCol(pow(clip01(g), 0.5)); + rgb->b = dblToCol(pow(clip01(b), 0.5)); } void GfxCalRGBColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { + GfxRGB rgb; GfxColorComp c, m, y, k; - c = clip01(gfxColorComp1 - color->c[0]); - m = clip01(gfxColorComp1 - color->c[1]); - y = clip01(gfxColorComp1 - color->c[2]); +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_CMYK) { + double in[gfxColorMaxComps]; + double 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 = dblToCol(out[0]); + cmyk->m = dblToCol(out[1]); + cmyk->y = dblToCol(out[2]); + cmyk->k = dblToCol(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; @@ -714,14 +1103,6 @@ void GfxDeviceCMYKColorSpace::getDefaultColor(GfxColor *color) { // GfxLabColorSpace //------------------------------------------------------------------------ -// 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 } -}; - GfxLabColorSpace::GfxLabColorSpace() { whiteX = whiteY = whiteZ = 1; blackX = blackY = blackZ = 0; @@ -823,18 +1204,30 @@ GfxColorSpace *GfxLabColorSpace::parse(Array *arr) { void GfxLabColorSpace::getGray(GfxColor *color, GfxGray *gray) { GfxRGB rgb; +#ifdef USE_CMS + if (XYZ2DisplayTransform != NULL && displayPixelType == PT_GRAY) { + double out[gfxColorMaxComps]; + double in[gfxColorMaxComps]; + + getXYZ(color, &in[0], &in[1], &in[2]); + XYZ2DisplayTransform->doTransform(in,out,1); + *gray = dblToCol(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 GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { +// 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; - double r, g, b; - // convert L*a*b* to CIE 1931 XYZ color space t1 = (colToDbl(color->c[0]) + 16) / 116; t2 = t1 + colToDbl(color->c[1]) / 500; if (t2 >= (6.0 / 29.0)) { @@ -842,21 +1235,45 @@ void GfxLabColorSpace::getRGB(GfxColor *color, GfxRGB *rgb) { } else { X = (108.0 / 841.0) * (t2 - (4.0 / 29.0)); } - X *= whiteX; if (t1 >= (6.0 / 29.0)) { Y = t1 * t1 * t1; } else { Y = (108.0 / 841.0) * (t1 - (4.0 / 29.0)); } - Y *= whiteY; 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)); } - Z *= whiteZ; + *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) { + double out[gfxColorMaxComps]; + double in[gfxColorMaxComps]; + + in[0] = clip01(X); + in[1] = clip01(Y); + in[2] = clip01(Z); + XYZ2DisplayTransform->doTransform(in,out,1); + rgb->r = dblToCol(out[0]); + rgb->g = dblToCol(out[1]); + rgb->b = dblToCol(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; @@ -870,6 +1287,20 @@ 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]; + double out[gfxColorMaxComps]; + + getXYZ(color, &in[0], &in[1], &in[2]); + XYZ2DisplayTransform->doTransform(in,out,1); + cmyk->c = dblToCol(out[0]); + cmyk->m = dblToCol(out[1]); + cmyk->y = dblToCol(out[2]); + cmyk->k = dblToCol(out[3]); + return; + } +#endif getRGB(color, &rgb); c = clip01(gfxColorComp1 - rgb.r); m = clip01(gfxColorComp1 - rgb.g); @@ -926,10 +1357,22 @@ GfxICCBasedColorSpace::GfxICCBasedColorSpace(int nCompsA, GfxColorSpace *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() { @@ -941,6 +1384,12 @@ GfxColorSpace *GfxICCBasedColorSpace::copy() { 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; } @@ -1015,24 +1464,163 @@ GfxColorSpace *GfxICCBasedColorSpace::parse(Array *arr) { } obj2.free(); obj1.free(); + +#ifdef USE_CMS + arr->get(1, &obj1); + dict = obj1.streamGetDict(); + Guchar *profBuf; + unsigned int bufSize; + Stream *iccStream = obj1.getStream(); + int c; + unsigned int size = 0; + + bufSize = 65536; + profBuf = (Guchar *)gmallocn(bufSize,1); + iccStream->reset(); + while ((c = iccStream->getChar()) != EOF) { + if (bufSize <= size) { + bufSize += 65536; + profBuf = (Guchar *)greallocn(profBuf,bufSize,1); + } + profBuf[size++] = c; + } + cmsHPROFILE hp = cmsOpenProfileFromMem(profBuf,size); + 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(0), + dhp, + COLORSPACE_SH(dcst) | + CHANNELS_SH(dNChannels) | BYTES_SH(0), + INTENT_RELATIVE_COLORIMETRIC,0)) == 0) { + error(-1, "Can't create transform"); + } + 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 = new GfxColorTransform(transform); + } + cmsCloseProfile(hp); + } + obj1.free(); +#endif return cs; } void GfxICCBasedColorSpace::getGray(GfxColor *color, GfxGray *gray) { +#ifdef USE_CMS + if (transform != 0 && displayPixelType == PT_GRAY) { + double in[gfxColorMaxComps]; + double out[gfxColorMaxComps]; + + for (int i = 0;i < nComps;i++) { + in[i] = colToDbl(color->c[i]); + } + transform->doTransform(in,out,1); + *gray = dblToCol(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)) { + double in[gfxColorMaxComps]; + double out[gfxColorMaxComps]; + + for (int i = 0;i < nComps;i++) { + in[i] = colToDbl(color->c[i]); + } + transform->doTransform(in,out,1); + rgb->r = dblToCol(out[0]); + rgb->g = dblToCol(out[1]); + rgb->b = dblToCol(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) { + for (int i = 0;i < length;i++) { + Guchar tmp[gfxColorMaxComps]; + + lineTransform->doTransform(in,tmp,1); + in += 3; + out[i] = (tmp[0] << 16) | (tmp[1] << 8) | tmp[2]; + } + } else { + alt->getRGBLine(in, out, length); + } +#else alt->getRGBLine(in, out, length); +#endif } void GfxICCBasedColorSpace::getCMYK(GfxColor *color, GfxCMYK *cmyk) { +#ifdef USE_CMS + if (transform != NULL && displayPixelType == PT_CMYK) { + double in[gfxColorMaxComps]; + double out[gfxColorMaxComps]; + + for (int i = 0;i < nComps;i++) { + in[i] = colToDbl(color->c[i]); + } + transform->doTransform(in,out,1); + cmyk->c = dblToCol(out[0]); + cmyk->m = dblToCol(out[1]); + cmyk->y = dblToCol(out[2]); + cmyk->k = dblToCol(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 } void GfxICCBasedColorSpace::getDefaultColor(GfxColor *color) { @@ -3938,6 +4526,9 @@ GfxState::GfxState(double hDPIA, double vDPIA, PDFRectangle *pageBox, clipYMax = pageHeight; saved = NULL; +#ifdef USE_CMS + GfxColorSpace::setupColorProfiles(); +#endif } GfxState::~GfxState() { diff --git a/poppler/GfxState.h b/poppler/GfxState.h index ac20d356..1a0aacfe 100644 --- a/poppler/GfxState.h +++ b/poppler/GfxState.h @@ -16,6 +16,7 @@ // Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com> // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff@infidigm.net> // Copyright (C) 2006 Carlos Garcia Campos <carlosgc@gnome.org> +// Copyright (C) 2009 Koji Otani <sho@bbr.jp> // // 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 @@ -32,6 +33,9 @@ #include "goo/gtypes.h" #include "Object.h" #include "Function.h" +#ifdef USE_CMS +#include "lcms.h" +#endif class Array; class GfxFont; @@ -151,6 +155,37 @@ enum GfxColorSpaceMode { csPattern }; +#ifdef USE_CMS + +#define COLOR_PROFILE_DIR "/ColorProfiles/" +#define GLOBAL_COLOR_PROFILE_DIR POPPLER_DATADIR COLOR_PROFILE_DIR + +// wrapper of cmsHTRANSFORM to copy +class GfxColorTransform { +public: + void doTransform(void *in, void *out, unsigned int size) { + cmsDoTransform(transform, in, out, size); + } + GfxColorTransform(cmsHTRANSFORM transformA) { + transform = transformA; + refCount = 1; + } + ~GfxColorTransform() { + cmsDeleteTransform(transform); + } + void ref() { + refCount++; + } + unsigned int unref() { + return --refCount; + } +private: + GfxColorTransform() {} + cmsHTRANSFORM transform; + unsigned int refCount; +}; +#endif + class GfxColorSpace { public: @@ -191,6 +226,32 @@ public: static char *getColorSpaceModeName(int idx); private: +#ifdef USE_CMS +protected: + static cmsHPROFILE RGBProfile; + static GooString *displayProfileName; // display profile file Name + static cmsHPROFILE displayProfile; // display profile + static unsigned int displayPixelType; + static GfxColorTransform *XYZ2DisplayTransform; + // 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); +public: + static int setupColorProfiles(); + static void setDisplayProfile(cmsHPROFILE displayProfileA) { + displayProfile = displayProfileA; + } + static void setDisplayProfileName(GooString *name) { + displayProfileName = name->copy(); + } + static cmsHPROFILE getRGBProfile() { + return RGBProfile; + } + static cmsHPROFILE getDisplayProfile() { + return displayProfile; + } +#endif }; //------------------------------------------------------------------------ @@ -235,8 +296,6 @@ public: virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); - virtual void getGrayLine(Guchar *in, Guchar *out, int length); - virtual void getRGBLine(Guchar *in, unsigned int *out, int length); virtual int getNComps() { return 1; } virtual void getDefaultColor(GfxColor *color); @@ -255,6 +314,8 @@ private: double whiteX, whiteY, whiteZ; // white point double blackX, blackY, blackZ; // black point double gamma; // gamma value + double kr, kg, kb; // gamut mapping mulitpliers + void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ); }; //------------------------------------------------------------------------ @@ -299,8 +360,6 @@ public: virtual void getGray(GfxColor *color, GfxGray *gray); virtual void getRGB(GfxColor *color, GfxRGB *rgb); virtual void getCMYK(GfxColor *color, GfxCMYK *cmyk); - virtual void getGrayLine(Guchar *in, Guchar *out, int length); - virtual void getRGBLine(Guchar *in, unsigned int *out, int length); virtual int getNComps() { return 3; } virtual void getDefaultColor(GfxColor *color); @@ -323,6 +382,8 @@ private: double blackX, blackY, blackZ; // black point double gammaR, gammaG, gammaB; // gamma values double mat[9]; // ABC -> XYZ transform matrix + double kr, kg, kb; // gamut mapping mulitpliers + void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ); }; //------------------------------------------------------------------------ @@ -390,6 +451,7 @@ private: double blackX, blackY, blackZ; // black point double aMin, aMax, bMin, bMax; // range for the a and b components double kr, kg, kb; // gamut mapping mulitpliers + void getXYZ(GfxColor *color, double *pX, double *pY, double *pZ); }; //------------------------------------------------------------------------ @@ -429,6 +491,10 @@ private: double rangeMin[4]; // min values for each component double rangeMax[4]; // max values for each component Ref iccProfileStream; // the ICC profile +#ifdef USE_CMS + GfxColorTransform *transform; + GfxColorTransform *lineTransform; // color transform for line +#endif }; //------------------------------------------------------------------------ diff --git a/poppler/Makefile.am b/poppler/Makefile.am index bfa44437..df7920fe 100644 --- a/poppler/Makefile.am +++ b/poppler/Makefile.am @@ -105,6 +105,10 @@ abiword_libs = \ endif +if USE_CMS +cms_libs = -llcms +endif + INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/goo \ @@ -126,6 +130,7 @@ CXXFLAGS+=$(PTHREAD_CFLAGS) libpoppler_la_LIBADD = \ $(top_builddir)/goo/libgoo.la \ $(top_builddir)/fofi/libfofi.la \ + $(cms_libs) \ $(splash_libs) \ $(libjpeg_libs) \ $(zlib_libs) \ |