summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoji Otani <sho@bbr.jp>2009-01-07 17:43:44 +0100
committerAlbert Astals Cid <aacid@kde.org>2009-01-07 17:59:51 +0100
commit140b8ed97416f9c2ec02eb749ca45ca50bd651a8 (patch)
tree1c606a2009fa8a0f43a063f6a7c6867b26280d6e
parentbdc76dc811a6e4d5fd929bbdc8cd3300aeaea31f (diff)
Add initial support for color management
-rw-r--r--configure.ac19
-rw-r--r--poppler/GfxState.cc697
-rw-r--r--poppler/GfxState.h74
-rw-r--r--poppler/Makefile.am5
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) \