[poppler] 2 commits - CMakeLists.txt cmake/modules config.h.cmake configure.ac poppler/GfxState.cc poppler/GfxState.h poppler/Makefile.am

Albert Astals Cid aacid at kemper.freedesktop.org
Wed Jan 7 09:03:19 PST 2009


 CMakeLists.txt               |    9 
 cmake/modules/FindLCMS.cmake |   37 ++
 config.h.cmake               |    3 
 configure.ac                 |   19 +
 poppler/GfxState.cc          |  697 +++++++++++++++++++++++++++++++++++++++----
 poppler/GfxState.h           |   74 ++++
 poppler/Makefile.am          |    5 
 7 files changed, 787 insertions(+), 57 deletions(-)

New commits:
commit 18d584158a781fecb4f696b01fb4d17803ce7d7a
Author: Albert Astals Cid <aacid at kde.org>
Date:   Wed Jan 7 17:55:48 2009 +0100

    Add lcms option to cmake buildsystem

diff --git a/CMakeLists.txt b/CMakeLists.txt
index c088bd3..a8712a2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@ option(ENABLE_SPLASH "Build the Splash graphics backend." ON)
 option(ENABLE_UTILS "Compile poppler command line utils." ON)
 option(ENABLE_ABIWORD "Build the Abiword backend." ON)
 option(ENABLE_LIBOPENJPEG "Use libopenjpeg for JPX streams." ON)
+option(ENABLE_LCMS "Use liblcms for color management." ON)
 option(ENABLE_ZLIB "TODO" OFF)
 option(USE_EXCEPTIONS "Throw exceptions to deal with not enough memory and similar problems." OFF)
 option(USE_FIXEDPOINT "Use fixed point arithmetic" OFF)
@@ -70,6 +71,10 @@ if(ENABLE_LIBOPENJPEG)
   find_package(LIBOPENJPEG)
   set(ENABLE_LIBOPENJPEG ${LIBOPENJPEG_FOUND})
 endif(ENABLE_LIBOPENJPEG)
+if(ENABLE_LCMS)
+  find_package(LCMS)
+  set(USE_CMS ${LCMS_FOUND})
+endif(ENABLE_LCMS)
 
 add_definitions(-DHAVE_CONFIG_H=1 ${FONTCONFIG_DEFINITIONS})
 include_directories(
@@ -233,6 +238,9 @@ else (LIBOPENJPEG_FOUND)
     poppler/JPXStream.cc
   )
 endif(LIBOPENJPEG_FOUND)
+if(USE_CMS)
+  set(poppler_LIBS ${poppler_LIBS} ${LCMS_LIBRARIES})
+endif(USE_CMS)
 if(ENABLE_ABIWORD)
   set(poppler_SRCS ${poppler_SRCS}
     poppler/ABWOutputDev.cc
@@ -434,4 +442,5 @@ show_end_message("use libjpeg" ENABLE_LIBJPEG)
 show_end_message("use libpng" ENABLE_LIBPNG)
 show_end_message("use zlib" ENABLE_ZLIB)
 show_end_message("use libopenjpeg" LIBOPENJPEG_FOUND)
+show_end_message("use cms" USE_CMS)
 show_end_message("command line utils" ENABLE_UTILS)
diff --git a/cmake/modules/FindLCMS.cmake b/cmake/modules/FindLCMS.cmake
new file mode 100644
index 0000000..32a8266
--- /dev/null
+++ b/cmake/modules/FindLCMS.cmake
@@ -0,0 +1,37 @@
+# - Try to find the liblcms library
+# Once done this will define
+#
+#  LCMS_FOUND - system has liblcms
+#  LCMS_INCLUDE_DIRS - the liblcms include directories
+#  LCMS_LIBRARIES - Link these to use liblcms
+#  LCMS_INCLUDE_DIR is internal and deprecated for use
+
+# Copyright (c) 2008, Albert Astals Cid, <aacid at kde.org>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+if (LCMS_LIBRARIES AND LCMS_INCLUDE_DIR)
+
+  # in cache already
+  set(LCMS_FOUND TRUE)
+
+else (LCMS_LIBRARIES AND LCMS_INCLUDE_DIR)
+
+  #reset vars
+  set(LCMS_LIBRARIES)
+  set(LCMS_INCLUDE_DIR)
+
+  find_path (LCMS_INCLUDE_DIR lcms.h)
+  find_library(LCMS_LIBRARIES lcms)
+  if(LCMS_INCLUDE_DIR AND LCMS_LIBRARIES)
+    set(LCMS_FOUND TRUE)
+  endif(LCMS_INCLUDE_DIR AND LCMS_LIBRARIES)
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(LCMS DEFAULT_MSG LCMS_LIBRARIES LCMS_INCLUDE_DIR)
+
+endif (LCMS_LIBRARIES AND LCMS_INCLUDE_DIR)
+
+set(LCMS_INCLUDE_DIRS ${LCMS_INCLUDE_DIR})
diff --git a/config.h.cmake b/config.h.cmake
index b64185c..7b57de0 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -9,6 +9,9 @@
 /* Use libpng to export images in pdftohtml. */
 #cmakedefine ENABLE_LIBPNG 1
 
+/* Use liblcms for color management. */
+#cmakedefine USE_CMS 1
+
 /* Use cairo for rendering. */
 #cmakedefine HAVE_CAIRO 1
 
commit 140b8ed97416f9c2ec02eb749ca45ca50bd651a8
Author: Koji Otani <sho at bbr.jp>
Date:   Wed Jan 7 17:43:44 2009 +0100

    Add initial support for color management

diff --git a/configure.ac b/configure.ac
index 010b77e..3b919c5 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 7bb0667..ce0e6f6 100644
--- a/poppler/GfxState.cc
+++ b/poppler/GfxState.cc
@@ -17,6 +17,7 @@
 // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff at infidigm.net>
 // Copyright (C) 2006 Carlos Garcia Campos <carlosgc at gnome.org>
 // Copyright (C) 2006-2008 Albert Astals Cid <aacid at kde.org>
+// Copyright (C) 2009 Koji Otani <sho at 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 ac20d35..1a0aacf 100644
--- a/poppler/GfxState.h
+++ b/poppler/GfxState.h
@@ -16,6 +16,7 @@
 // Copyright (C) 2005 Kristian Høgsberg <krh at redhat.com>
 // Copyright (C) 2006, 2007 Jeff Muizelaar <jeff at infidigm.net>
 // Copyright (C) 2006 Carlos Garcia Campos <carlosgc at gnome.org>
+// Copyright (C) 2009 Koji Otani <sho at 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 bfa4443..df7920f 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)				\


More information about the poppler mailing list