diff --git a/configure.in b/configure.in
index e835274..6b3ca2a 100644
--- a/configure.in
+++ b/configure.in
@@ -1950,6 +1950,45 @@ 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 print 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
+
+################################################################
+# Directory for GIO modules
+################################################################
+
+AC_ARG_WITH(gtk-giomodule-dir,
+           [AC_HELP_STRING([--with-gtk-giomodule-dir=PATH],
+                           [Load gio modules from this directory [LIBDIR/gtk-2.0/${GTK_BINARY_VERSION}/giomodules]])],
+           [],
+           [with_gtk_giomodule_dir='${libdir}/gtk-2.0/${GTK_BINARY_VERSION}/giomodules'])
+GTK_GIOMODULE_DIR=$with_gtk_giomodule_dir
+AC_SUBST(GTK_GIOMODULE_DIR)
 
 ################################################################
 # Strip -export-dynamic from the link lines of various libraries
@@ -2148,6 +2187,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/demos/icc-profile-fish.png b/demos/icc-profile-fish.png
new file mode 100644
index 0000000..7f8b64b
Binary files /dev/null and b/demos/icc-profile-fish.png differ
diff --git a/demos/testpixbuf-color.c b/demos/testpixbuf-color.c
index 78fa939..1f570f7 100644
--- a/demos/testpixbuf-color.c
+++ b/demos/testpixbuf-color.c
@@ -100,6 +100,72 @@ out:
 	return ret;
 }
 
+static gboolean
+transform_image (const gchar *filename, GError **error)
+{
+	GtkWidget *image_test;
+	GtkWidget *dialog;
+	GtkWidget *vbox;
+	gint response;
+	GdkPixbuf *pixbuf;
+	GdkIccTransform *transform = NULL;
+	gchar *icc_profile = NULL;
+	gsize len = 0;
+	gboolean ret;
+	const gchar *option;
+
+	image_test = gtk_image_new_from_file (filename);
+	pixbuf = gtk_image_get_pixbuf (GTK_IMAGE(image_test));
+
+	/* check values */
+	option = gdk_pixbuf_get_option (pixbuf, "icc-profile");
+	if (option == NULL) {
+		*error = g_error_new (1, 0, "no profile set");
+		goto out;
+	}
+
+	/* decode base64 */
+	icc_profile = (gchar *) g_base64_decode (option, &len);
+	if (len == 0) {
+		*error = g_error_new (1, 0, "no embedded profile");
+		goto out;
+	}
+
+        /* transform this image with the embedded profile */
+        transform = gdk_icc_transform_new (error);
+        if (transform == NULL) {
+                ret = FALSE;
+                goto out;
+        }
+        ret = gdk_icc_transform_set_input_profile (transform, (const guint8 *)icc_profile, len, error);
+        if (!ret)
+                goto out;
+        ret = gdk_icc_transform_apply_in_place (transform, pixbuf, error);
+        if (!ret)
+                goto out;
+
+	/* show in a dialog as an example */
+	dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "Are the fish yellow?");
+	vbox = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+	gtk_box_pack_end (GTK_BOX(vbox), image_test, TRUE, TRUE, 12);
+	gtk_widget_set_size_request (GTK_WIDGET(image_test), 300, 300);
+	gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE);
+	gtk_widget_show (image_test);
+
+        /* ask the user to ensure the image was converted */
+	response = gtk_dialog_run (GTK_DIALOG (dialog));
+	if (response != GTK_RESPONSE_YES) {
+		ret = FALSE;
+		*error = g_error_new (1, 0, "failed to convert fish");
+		goto out;
+	}
+out:
+        if (transform != NULL)
+                g_object_unref (transform);
+	g_free (icc_profile);
+	return ret;
+}
+
 int
 main (int argc, char **argv)
 {
@@ -118,6 +184,15 @@ main (int argc, char **argv)
 					       0, 0, 0, 0, 150, 160);
 
 	/* PASS */
+	g_debug ("try to render color corrected image");
+	ret = transform_image ("icc-profile-fish.png", &error);
+	if (!ret) {
+		g_warning ("FAILED: did not load image: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* PASS */
 	g_debug ("try to save PNG with a profile");
 	ret = save_image_png ("icc-profile.png", pixbuf, &error);
 	if (!ret) {
diff --git a/gdk/Makefile.am b/gdk/Makefile.am
index e1cd8c7..ad0a57d 100644
--- a/gdk/Makefile.am
+++ b/gdk/Makefile.am
@@ -86,6 +86,7 @@ gdk_public_h_sources =				\
 	gdkgc.h					\
 	gdki18n.h				\
 	gdkimage.h				\
+	gdkicctransform.h			\
 	gdkinput.h				\
 	gdkkeys.h				\
 	gdkkeysyms.h				\
@@ -131,6 +132,7 @@ gdk_c_sources =                 \
 	gdkfont.c		\
 	gdkgc.c			\
 	gdkglobals.c		\
+	gdkicctransform.c	\
 	gdkimage.c		\
 	gdkkeys.c		\
 	gdkkeyuni.c		\
diff --git a/gdk/gdk.h b/gdk/gdk.h
index d834bb1..8d85b8e 100644
--- a/gdk/gdk.h
+++ b/gdk/gdk.h
@@ -46,6 +46,7 @@
 #include <gdk/gdkkeys.h>
 #include <gdk/gdkpango.h>
 #include <gdk/gdkpixbuf.h>
+#include <gdk/gdkicctransform.h>
 #include <gdk/gdkpixmap.h>
 #include <gdk/gdkproperty.h>
 #include <gdk/gdkregion.h>
diff --git a/gdk/gdkicctransform.c b/gdk/gdkicctransform.c
new file mode 100644
index 0000000..28312ca
--- /dev/null
+++ b/gdk/gdkicctransform.c
@@ -0,0 +1,468 @@
+/* GdkPixbuf library - Converts %GdkPixbuf's from one colorspace to another, and applies ICC input
+ * and output profiles if required.
+ *
+ * Copyright (C) 2010 Richard Hughes <richard@hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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.
+ */
+
+/**
+ * SECTION:gdk-icc-transform
+ * @short_description: Converts %GdkPixbuf's from one colorspace to another
+ *
+ * Converts %GdkPixbuf's from one colorspace to another, and applies ICC input
+ * and output profiles if required. Extensions are automatically loaded at
+ * startup, but none are shipped by default.
+ */
+
+#include "config.h"
+#include "gdkpixbuf.h"
+
+#include "gdkicctransform.h"
+
+static void gdk_icc_transform_finalize (GObject * object);
+
+#define GDK_ICC_TRANSFORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDK_TYPE_ICC_TRANSFORM, GdkIccTransformPrivate))
+
+/**
+ * GdkIccTransformPrivate:
+ *
+ * Private #GdkIccTransform data
+ **/
+struct _GdkIccTransformPrivate
+{
+  GdkIccIntent intent;
+  gboolean      black_point_compensation;
+  gpointer      profile_in;
+  gpointer      profile_out;
+};
+
+enum
+{
+  PROP_0,
+  PROP_INTENT,
+  PROP_BLACK_POINT_COMPENSATION,
+  PROP_LAST
+};
+
+G_DEFINE_TYPE (GdkIccTransform, gdk_icc_transform, G_TYPE_OBJECT)
+
+/**
+ * gdk_icc_transform_profile_open:
+ **/
+static gpointer
+gdk_icc_transform_profile_open (GdkIccTransform *icc_transform,
+                                const guint8    *data,
+                                gsize            length,
+                                GError         **error)
+{
+  GdkIccTransformClass *klass = GDK_ICC_TRANSFORM_GET_CLASS (icc_transform);
+
+  g_return_val_if_fail (klass->profile_open != NULL, NULL);
+
+  /* FIXME: check header bytes to ensure this is an ICC profile */
+
+  /* proxy */
+  return klass->profile_open (icc_transform, data, length, error);
+}
+
+/**
+ * gdk_icc_transform_profile_close:
+ **/
+static gboolean
+gdk_icc_transform_profile_close (GdkIccTransform *icc_transform,
+                                 gpointer         profile,
+                                 GError         **error)
+{
+  GdkIccTransformClass *klass = GDK_ICC_TRANSFORM_GET_CLASS (icc_transform);
+
+  g_return_val_if_fail (profile != NULL, FALSE);
+  g_return_val_if_fail (klass->profile_close != NULL, FALSE);
+
+  /* proxy */
+  return klass->profile_close (icc_transform, profile, error);
+}
+
+/**
+ * gdk_icc_transform_clear_input:
+ * @icc_transform: a %GdkIccTransform
+ * @error: a %GError, or %NULL
+ *
+ * Clears the input profile, which for most implementations will use an internal
+ * sRGB profile.
+ *
+ * Return value: %TRUE if the profile was cleared, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_clear_input (GdkIccTransform *icc_transform,
+                               GError         **error)
+{
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  gboolean ret = TRUE;
+
+  g_return_val_if_fail (GDK_IS_ICC_TRANSFORM (icc_transform), FALSE);
+
+  /* no old profile */
+  if (priv->profile_in == NULL)
+    goto out;
+
+  /* close old profile */
+  ret = gdk_icc_transform_profile_close (icc_transform, priv->profile_in, error);
+  if (!ret)
+    goto out;
+  priv->profile_in = NULL;
+out:
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_clear_output:
+ * @icc_transform: a %GdkIccTransform
+ * @error: a %GError, or %NULL
+ *
+ * Clears the output profile, which for most implementations will use an internal
+ * sRGB profile.
+ *
+ * Return value: %TRUE if the profile was cleared, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_clear_output (GdkIccTransform *icc_transform,
+                                GError         **error)
+{
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  gboolean ret = TRUE;
+
+  g_return_val_if_fail (GDK_IS_ICC_TRANSFORM (icc_transform), FALSE);
+
+  /* no old profile */
+  if (priv->profile_out == NULL)
+    goto out;
+
+  /* close old profile */
+  ret = gdk_icc_transform_profile_close (icc_transform,
+                                         priv->profile_out,
+                                         error);
+  if (!ret)
+    goto out;
+  priv->profile_out = NULL;
+out:
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_set_input_profile:
+ * @icc_transform: a %GdkIccTransform
+ * @data: the ICC profile data
+ * @length: the ICC profile data
+ * @error: a %GError, or %NULL
+ *
+ * Loads an ICC file as an input profile.
+ *
+ * Return value: %TRUE if the profile was opened, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_set_input_profile (GdkIccTransform *icc_transform,
+                                     const guint8    *data,
+                                     gsize            length,
+                                     GError         **error)
+{
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  gboolean ret;
+
+  g_return_val_if_fail (GDK_IS_ICC_TRANSFORM (icc_transform), FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  /* close old profile */
+  ret = gdk_icc_transform_clear_input (icc_transform, error);
+  if (!ret)
+    goto out;
+
+  /* open new profile */
+  priv->profile_in = gdk_icc_transform_profile_open (icc_transform, data, length, error);
+  ret = (priv->profile_in != NULL);
+out:
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_set_output_profile:
+ * @icc_transform: a %GdkIccTransform
+ * @data: the ICC profile data
+ * @length: the ICC profile data
+ * @error: a %GError, or %NULL
+ *
+ * Loads an ICC file as an output profile.
+ *
+ * Return value: %TRUE if the profile was opened, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_set_output_profile (GdkIccTransform *icc_transform,
+                                      const guint8    *data,
+                                      gsize            length,
+                                      GError         **error)
+{
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  gboolean ret;
+
+  g_return_val_if_fail (GDK_IS_ICC_TRANSFORM (icc_transform), FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  /* close old profile */
+  ret = gdk_icc_transform_clear_output (icc_transform, error);
+  if (!ret)
+    goto out;
+
+  /* open new profile */
+  priv->profile_out = gdk_icc_transform_profile_open (icc_transform, data, length, error);
+  ret = (priv->profile_in != NULL);
+out:
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_apply_in_place:
+ * @icc_transform: a %GdkIccTransform
+ * @pixbuf: a %GdkPixbuf
+ * @error: a %GError, or %NULL
+ *
+ * Transform the pixmap in-place, destroying the old content.
+ *
+ * This function will be faster than gdk_icc_transform_apply(), as a new
+ * pixmap does not have to be allocated, and the conversion can be done
+ * one row at a time.
+ *
+ * If the input profile or output profile have not been set then these are
+ * assumed to be sRGB.
+ *
+ * Return value: %TRUE if the pixbuf was transformed, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_apply_in_place (GdkIccTransform *icc_transform,
+                                  GdkPixbuf       *pixbuf,
+                                  GError         **error)
+{
+  gboolean ret;
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  GdkIccTransformClass *klass = GDK_ICC_TRANSFORM_GET_CLASS (icc_transform);
+
+  g_return_val_if_fail (pixbuf != NULL, FALSE);
+  g_return_val_if_fail (klass->overwrite != NULL, FALSE);
+
+  /* proxy */
+  ret = klass->overwrite (icc_transform, pixbuf,
+                          priv->profile_in, priv->profile_out, error);
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_apply:
+ * @icc_transform: a %GdkIccTransform
+ * @pixbuf: a %GdkPixbuf
+ * @error: a %GError, or %NULL
+ *
+ * Transform from one pixmap to another.
+ *
+ * This function will be slower than gdk_icc_transform_apply_in_place(), as a new
+ * pixmap will have to be allocated by your application.
+ *
+ * If the input profile or output profile have not been set then these are
+ * assumed to be sRGB.
+ *
+ * Return value: %TRUE if the pixbuf was transformed, otherwise %FALSE and @error is set.
+ **/
+gboolean
+gdk_icc_transform_apply (GdkIccTransform *icc_transform,
+                         GdkPixbuf       *pixbuf_in,
+                         GdkPixbuf       *pixbuf_out,
+                         GError         **error)
+{
+  gboolean ret;
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+  GdkIccTransformClass *klass = GDK_ICC_TRANSFORM_GET_CLASS (icc_transform);
+
+  g_return_val_if_fail (pixbuf_in != NULL, FALSE);
+  g_return_val_if_fail (pixbuf_out != NULL, FALSE);
+  g_return_val_if_fail (klass->copy != NULL, FALSE);
+
+  /* proxy */
+  ret = klass->copy (icc_transform, pixbuf_in, pixbuf_out,
+                     priv->profile_in, priv->profile_out, error);
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_get_property:
+ **/
+static void
+gdk_icc_transform_get_property (GObject *object, guint prop_id,
+                                   GValue *value, GParamSpec *pspec)
+{
+  GdkIccTransform *icc_transform = GDK_ICC_TRANSFORM (object);
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+
+  switch (prop_id)
+    {
+    case PROP_INTENT:
+      g_value_set_uint (value, priv->intent);
+      break;
+    case PROP_BLACK_POINT_COMPENSATION:
+      g_value_set_boolean (value, priv->black_point_compensation);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gdk_icc_transform_set_property:
+ **/
+static void
+gdk_icc_transform_set_property (GObject *object, guint prop_id,
+                                   const GValue *value, GParamSpec *pspec)
+{
+  GdkIccTransform *icc_transform = GDK_ICC_TRANSFORM (object);
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+
+  switch (prop_id)
+    {
+    case PROP_INTENT:
+      priv->intent = g_value_get_uint (value);
+      break;
+    case PROP_BLACK_POINT_COMPENSATION:
+      priv->black_point_compensation = g_value_get_boolean (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/**
+ * gdk_icc_transform_class_init:
+ **/
+static void
+gdk_icc_transform_class_init (GdkIccTransformClass *klass)
+{
+  GParamSpec *pspec;
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize = gdk_icc_transform_finalize;
+  object_class->get_property = gdk_icc_transform_get_property;
+  object_class->set_property = gdk_icc_transform_set_property;
+
+  /**
+    * GdkIccTransform:intent:
+    */
+  pspec = g_param_spec_uint ("intent", "rendering intent", NULL,
+                             0, G_MAXUINT, 0, G_PARAM_READWRITE);
+  g_object_class_install_property (object_class, PROP_INTENT, pspec);
+
+  /**
+   * GdkIccTransform:black-point-compensation:
+   */
+  pspec = g_param_spec_boolean ("black-point-compensation", "black point compensation", NULL,
+                                FALSE, G_PARAM_READWRITE);
+  g_object_class_install_property (object_class,
+                                   PROP_BLACK_POINT_COMPENSATION, pspec);
+
+  g_type_class_add_private (klass, sizeof (GdkIccTransformPrivate));
+}
+
+/**
+ * gdk_icc_transform_init:
+ **/
+static void
+gdk_icc_transform_init (GdkIccTransform *icc_transform)
+{
+  icc_transform->priv = GDK_ICC_TRANSFORM_GET_PRIVATE (icc_transform);
+  icc_transform->priv->intent = GDK_ICC_INTENT_PERCEPTUAL;
+  icc_transform->priv->black_point_compensation = FALSE;
+  icc_transform->priv->profile_in = NULL;
+  icc_transform->priv->profile_out = NULL;
+}
+
+/**
+ * gdk_icc_transform_finalize:
+ **/
+static void
+gdk_icc_transform_finalize (GObject *object)
+{
+  GdkIccTransform *icc_transform = GDK_ICC_TRANSFORM (object);
+  GdkIccTransformPrivate *priv = icc_transform->priv;
+
+  /* close profiles if they ever existed */
+  if (priv->profile_in != NULL)
+    gdk_icc_transform_profile_close (icc_transform,
+                                     priv->profile_in,
+                                     NULL);
+  if (priv->profile_out != NULL)
+    gdk_icc_transform_profile_close (icc_transform,
+                                     priv->profile_out,
+                                     NULL);
+
+  G_OBJECT_CLASS (gdk_icc_transform_parent_class)->finalize (object);
+}
+
+/**
+ * gdk_icc_transform_get_default_cb:
+ **/
+static gpointer
+gdk_icc_transform_get_default_cb (gpointer data)
+{
+  GIOExtensionPoint *ep;
+  GList *extensions;
+
+  /* find our extension point */
+  ep = g_io_extension_point_lookup (GDK_ICC_TRANSFORM_EXTENSION_POINT_NAME);
+  if (ep == NULL)
+    goto out;
+
+  /* try what we've got */
+  extensions = g_io_extension_point_get_extensions (ep);
+  if (extensions != NULL)
+    return (gpointer) g_io_extension_get_type (extensions->data);
+out:
+  return (gpointer) G_TYPE_INVALID;
+}
+
+/**
+ * gdk_icc_transform_new:
+ * @error: a %GError, or %NULL
+ *
+ * Return value: a new GdkIccTransform object.
+ **/
+GdkIccTransform *
+gdk_icc_transform_new (GError **error)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  GdkIccTransform *icc_transform = NULL;
+  GType type;
+
+  /* get the type */
+  g_once (&once_init, gdk_icc_transform_get_default_cb, NULL);
+  type = (GType) once_init.retval;
+  if (type != G_TYPE_INVALID)
+    icc_transform = GDK_ICC_TRANSFORM (g_object_new (type, NULL));
+  else
+    g_set_error_literal (error, 1, 0,
+                         "Unable to find default pixbuf transform type");
+  return icc_transform;
+}
+
+#define __GDK_ICC_TRANSFORM_C__
+#include "gdkaliasdef.c"
diff --git a/gdk/gdkicctransform.h b/gdk/gdkicctransform.h
new file mode 100644
index 0000000..3ff3769
--- /dev/null
+++ b/gdk/gdkicctransform.h
@@ -0,0 +1,115 @@
+/* GDK - The GIMP Drawing Kit
+ *
+ * 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.
+ */
+
+#if defined(GTK_DISABLE_SINGLE_INCLUDES) && !defined (__GDK_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdk.h> can be included directly."
+#endif
+
+#ifndef __GDK_ICC_TRANSFORM_H
+#define __GDK_ICC_TRANSFORM_H
+
+#include <glib-object.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_ICC_TRANSFORM               (gdk_icc_transform_get_type ())
+#define GDK_ICC_TRANSFORM(o)                 (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_ICC_TRANSFORM, GdkIccTransform))
+#define GDK_ICC_TRANSFORM_CLASS(k)           (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_ICC_TRANSFORM, GdkIccTransformClass))
+#define GDK_IS_ICC_TRANSFORM(o)              (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_ICC_TRANSFORM))
+#define GDK_IS_ICC_TRANSFORM_CLASS(k)        (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_ICC_TRANSFORM))
+#define GDK_ICC_TRANSFORM_GET_CLASS(o)       (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_ICC_TRANSFORM, GdkIccTransformClass))
+
+typedef struct _GdkIccTransformPrivate       GdkIccTransformPrivate;
+typedef struct _GdkIccTransform              GdkIccTransform;
+typedef struct _GdkIccTransformClass         GdkIccTransformClass;
+
+struct _GdkIccTransform
+{
+  GObject parent;
+  GdkIccTransformPrivate *priv;
+};
+
+typedef enum {
+  GDK_ICC_INTENT_UNKNOWN,
+  GDK_ICC_INTENT_PERCEPTUAL,
+  GDK_ICC_INTENT_RELATIVE_COLORMETRIC,
+  GDK_ICC_INTENT_SATURATION,
+  GDK_ICC_INTENT_ABSOLUTE_COLORMETRIC,
+  GDK_ICC_INTENT_LAST
+} GdkIccIntent;
+
+#define GDK_ICC_TRANSFORM_EXTENSION_POINT_NAME "gdk-icc-transform"
+
+struct _GdkIccTransformClass
+{
+        GObjectClass         parent_class;
+        gboolean         (*overwrite)                           (GdkIccTransform      *icc_transform,
+                                                                 GdkPixbuf            *pixbuf,
+                                                                 gpointer              profile_in,
+                                                                 gpointer              profile_out,
+                                                                 GError              **error);
+        gboolean         (*copy)                                (GdkIccTransform      *icc_transform,
+                                                                 GdkPixbuf            *pixbuf_in,
+                                                                 GdkPixbuf            *pixbuf_out,
+                                                                 gpointer              profile_in,
+                                                                 gpointer              profile_out,
+                                                                 GError              **error);
+        gpointer         (*profile_open)                        (GdkIccTransform      *icc_transform,
+                                                                 const guint8         *profile_data,
+                                                                 gsize                 profile_length,
+                                                                 GError              **error);
+        gboolean         (*profile_close)                       (GdkIccTransform      *icc_transform,
+                                                                 gpointer              profile,
+                                                                 GError               **error);
+        /* padding for future expansion */
+        void (*_gdk_reserved1) (void);
+        void (*_gdk_reserved2) (void);
+        void (*_gdk_reserved3) (void);
+        void (*_gdk_reserved4) (void);
+        void (*_gdk_reserved5) (void);
+};
+
+GType                    gdk_icc_transform_get_type             (void);
+GdkIccTransform         *gdk_icc_transform_new                  (GError              **error);
+gboolean                 gdk_icc_transform_clear_input          (GdkIccTransform      *icc_transform,
+                                                                 GError              **error);
+gboolean                 gdk_icc_transform_set_input_profile    (GdkIccTransform      *icc_transform,
+                                                                 const guint8         *profile_data,
+                                                                 gsize                 profile_length,
+                                                                 GError              **error);
+gboolean                 gdk_icc_transform_clear_output         (GdkIccTransform      *icc_transform,
+                                                                 GError              **error);
+gboolean                 gdk_icc_transform_set_output_profile   (GdkIccTransform      *icc_transform,
+                                                                 const guint8         *profile_data,
+                                                                 gsize                 profile_length,
+                                                                 GError              **error);
+gboolean                 gdk_icc_transform_apply_in_place       (GdkIccTransform      *icc_transform,
+                                                                 GdkPixbuf            *pixbuf,
+                                                                 GError              **error);
+gboolean                 gdk_icc_transform_apply                (GdkIccTransform      *icc_transform,
+                                                                 GdkPixbuf            *pixbuf_in,
+                                                                 GdkPixbuf            *pixbuf_out,
+                                                                 GError              **error);
+
+G_END_DECLS
+
+#endif /* __GDK_ICC_TRANSFORM_H */
+
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index c3d950f..16457df 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -55,6 +55,7 @@ INCLUDES =						\
 	-DGTK_DISABLE_DEPRECATED			\
 	-DGTK_FILE_SYSTEM_ENABLE_UNSUPPORTED		\
 	-DGTK_PRINT_BACKEND_ENABLE_UNSUPPORTED		\
+	-DGTK_GIOMODULE_DIR=\""$(GTK_GIOMODULE_DIR)"\"	\
 	$(GTK_DEBUG_FLAGS)				\
 	$(GTK_DEP_CFLAGS)				\
 	$(gtk_clipboard_dnd_c_sources_CFLAGS)		\
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index b150bf3..30f803c 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -822,6 +822,61 @@ gtk_get_option_group (gboolean open_default_display)
   return group;
 }
 
+G_LOCK_DEFINE_STATIC (registered_extensions);
+
+static void
+_gtk_io_modules_ensure_extension_points_registered (void)
+{
+  static gboolean registered_extensions = FALSE;
+  GIOExtensionPoint *ep;
+
+  G_LOCK (registered_extensions);
+
+  if (!registered_extensions)
+    {
+      registered_extensions = TRUE;
+
+      ep = g_io_extension_point_register (GDK_ICC_TRANSFORM_EXTENSION_POINT_NAME);
+      g_io_extension_point_set_required_type (ep, GDK_TYPE_ICC_TRANSFORM);
+    }
+  G_UNLOCK (registered_extensions);
+}
+
+G_LOCK_DEFINE_STATIC (loaded_dirs);
+
+static void
+_gtk_io_modules_ensure_loaded (void)
+{
+  static gboolean loaded_dirs = FALSE;
+  const gchar *module_path;
+
+  _gtk_io_modules_ensure_extension_points_registered ();
+
+  G_LOCK (loaded_dirs);
+
+  if (!loaded_dirs)
+    {
+      loaded_dirs = TRUE;
+      g_io_modules_scan_all_in_directory (GTK_GIOMODULE_DIR);
+
+      module_path = g_getenv ("GTK_EXTRA_GIOMODULES");
+      if (module_path)
+        {
+          gint i = 0;
+          gchar **paths;
+          paths = g_strsplit (module_path, ":", 0);
+          while (paths[i] != NULL)
+            {
+              g_io_modules_scan_all_in_directory (paths[i]);
+              i++;
+            }
+          g_strfreev (paths);
+        }
+    }
+
+  G_UNLOCK (loaded_dirs);
+}
+
 /**
  * gtk_init_with_args:
  * @argc: a pointer to the number of command line arguments.
@@ -959,6 +1014,8 @@ gtk_init_check (int	 *argc,
   if (!gtk_parse_args (argc, argv))
     return FALSE;
 
+  _gtk_io_modules_ensure_loaded ();
+
   return gdk_display_open_default_libgtk_only () != NULL;
 }
 
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..2347d3b
--- /dev/null
+++ b/modules/giomodules/lcms/Makefile.am
@@ -0,0 +1,28 @@
+include $(top_srcdir)/Makefile.decl
+
+INCLUDES = \
+	-I$(top_srcdir) 				\
+	-I$(top_srcdir)/gdk				\
+	-I$(top_builddir)/gdk				\
+	$(LCMS_CFLAGS)					\
+	$(GTK_DEP_CFLAGS)				\
+	$(GTK_DEBUG_FLAGS)
+
+LDADDS = \
+	$(top_builddir)/gdk/$(gdktargetlib)		\
+	$(GTK_DEP_LIBS)
+
+backenddir = $(libdir)/gtk-2.0/$(GTK_BINARY_VERSION)/giomodules
+
+backend_LTLIBRARIES = libicctransformlcms.la
+
+libicctransformlcms_la_SOURCES =			\
+	gdkicctransformlcms.c
+
+noinst_HEADERS =					\
+	gdkicctransformlcms.h
+
+libicctransformlcms_la_LDFLAGS =  -avoid-version -module $(no_undefined)
+libicctransformlcms_la_LIBADD = $(LDADDS) $(LCMS_LIBS)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/giomodules/lcms/gdkicctransformlcms.c b/modules/giomodules/lcms/gdkicctransformlcms.c
new file mode 100644
index 0000000..8210663
--- /dev/null
+++ b/modules/giomodules/lcms/gdkicctransformlcms.c
@@ -0,0 +1,371 @@
+/* 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 "gdkicctransformlcms.h"
+
+static void     gdk_icc_transform_lcms_finalize  (GObject     *object);
+
+#define GDK_ICC_TRANSFORM_LCMS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDK_TYPE_ICC_TRANSFORM_LCMS, GdkIccTransformLcmsPrivate))
+
+/**
+ * GdkIccTransformLcmsPrivate:
+ *
+ * Private #GdkIccTransformLcms data
+ **/
+struct _GdkIccTransformLcmsPrivate
+{
+  cmsHPROFILE   srgb_profile;
+};
+
+G_DEFINE_DYNAMIC_TYPE (GdkIccTransformLcms, gdk_icc_transform_lcms, GDK_TYPE_ICC_TRANSFORM)
+
+/**
+ * gdk_icc_transform_lcms_get_format:
+ **/
+static DWORD
+gdk_icc_transform_lcms_get_format (GdkPixbuf *pixbuf)
+{
+  guint bits;
+  guint has_alpha;
+
+  /* get data */
+  has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+  bits = gdk_pixbuf_get_bits_per_sample (pixbuf);
+
+  /* no alpha channel */
+  if (G_LIKELY(!has_alpha))
+    {
+      if (bits == 8)
+        return TYPE_RGB_8;
+      if (bits == 16)
+        return TYPE_RGB_16;
+      return 0;
+    }
+
+  /* alpha channel */
+  if (bits == 8)
+    return TYPE_RGBA_8;
+  if (bits == 16)
+    return TYPE_RGBA_16;
+  return 0;
+}
+
+/**
+ * gdk_icc_transform_lcms_get_flags:
+ **/
+static DWORD
+gdk_icc_transform_lcms_get_flags (GdkIccTransform *icc_transform)
+{
+  DWORD flags = 0;
+  gboolean black_point_compensation;
+
+  g_object_get (icc_transform,
+                "black-point-compensation", &black_point_compensation,
+                NULL);
+  if (black_point_compensation)
+    flags &= cmsFLAGS_BLACKPOINTCOMPENSATION;
+  return flags;
+}
+
+/**
+ * gdk_icc_transform_lcms_get_intent:
+ **/
+static gint
+gdk_icc_transform_lcms_get_intent (GdkIccTransform *icc_transform)
+{
+  GdkIccIntent intent;
+
+  g_object_get (icc_transform,
+                "intent", &intent,
+                NULL);
+  if (intent == GDK_ICC_INTENT_PERCEPTUAL)
+    return INTENT_PERCEPTUAL;
+  if (intent == GDK_ICC_INTENT_RELATIVE_COLORMETRIC)
+    return INTENT_RELATIVE_COLORIMETRIC;
+  if (intent == GDK_ICC_INTENT_SATURATION)
+    return INTENT_SATURATION;
+  if (intent == GDK_ICC_INTENT_ABSOLUTE_COLORMETRIC)
+    return INTENT_ABSOLUTE_COLORIMETRIC;
+
+  /* choose a good default */
+  return INTENT_PERCEPTUAL;
+}
+
+/**
+ * gdk_icc_transform_lcms_copy:
+ **/
+static gboolean
+gdk_icc_transform_lcms_copy (GdkIccTransform *icc_transform,
+                             GdkPixbuf       *pixbuf_in,
+                             GdkPixbuf       *pixbuf_out,
+                             gpointer         profile_in,
+                             gpointer         profile_out,
+                             GError         **error)
+{
+  DWORD format;
+  DWORD flags;
+  gint intent;
+  gboolean ret = FALSE;
+  cmsHTRANSFORM transform;
+  gint width, height, rowstride;
+  guchar *p_in;
+  guchar *p_out;
+  gint i;
+  GdkIccTransformLcms *icc_transform_lcms = GDK_ICC_TRANSFORM_LCMS(icc_transform);
+  GdkIccTransformLcmsPrivate *priv = icc_transform_lcms->priv;
+
+  /* check the pixbufs are compatible */
+  if (pixbuf_in != pixbuf_out)
+    {
+      if (gdk_pixbuf_get_colorspace (pixbuf_in) != gdk_pixbuf_get_colorspace (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, colorspace is not the same");
+          goto out;
+        }
+      if (gdk_pixbuf_get_height (pixbuf_in) != gdk_pixbuf_get_height (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, height is not the same");
+          goto out;
+        }
+      if (gdk_pixbuf_get_width (pixbuf_in) != gdk_pixbuf_get_width (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, width is not the same");
+          goto out;
+        }
+      if (gdk_pixbuf_get_rowstride (pixbuf_in) != gdk_pixbuf_get_rowstride (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, rowstride is not the same");
+          goto out;
+        }
+      if (gdk_pixbuf_get_bits_per_sample (pixbuf_in) != gdk_pixbuf_get_bits_per_sample (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, bits_per_sample is not the same");
+          goto out;
+        }
+      if (gdk_pixbuf_get_has_alpha (pixbuf_in) != gdk_pixbuf_get_has_alpha (pixbuf_in))
+        {
+          if (error != NULL)
+            *error = g_error_new (1, 0, "cannot transform, has_alpha is not the same");
+          goto out;
+        }
+    }
+
+  /* CMYK not supported */
+  if (gdk_pixbuf_get_colorspace (pixbuf_in) != GDK_COLORSPACE_RGB)
+    {
+      if (error != NULL)
+        *error = g_error_new (1, 0, "non-RGB not supported");
+      goto out;
+    }
+
+  /* work out the LCMS format flags */
+  flags = gdk_icc_transform_lcms_get_flags (icc_transform);
+  intent = gdk_icc_transform_lcms_get_intent (icc_transform);
+  format = gdk_icc_transform_lcms_get_format (pixbuf_in);
+  if (format == 0)
+    {
+      if (error != NULL)
+        *error = g_error_new (1, 0, "format not supported");
+      goto out;
+    }
+
+  /* no profiles */
+  if (profile_in == NULL && profile_out == NULL && pixbuf_in == pixbuf_out)
+    {
+      /* no need to blit */
+      goto out;
+    }
+
+  /* fall back to sRGB */
+  if (profile_in == NULL)
+    profile_in = priv->srgb_profile;
+  if (profile_out == NULL)
+    profile_out = priv->srgb_profile;
+
+  /* create transform */
+  transform = cmsCreateTransform (profile_in, format, profile_out, format, intent, flags);
+
+  /* process each row */
+  height = gdk_pixbuf_get_height (pixbuf_in);
+  width = gdk_pixbuf_get_width (pixbuf_in);
+  rowstride = gdk_pixbuf_get_rowstride (pixbuf_in);
+  p_in = gdk_pixbuf_get_pixels (pixbuf_in);
+  p_out = gdk_pixbuf_get_pixels (pixbuf_out);
+  for (i=0; i<height; ++i)
+    {
+      cmsDoTransform (transform, p_in, p_out, width);
+      p_in += rowstride;
+      p_out += rowstride;
+    }
+
+  /* success */
+  ret = TRUE;
+
+  /* destroy lcms state */
+  cmsDeleteTransform (transform);
+out:
+  return ret;
+}
+
+/**
+ * gdk_icc_transform_lcms_overwrite:
+ **/
+static gboolean
+gdk_icc_transform_lcms_overwrite (GdkIccTransform *icc_transform,
+                                  GdkPixbuf       *pixbuf,
+                                  gpointer         profile_in,
+                                  gpointer         profile_out,
+                                  GError         **error)
+{
+  return gdk_icc_transform_lcms_copy (icc_transform, pixbuf, pixbuf, profile_in, profile_out, error);
+}
+
+/**
+ * gdk_icc_transform_lcms_profile_close:
+ **/
+static gboolean
+gdk_icc_transform_lcms_profile_close (GdkIccTransform *icc_transform,
+                                      gpointer         profile,
+                                      GError         **error)
+{
+  cmsCloseProfile (profile);
+  return TRUE;
+}
+
+/**
+ * gdk_icc_transform_lcms_profile_open:
+ **/
+static gpointer
+gdk_icc_transform_lcms_profile_open (GdkIccTransform *icc_transform,
+                                     const guint8    *data,
+                                     gsize            length,
+                                     GError         **error)
+{
+  gpointer profile;
+  profile = cmsOpenProfileFromMem ((LPVOID)data, length);
+  return profile;
+}
+
+/**
+ * gdk_icc_transform_lcms_error_cb:
+ **/
+static int
+gdk_icc_transform_lcms_error_cb (gint         error_code,
+                                 const gchar *error_text)
+{
+  g_warning ("LCMS error %i: %s", error_code, error_text);
+  return LCMS_ERRC_WARNING;
+}
+
+/**
+ * gdk_icc_transform_lcms_class_init:
+ **/
+static void
+gdk_icc_transform_lcms_class_init (GdkIccTransformLcmsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  GdkIccTransformClass *parent_class = GDK_ICC_TRANSFORM_CLASS (klass);
+  object_class->finalize = gdk_icc_transform_lcms_finalize;
+
+  parent_class->overwrite = gdk_icc_transform_lcms_overwrite;
+  parent_class->copy = gdk_icc_transform_lcms_copy;
+  parent_class->profile_open = gdk_icc_transform_lcms_profile_open;
+  parent_class->profile_close = gdk_icc_transform_lcms_profile_close;
+
+  g_type_class_add_private (klass, sizeof (GdkIccTransformLcmsPrivate));
+}
+
+/**
+ * gdk_icc_transform_lcms_init:
+ **/
+static void
+gdk_icc_transform_lcms_init (GdkIccTransformLcms *icc_transform_lcms)
+{
+  icc_transform_lcms->priv = GDK_ICC_TRANSFORM_LCMS_GET_PRIVATE (icc_transform_lcms);
+
+  /* setup LCMS */
+  cmsSetErrorHandler (gdk_icc_transform_lcms_error_cb);
+  cmsErrorAction (LCMS_ERROR_SHOW);
+  cmsSetLanguage ("en", "US");
+  icc_transform_lcms->priv->srgb_profile = cmsCreate_sRGBProfile ();
+}
+
+/**
+ * gdk_icc_transform_lcms_finalize:
+ **/
+static void
+gdk_icc_transform_lcms_finalize (GObject *object)
+{
+  GdkIccTransformLcms *icc_transform_lcms = GDK_ICC_TRANSFORM_LCMS (object);
+  GdkIccTransformLcmsPrivate *priv = icc_transform_lcms->priv;
+
+  cmsCloseProfile (priv->srgb_profile);
+
+  G_OBJECT_CLASS (gdk_icc_transform_lcms_parent_class)->finalize (object);
+}
+
+/**
+ * gdk_icc_transform_lcms_class_finalize:
+ **/
+static void
+gdk_icc_transform_lcms_class_finalize (GdkIccTransformLcmsClass *klass)
+{
+}
+
+/**
+ * gdk_icc_transform_lcms_register:
+ **/
+void
+gdk_icc_transform_lcms_register (GIOModule *module)
+{
+  gdk_icc_transform_lcms_register_type (G_TYPE_MODULE (module));
+  g_io_extension_point_implement (GDK_ICC_TRANSFORM_EXTENSION_POINT_NAME,
+                                  GDK_TYPE_ICC_TRANSFORM_LCMS,
+                                  "lcms",
+                                  10);
+}
+
+/**
+ * g_io_module_load:
+ **/
+void
+g_io_module_load (GIOModule *module)
+{
+  gdk_icc_transform_lcms_register (module);
+}
+
+/**
+ * g_io_module_unload:
+ **/
+void
+g_io_module_unload (GIOModule *module)
+{
+}
+
diff --git a/modules/giomodules/lcms/gdkicctransformlcms.h b/modules/giomodules/lcms/gdkicctransformlcms.h
new file mode 100644
index 0000000..37b1b84
--- /dev/null
+++ b/modules/giomodules/lcms/gdkicctransformlcms.h
@@ -0,0 +1,64 @@
+/* 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 __GDK_ICC_TRANSFORM_LCMS_H
+#define __GDK_ICC_TRANSFORM_LCMS_H
+
+#include <glib-object.h>
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_ICC_TRANSFORM_LCMS          (gdk_icc_transform_lcms_get_type ())
+#define GDK_ICC_TRANSFORM_LCMS(o)            (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_ICC_TRANSFORM_LCMS, GdkIccTransformLcms))
+#define GDK_ICC_TRANSFORM_LCMS_CLASS(k)      (G_TYPE_CHECK_CLASS_CAST((k), GDK_TYPE_ICC_TRANSFORM_LCMS, GdkIccTransformLcmsClass))
+#define GDK_IS_ICC_TRANSFORM_LCMS(o)         (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_ICC_TRANSFORM_LCMS))
+#define GDK_IS_ICC_TRANSFORM_LCMS_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE ((k), GDK_TYPE_ICC_TRANSFORM_LCMS))
+#define GDK_ICC_TRANSFORM_LCMS_GET_CLASS(o)  (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_ICC_TRANSFORM_LCMS, GdkIccTransformLcmsClass))
+
+typedef struct _GdkIccTransformLcmsPrivate GdkIccTransformLcmsPrivate;
+typedef struct _GdkIccTransformLcms        GdkIccTransformLcms;
+typedef struct _GdkIccTransformLcmsClass   GdkIccTransformLcmsClass;
+
+struct _GdkIccTransformLcms
+{
+   GdkIccTransform parent;
+   GdkIccTransformLcmsPrivate *priv;
+};
+
+struct _GdkIccTransformLcmsClass
+{
+  GdkIccTransformClass parent_class;
+  /* padding for future expansion */
+  void (*_gdk_reserved1) (void);
+  void (*_gdk_reserved2) (void);
+  void (*_gdk_reserved3) (void);
+  void (*_gdk_reserved4) (void);
+  void (*_gdk_reserved5) (void);
+};
+
+GType      gdk_icc_transform_lcms_get_type    (void);
+void       gdk_icc_transform_lcms_register    (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __GDK_ICC_TRANSFORM_LCMS_H */
+
