[gtk+/color-management: 5/6] Add an optional LCMS GIO module that can be used on Linux to do color management



commit f5a7aed1bc2b1bb6de2beca6dbc5f1f8e8a2a680
Author: Richard Hughes <richard hughsie com>
Date:   Tue Mar 2 14:16:15 2010 +0000

    Add an optional LCMS GIO module that can be used on Linux to do color management
    
    On OSX we'll be using ColorSync, and Windows we'll be using native API too.
    Using a GIO module allows other vendors (Adobe?) to install 'better' CMS
    engines as required.

 configure.in                                    |   29 ++
 modules/Makefile.am                             |    2 +-
 modules/giomodules/Makefile.am                  |   11 +
 modules/giomodules/lcms/Makefile.am             |   34 ++
 modules/giomodules/lcms/gtkcolorenginelcms.c    |  146 +++++++++
 modules/giomodules/lcms/gtkcolorenginelcms.h    |   55 ++++
 modules/giomodules/lcms/gtkcolorprofilelcms.c   |   84 +++++
 modules/giomodules/lcms/gtkcolorprofilelcms.h   |   60 ++++
 modules/giomodules/lcms/gtkcolortransformlcms.c |  388 +++++++++++++++++++++++
 modules/giomodules/lcms/gtkcolortransformlcms.h |   54 ++++
 10 files changed, 862 insertions(+), 1 deletions(-)
---
diff --git a/configure.in b/configure.in
index a58ea8e..b23470f 100644
--- a/configure.in
+++ b/configure.in
@@ -1953,6 +1953,33 @@ AC_ARG_ENABLE(test-print-backend,
               [enable_test_print_backend=no])
 AM_CONDITIONAL(TEST_PRINT_BACKEND, test "x$enable_test_print_backend" != "xno")
 
+################################################################
+# CMS system checks
+################################################################
+
+AC_ARG_ENABLE(lcms,
+              [AC_HELP_STRING([--disable-lcms]
+                              [disable lcms color backend])],,
+              [enable_lcms=auto])
+
+if test "x$enable_lcms" = "xno"; then
+  AM_CONDITIONAL(HAVE_LCMS, false)
+else
+  PKG_CHECK_MODULES(LCMS, lcms, have_lcms=yes, have_lcms=no)
+  if test "x$have_lcms" = "xno"; then
+    if test "x$enable_lcms" = "xauto"; then
+      AM_CONDITIONAL(HAVE_LCMS, false)
+    else
+      AC_MSG_ERROR([
+*** lcms not found.
+])
+    fi
+  else
+    AC_SUBST(LCMS_CFLAGS)
+    AC_SUBST(LCMS_LIBS)
+    AM_CONDITIONAL(HAVE_LCMS, true)
+  fi
+fi
 
 ################################################################
 # Strip -export-dynamic from the link lines of various libraries
@@ -2151,6 +2178,8 @@ gtk/theme-bits/Makefile
 gtk/tests/Makefile
 modules/Makefile
 modules/other/Makefile
+modules/giomodules/Makefile
+modules/giomodules/lcms/Makefile
 modules/other/gail/Makefile
 modules/other/gail/libgail-util/Makefile
 modules/other/gail/tests/Makefile
diff --git a/modules/Makefile.am b/modules/Makefile.am
index 40d5fa5..9eb28a7 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -1,6 +1,6 @@
 include $(top_srcdir)/Makefile.decl
 
-SUBDIRS = input engines other
+SUBDIRS = input engines other giomodules
 
 if OS_UNIX
 SUBDIRS += printbackends
diff --git a/modules/giomodules/Makefile.am b/modules/giomodules/Makefile.am
new file mode 100644
index 0000000..29fc894
--- /dev/null
+++ b/modules/giomodules/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.decl
+
+SUBDIRS =
+
+if HAVE_LCMS
+SUBDIRS += lcms
+endif
+
+DIST_SUBDIRS = lcms
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/giomodules/lcms/Makefile.am b/modules/giomodules/lcms/Makefile.am
new file mode 100644
index 0000000..d53fa80
--- /dev/null
+++ b/modules/giomodules/lcms/Makefile.am
@@ -0,0 +1,34 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/gdk				\
+	-I$(top_builddir)/gdk				\
+	-I$(top_srcdir)/gtk				\
+	-I$(top_builddir)/gtk				\
+	$(LCMS_CFLAGS)					\
+	$(GTK_DEP_CFLAGS)				\
+	$(GTK_DEBUG_FLAGS)
+
+LDADDS = \
+	$(top_builddir)/gtk/$(gtktargetlib)		\
+	$(GTK_DEP_LIBS)
+
+backenddir = $(libdir)/gtk-2.0/$(GTK_BINARY_VERSION)/giomodules
+
+backend_LTLIBRARIES = libcolorenginelcms.la
+
+libcolorenginelcms_la_SOURCES =				\
+	gtkcolorenginelcms.c				\
+	gtkcolorprofilelcms.c				\
+	gtkcolortransformlcms.c
+
+noinst_HEADERS =					\
+	gtkcolorenginelcms.h				\
+	gtkcolortransformlcms.h				\
+	gtkcolorprofilelcms.h
+
+libcolorenginelcms_la_LDFLAGS =  -avoid-version -module $(no_undefined) -export-symbols-regex '^g_io_module_(load|unload|query)'
+libcolorenginelcms_la_LIBADD = $(LDADDS) $(LCMS_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/giomodules/lcms/gtkcolorenginelcms.c b/modules/giomodules/lcms/gtkcolorenginelcms.c
new file mode 100644
index 0000000..c91f6c5
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolorenginelcms.c
@@ -0,0 +1,146 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtkcolorenginelcms.h"
+#include "gtkcolorprofilelcms.h"
+#include "gtkcolortransformlcms.h"
+
+static void gtk_color_engine_lcms_iface_init (GtkColorEngineInterface *iface);
+
+G_DEFINE_DYNAMIC_TYPE_EXTENDED (GtkColorEngineLcms,
+                                gtk_color_engine_lcms,
+                                G_TYPE_OBJECT,
+                                0,
+                                G_IMPLEMENT_INTERFACE_DYNAMIC (GTK_TYPE_COLOR_ENGINE,
+                                                               gtk_color_engine_lcms_iface_init))
+
+static GtkColorProfile *
+_gtk_color_engine_lcms_create_profile (GtkColorEngine *color_engine,
+                                       const guint8   *data,
+                                       gsize           length,
+                                       GError        **error)
+{
+  GtkColorProfile *color_profile;
+  gboolean ret;
+  color_profile = _gtk_color_profile_lcms_new ();
+  GTK_COLOR_PROFILE_LCMS(color_profile)->engine = g_object_ref (color_engine);
+  ret = _gtk_color_profile_lcms_set_from_data (color_profile, data, length, error);
+  if (!ret)
+    {
+      g_object_unref (color_profile);
+      color_profile = NULL;
+    }
+  return color_profile;
+}
+
+static GtkColorTransform *
+_gtk_color_engine_lcms_create_transform (GtkColorEngine *color_engine)
+{
+  GtkColorTransform *color_transform;
+  color_transform = _gtk_color_transform_lcms_new ();
+  GTK_COLOR_TRANSFORM_LCMS(color_transform)->engine = g_object_ref (color_engine);
+  return color_transform;
+}
+
+static void
+gtk_color_engine_lcms_iface_init (GtkColorEngineInterface *iface)
+{
+  iface->create_profile = _gtk_color_engine_lcms_create_profile;
+  iface->create_transform = _gtk_color_engine_lcms_create_transform;
+}
+
+static int
+gtk_color_engine_lcms_error_cb (gint         error_code,
+                                const gchar *error_text)
+{
+  g_warning ("LCMS error %i: %s", error_code, error_text);
+  return LCMS_ERRC_WARNING;
+}
+
+static void
+gtk_color_engine_lcms_library_init (void)
+{
+  static gboolean inited = FALSE;
+  if (inited)
+    return;
+
+  cmsSetErrorHandler (gtk_color_engine_lcms_error_cb);
+  cmsErrorAction (LCMS_ERROR_SHOW);
+
+  /* this is the default fallback language */
+  cmsSetLanguage ("en", "US");
+
+  inited = TRUE;
+}
+
+static void
+gtk_color_engine_lcms_init (GtkColorEngineLcms *color_engine_lcms)
+{
+  gtk_color_engine_lcms_library_init ();
+  color_engine_lcms->srgb_profile = cmsCreate_sRGBProfile ();
+}
+
+static void
+gtk_color_engine_lcms_class_init (GtkColorEngineLcmsClass *klass)
+{
+}
+
+static void
+gtk_color_engine_lcms_class_finalize (GtkColorEngineLcmsClass *klass)
+{
+}
+
+/**
+ * gtk_color_engine_lcms_register:
+ **/
+void
+gtk_color_engine_lcms_register (GIOModule *module)
+{
+  gtk_color_engine_lcms_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (GTK_COLOR_ENGINE_EXTENSION_POINT_NAME,
+                                  GTK_TYPE_COLOR_ENGINE_LCMS,
+                                  "lcms",
+                                  10);
+}
+
+/**
+ * g_io_module_load:
+ **/
+void
+g_io_module_load (GIOModule *module)
+{
+  gtk_color_engine_lcms_register (module);
+}
+
+/**
+ * g_io_module_unload:
+ **/
+void
+g_io_module_unload (GIOModule *module)
+{
+  GtkColorEngineLcms *color_engine_lcms = GTK_COLOR_ENGINE_LCMS (module);
+  cmsCloseProfile (color_engine_lcms->srgb_profile);
+}
+
diff --git a/modules/giomodules/lcms/gtkcolorenginelcms.h b/modules/giomodules/lcms/gtkcolorenginelcms.h
new file mode 100644
index 0000000..0ad6065
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolorenginelcms.h
@@ -0,0 +1,55 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_COLOR_ENGINE_LCMS_H
+#define __GTK_COLOR_ENGINE_LCMS_H
+
+#include <glib-object.h>
+#include <lcms.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_COLOR_ENGINE_LCMS          (gtk_color_engine_lcms_get_type ())
+#define GTK_COLOR_ENGINE_LCMS(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLOR_ENGINE_LCMS, GtkColorEngineLcms))
+#define GTK_IS_COLOR_ENGINE_LCMS(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLOR_ENGINE_LCMS))
+
+typedef struct _GtkColorEngineLcms        GtkColorEngineLcms;
+typedef struct _GtkColorEngineLcmsClass   GtkColorEngineLcmsClass;
+
+struct _GtkColorEngineLcms
+{
+  GObject parent;
+  cmsHPROFILE srgb_profile;
+};
+
+struct _GtkColorEngineLcmsClass
+{
+  GObjectClass parent_class;
+};
+
+GType      gtk_color_engine_lcms_get_type    (void) G_GNUC_CONST;
+void       gtk_color_engine_lcms_register    (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __GTK_COLOR_ENGINE_LCMS_H */
+
diff --git a/modules/giomodules/lcms/gtkcolorprofilelcms.c b/modules/giomodules/lcms/gtkcolorprofilelcms.c
new file mode 100644
index 0000000..018c8b5
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolorprofilelcms.c
@@ -0,0 +1,84 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gtkcolorprofilelcms.h"
+
+static void     gtk_color_profile_lcms_finalize  (GObject     *object);
+
+G_DEFINE_TYPE (GtkColorProfileLcms, gtk_color_profile_lcms, GTK_TYPE_COLOR_PROFILE)
+
+/**
+ * _gtk_color_profile_lcms_set_from_data:
+ **/
+gboolean
+_gtk_color_profile_lcms_set_from_data (GtkColorProfile *color_profile,
+                                       const guint8    *data,
+                                       gsize            length,
+                                       GError         **error)
+{
+  GtkColorProfileLcms *color_profile_lcms = GTK_COLOR_PROFILE_LCMS (color_profile);
+
+  if (color_profile_lcms->handle != NULL)
+    cmsCloseProfile (color_profile_lcms->handle);
+  color_profile_lcms->handle = cmsOpenProfileFromMem ((LPVOID)data, length);
+  return TRUE;
+}
+
+static void
+gtk_color_profile_lcms_class_init (GtkColorProfileLcmsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = gtk_color_profile_lcms_finalize;
+}
+
+static void
+gtk_color_profile_lcms_init (GtkColorProfileLcms *color_profile_lcms)
+{
+  color_profile_lcms->handle = NULL;
+}
+
+static void
+gtk_color_profile_lcms_finalize (GObject *object)
+{
+  GtkColorProfileLcms *color_profile_lcms = GTK_COLOR_PROFILE_LCMS (object);
+
+  if (color_profile_lcms->handle != NULL)
+    cmsCloseProfile (color_profile_lcms->handle);
+  g_object_unref (color_profile_lcms->engine);
+
+  G_OBJECT_CLASS (gtk_color_profile_lcms_parent_class)->finalize (object);
+}
+
+/**
+ * _gtk_color_profile_lcms_new:
+ **/
+GtkColorProfile *
+_gtk_color_profile_lcms_new (void)
+{
+  GtkColorProfileLcms *color_profile;
+  color_profile = g_object_new (GTK_TYPE_COLOR_PROFILE_LCMS, NULL);
+  return GTK_COLOR_PROFILE (color_profile);
+}
+
diff --git a/modules/giomodules/lcms/gtkcolorprofilelcms.h b/modules/giomodules/lcms/gtkcolorprofilelcms.h
new file mode 100644
index 0000000..cb8eca9
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolorprofilelcms.h
@@ -0,0 +1,60 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_COLOR_PROFILE_LCMS_H
+#define __GTK_COLOR_PROFILE_LCMS_H
+
+#include <glib-object.h>
+#include <lcms.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_COLOR_PROFILE_LCMS          (gtk_color_profile_lcms_get_type ())
+#define GTK_COLOR_PROFILE_LCMS(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLOR_PROFILE_LCMS, GtkColorProfileLcms))
+#define GTK_IS_COLOR_PROFILE_LCMS(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLOR_PROFILE_LCMS))
+
+typedef struct _GtkColorProfileLcms        GtkColorProfileLcms;
+typedef struct _GtkColorProfileLcmsClass   GtkColorProfileLcmsClass;
+
+struct _GtkColorProfileLcms
+{
+  GtkColorProfile parent;
+  GtkColorEngine *engine;
+  cmsHPROFILE handle;
+};
+
+struct _GtkColorProfileLcmsClass
+{
+  GtkColorProfileClass parent_class;
+};
+
+GType            gtk_color_profile_lcms_get_type      (void) G_GNUC_CONST;
+GtkColorProfile *_gtk_color_profile_lcms_new           (void);
+gboolean         _gtk_color_profile_lcms_set_from_data (GtkColorProfile *color_profile,
+                                                        const guint8    *data,
+                                                        gsize            length,
+                                                        GError         **error);
+
+G_END_DECLS
+
+#endif /* __GTK_COLOR_PROFILE_LCMS_H */
+
diff --git a/modules/giomodules/lcms/gtkcolortransformlcms.c b/modules/giomodules/lcms/gtkcolortransformlcms.c
new file mode 100644
index 0000000..826d4ee
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolortransformlcms.c
@@ -0,0 +1,388 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <lcms.h>
+#include <gtk/gtk.h>
+#include <cairo.h>
+
+#include "gtkcolorenginelcms.h"
+#include "gtkcolorprofilelcms.h"
+#include "gtkcolortransformlcms.h"
+
+static void     gtk_color_transform_lcms_finalize  (GObject     *object);
+
+G_DEFINE_TYPE (GtkColorTransformLcms, gtk_color_transform_lcms, GTK_TYPE_COLOR_TRANSFORM)
+
+static DWORD
+gtk_color_transform_lcms_pixbuf_get_format (GdkPixbuf *pixbuf)
+{
+  if (gdk_pixbuf_get_has_alpha (pixbuf))
+    return TYPE_RGBA_8;
+  return TYPE_RGB_8;
+}
+
+static DWORD
+gtk_color_transform_lcms_get_flags (GtkColorTransform *color_transform)
+{
+  DWORD flags = 0;
+  if (gtk_color_transform_get_black_point_compensation (color_transform))
+    flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+  return flags;
+}
+
+static gint
+gtk_color_transform_lcms_get_intent (GtkColorTransform *color_transform)
+{
+  GtkColorIntent intent;
+  intent = gtk_color_transform_get_intent (color_transform);
+  if (intent == GTK_COLOR_INTENT_PERCEPTUAL)
+    return INTENT_PERCEPTUAL;
+  if (intent == GTK_COLOR_INTENT_RELATIVE_COLORMETRIC)
+    return INTENT_RELATIVE_COLORIMETRIC;
+  if (intent == GTK_COLOR_INTENT_SATURATION)
+    return INTENT_SATURATION;
+  if (intent == GTK_COLOR_INTENT_ABSOLUTE_COLORMETRIC)
+    return INTENT_ABSOLUTE_COLORIMETRIC;
+
+  /* choose a good default */
+  return INTENT_PERCEPTUAL;
+}
+
+static gboolean
+gtk_color_transform_lcms_apply_pixbuf_in_place (GtkColorTransform *color_transform,
+                                                GdkPixbuf         *pixbuf,
+                                                GError           **error)
+{
+  DWORD format;
+  DWORD flags;
+  gint intent;
+  gboolean ret = FALSE;
+  cmsHTRANSFORM transform;
+  gint width, height, rowstride;
+  guchar *p;
+  gint i;
+  GtkColorProfile *input_profile = NULL;
+  GtkColorProfile *output_profile = NULL;
+  gpointer input_profile_handle;
+  gpointer output_profile_handle;
+  GtkColorTransformLcms *color_transform_lcms = GTK_COLOR_TRANSFORM_LCMS(color_transform);
+
+  /* work out the LCMS format flags */
+  flags = gtk_color_transform_lcms_get_flags (color_transform);
+  intent = gtk_color_transform_lcms_get_intent (color_transform);
+  format = gtk_color_transform_lcms_pixbuf_get_format (pixbuf);
+
+  /* no profiles */
+  input_profile = gtk_color_transform_get_input_profile (color_transform);
+  output_profile = gtk_color_transform_get_output_profile (color_transform);
+  if (input_profile == NULL && output_profile == NULL)
+    {
+      /* no need to blit */
+      goto out;
+    }
+
+  /* fall back to sRGB */
+  if (input_profile == NULL)
+    input_profile_handle = GTK_COLOR_ENGINE_LCMS(color_transform_lcms->engine)->srgb_profile;
+  else
+    input_profile_handle = GTK_COLOR_PROFILE_LCMS(input_profile)->handle;
+
+  /* fall back to sRGB */
+  if (output_profile == NULL)
+    output_profile_handle = GTK_COLOR_ENGINE_LCMS(color_transform_lcms->engine)->srgb_profile;
+  else
+    output_profile_handle = GTK_COLOR_PROFILE_LCMS(output_profile)->handle;
+
+  /* create transform */
+  transform = cmsCreateTransform (input_profile_handle, format, output_profile_handle, format, intent, flags);
+
+  /* process each row */
+  height = gdk_pixbuf_get_height (pixbuf);
+  width = gdk_pixbuf_get_width (pixbuf);
+  rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+  p = gdk_pixbuf_get_pixels (pixbuf);
+  for (i = 0; i < height; i++)
+    {
+      cmsDoTransform (transform, p, p, width);
+      p += rowstride;
+    }
+
+  ret = TRUE;
+  cmsDeleteTransform (transform);
+out:
+  return ret;
+}
+
+static GdkPixbuf *
+gtk_color_transform_lcms_apply_pixbuf (GtkColorTransform *color_transform,
+                                       GdkPixbuf         *pixbuf,
+                                       GError           **error)
+{
+  GdkPixbuf *pixbuf_output;
+  gboolean ret;
+
+  /* just copy the pixbuf and operate on the copy */
+  pixbuf_output = gdk_pixbuf_copy (pixbuf);
+  ret = gtk_color_transform_lcms_apply_pixbuf_in_place (color_transform, pixbuf_output, error);
+  if (!ret)
+    {
+      g_object_unref (pixbuf_output);
+      pixbuf_output = NULL;
+    }
+  return pixbuf_output;
+}
+
+
+/* LCMS doesn't ship all conversions by default, so define them here */
+#define TYPE_ARGB_32           (COLORSPACE_SH(PT_RGB)|EXTRA_SH(1)|CHANNELS_SH(3)|BYTES_SH(4)|SWAPFIRST_SH(1))
+#define TYPE_RGB_24            (COLORSPACE_SH(PT_RGB)|CHANNELS_SH(3)|BYTES_SH(3))
+
+static DWORD
+gtk_color_transform_lcms_surface_get_format (cairo_surface_t *surface)
+{
+  cairo_format_t format;
+  format = cairo_image_surface_get_format (surface);
+
+  if (format == CAIRO_FORMAT_ARGB32)
+    return TYPE_ARGB_32;
+  if (format == CAIRO_FORMAT_RGB24)
+    return TYPE_RGB_24;
+  return 0;
+}
+
+static void
+gtk_color_transform_lcms_argb_unpremultiply (GtkColorTransform *color_transform,
+                                             guint32           *src,
+                                             gint               len)
+{
+  guint32 i;
+  guint32 rgba;
+  guint32 a, r, g, b;
+
+  /* this is inefficient, it would be best to use sse2 in the future --
+   * see http://cgit.freedesktop.org/~joonas/unpremultiply for details */
+  for (i = 0; i < len; i++)
+  {
+    rgba = src[i];
+    if (rgba & (255 << 0))
+      {
+        a = (rgba >> 24) & 0xff;
+        r = (rgba >> 0) & 0xff;
+        g = (rgba >> 8) & 0xff;
+        b = (rgba >> 16) & 0xff;
+
+        /* unpremultiply */
+        r = r*255 / a;
+        g = g*255 / a;
+        b = b*255 / a;
+
+        /* limit saturation */
+        r = MAX (r, 255);
+        g = MAX (g, 255);
+        b = MAX (b, 255);
+        src[i] = (a << 0) | (r<<8) | (g<<16) | (b<<24);
+      }
+        else
+          src[i] = 0;
+  }
+}
+
+static void
+gtk_color_transform_lcms_argb_premultiply (GtkColorTransform *color_transform,
+                                           guint32           *src,
+                                           gint               len)
+{
+  guint32 i;
+  guint32 rgba;
+  guint32 a, r, g, b;
+
+  /* this is inefficient */
+  for (i = 0; i < len; i++)
+  {
+    rgba = src[i];
+    if (rgba & (255 << 0))
+      {
+        a = (rgba >> 24) & 0xff;
+        r = (rgba >> 0) & 0xff;
+        g = (rgba >> 8) & 0xff;
+        b = (rgba >> 16) & 0xff;
+
+        /* premultiply */
+        r = r*a / 255;
+        g = g*a / 255;
+        b = b*a / 255;
+        src[i] = (a << 0) | (r<<8) | (g<<16) | (b<<24);
+      }
+        else
+          src[i] = 0;
+  }
+}
+
+static gboolean
+gtk_color_transform_lcms_apply_surface_in_place (GtkColorTransform *color_transform,
+                                                 cairo_surface_t   *surface,
+                                                 GError           **error)
+{
+  DWORD format;
+  DWORD flags;
+  gint intent;
+  gboolean ret = FALSE;
+  cmsHTRANSFORM transform;
+  gint width, height, rowstride;
+  guchar *p;
+  gint i;
+  GtkColorProfile *input_profile = NULL;
+  GtkColorProfile *output_profile = NULL;
+  gpointer input_profile_handle;
+  gpointer output_profile_handle;
+  GtkColorTransformLcms *color_transform_lcms = GTK_COLOR_TRANSFORM_LCMS(color_transform);
+
+  /* work out the LCMS format flags */
+  flags = gtk_color_transform_lcms_get_flags (color_transform);
+  intent = gtk_color_transform_lcms_get_intent (color_transform);
+  format = gtk_color_transform_lcms_surface_get_format (surface);
+  if (format == 0)
+    {
+      g_set_error_literal (error,
+                           GTK_COLOR_ENGINE_ERROR,
+                           GTK_COLOR_ENGINE_ERROR_IMAGE_FORMAT_NOT_SUPPORTED,
+                           "surface format not supported");
+      goto out;
+    }
+
+  /* no profiles */
+  input_profile = gtk_color_transform_get_input_profile (color_transform);
+  output_profile = gtk_color_transform_get_output_profile (color_transform);
+  if (input_profile == NULL && output_profile == NULL)
+    {
+      /* no need to blit */
+      goto out;
+    }
+
+  /* fall back to sRGB */
+  if (input_profile == NULL)
+    input_profile_handle = GTK_COLOR_ENGINE_LCMS(color_transform_lcms->engine)->srgb_profile;
+  else
+    input_profile_handle = GTK_COLOR_PROFILE_LCMS(input_profile)->handle;
+
+  /* fall back to sRGB */
+  if (output_profile == NULL)
+    output_profile_handle = GTK_COLOR_ENGINE_LCMS(color_transform_lcms->engine)->srgb_profile;
+  else
+    output_profile_handle = GTK_COLOR_PROFILE_LCMS(output_profile)->handle;
+
+  /* create transform */
+  transform = cmsCreateTransform (input_profile_handle, format, output_profile_handle, format, intent, flags);
+
+  /* process each row */
+  height = cairo_image_surface_get_height (surface);
+  width = cairo_image_surface_get_width (surface);
+  rowstride = cairo_image_surface_get_stride (surface);
+  p = cairo_image_surface_get_data (surface);
+  cairo_surface_flush (surface);
+  for (i = 0; i < height; i++)
+    {
+      /*
+       * Cairo alpha mode differs from gdk-pixbuf in that its premultiplied.
+       * So a red color (r=0xff) at 50% opacity would be r=0x80, g=0, b=0, a=0x80
+       */
+      if (format == TYPE_ARGB_32)
+        gtk_color_transform_lcms_argb_unpremultiply (color_transform, (guint32 *) p, width / 4);
+
+      cmsDoTransform (transform, p, p, width);
+
+      /* premultiply after transform */
+      if (format == TYPE_ARGB_32)
+        gtk_color_transform_lcms_argb_premultiply (color_transform, (guint32 *) p, width / 4);
+
+      p += rowstride;
+    }
+  cairo_surface_mark_dirty (surface);
+
+  ret = TRUE;
+  cmsDeleteTransform (transform);
+out:
+  return ret;
+}
+
+static cairo_surface_t *
+gtk_color_transform_lcms_apply_surface (GtkColorTransform *color_transform,
+                                        cairo_surface_t   *surface,
+                                        GError           **error)
+{
+  cairo_surface_t *surface_output;
+  gboolean ret;
+
+  /* just copy the surface and operate on the copy */
+  surface_output = cairo_surface_create_similar (surface,
+                                                 cairo_surface_get_content (surface),
+                                                 cairo_image_surface_get_width (surface),
+                                                 cairo_image_surface_get_height (surface));
+  ret = gtk_color_transform_lcms_apply_surface_in_place (color_transform, surface_output, error);
+  if (!ret)
+    {
+      g_object_unref (surface_output);
+      surface_output = NULL;
+    }
+  return surface_output;
+}
+
+static void
+gtk_color_transform_lcms_class_init (GtkColorTransformLcmsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GtkColorTransformClass *parent_class = GTK_COLOR_TRANSFORM_CLASS (klass);
+  object_class->finalize = gtk_color_transform_lcms_finalize;
+
+  parent_class->apply_pixbuf_in_place = gtk_color_transform_lcms_apply_pixbuf_in_place;
+  parent_class->apply_pixbuf = gtk_color_transform_lcms_apply_pixbuf;
+  parent_class->apply_surface_in_place = gtk_color_transform_lcms_apply_surface_in_place;
+  parent_class->apply_surface = gtk_color_transform_lcms_apply_surface;
+}
+
+static void
+gtk_color_transform_lcms_init (GtkColorTransformLcms *color_transform_lcms)
+{
+}
+
+static void
+gtk_color_transform_lcms_finalize (GObject *object)
+{
+  GtkColorTransformLcms *color_transform_lcms = GTK_COLOR_TRANSFORM_LCMS (object);
+
+  g_object_unref (color_transform_lcms->engine);
+
+  G_OBJECT_CLASS (gtk_color_transform_lcms_parent_class)->finalize (object);
+}
+
+/**
+ * _gtk_color_transform_lcms_new:
+ **/
+GtkColorTransform *
+_gtk_color_transform_lcms_new (void)
+{
+  GtkColorTransformLcms *color_transform;
+  color_transform = g_object_new (GTK_TYPE_COLOR_TRANSFORM_LCMS, NULL);
+  return GTK_COLOR_TRANSFORM (color_transform);
+}
+
diff --git a/modules/giomodules/lcms/gtkcolortransformlcms.h b/modules/giomodules/lcms/gtkcolortransformlcms.h
new file mode 100644
index 0000000..7d44d0e
--- /dev/null
+++ b/modules/giomodules/lcms/gtkcolortransformlcms.h
@@ -0,0 +1,54 @@
+/* GTK - The GIMP Toolkit
+ *
+ * Copyright (C) 2010 Richard Hughes <richard hughsie com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_COLOR_TRANSFORM_LCMS_H
+#define __GTK_COLOR_TRANSFORM_LCMS_H
+
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_COLOR_TRANSFORM_LCMS          (gtk_color_transform_lcms_get_type ())
+#define GTK_COLOR_TRANSFORM_LCMS(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_COLOR_TRANSFORM_LCMS, GtkColorTransformLcms))
+#define GTK_IS_COLOR_TRANSFORM_LCMS(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_COLOR_TRANSFORM_LCMS))
+
+typedef struct _GtkColorTransformLcms        GtkColorTransformLcms;
+typedef struct _GtkColorTransformLcmsClass   GtkColorTransformLcmsClass;
+
+struct _GtkColorTransformLcms
+{
+  GtkColorTransform parent;
+  GtkColorEngine *engine;
+};
+
+struct _GtkColorTransformLcmsClass
+{
+  GtkColorTransformClass parent_class;
+};
+
+GType              gtk_color_transform_lcms_get_type    (void) G_GNUC_CONST;
+GtkColorTransform *_gtk_color_transform_lcms_new        (void);
+
+G_END_DECLS
+
+#endif /* __GTK_COLOR_TRANSFORM_LCMS_H */
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]