[gthumb] use colord to get the monitor color profile



commit 6693627d8eb83e1715c4c41c144326d5b4a45632
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Jun 25 19:30:19 2017 +0200

    use colord to get the monitor color profile
    
    this makes color profiles work on wayland as well.

 configure.ac                                    |   22 +
 extensions/cairo_io/cairo-image-surface-jpeg.c  |    7 +-
 extensions/image_viewer/gth-image-viewer-page.c |  102 +++++-
 extensions/slideshow/actions.c                  |    2 +-
 gthumb/Makefile.am                              |    6 +
 gthumb/gth-browser.c                            |   57 ++--
 gthumb/gth-browser.h                            |    2 +-
 gthumb/gth-color-manager.c                      |  500 +++++++++++++++++++++++
 gthumb/gth-color-manager.h                      |   79 ++++
 gthumb/gth-icc-profile.c                        |  179 ++++++++
 gthumb/gth-icc-profile.h                        |   65 +++
 gthumb/gth-image-loader.c                       |   11 +-
 gthumb/gth-image-loader.h                       |    2 +-
 gthumb/gth-image-preloader.c                    |    8 +-
 gthumb/gth-image-preloader.h                    |    2 +-
 gthumb/gth-image.c                              |  152 ++------
 gthumb/gth-image.h                              |   13 +-
 gthumb/gth-main.c                               |   12 +
 gthumb/gth-main.h                               |    2 +
 gthumb/gtk-utils.c                              |   19 +-
 gthumb/gtk-utils.h                              |    3 +-
 21 files changed, 1062 insertions(+), 183 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 06f0fcc..6807385 100644
--- a/configure.ac
+++ b/configure.ac
@@ -71,6 +71,7 @@ LIBWEBP_REQUIRED=0.2.0
 JSON_GLIB_REQUIRED=0.15.0
 WEBKIT2_REQUIRED=1.10.0
 LCMS2_REQUIRED=2.6
+COLORD_REQUIRED=1.3
 
 dnl ===========================================================================
 
@@ -290,6 +291,26 @@ AM_CONDITIONAL(ENABLE_LCMS2, test "x$enable_lcms2" = xyes)
 
 dnl ===========================================================================
 
+AC_ARG_ENABLE([colord],
+             [AS_HELP_STRING([--disable-colord],[do not compile code that uses the colord library])],,
+             [enable_colord=yes])
+
+if test x$enable_colord = xyes ; then
+       PKG_CHECK_MODULES(COLORD,
+                         colord >= $COLORD_REQUIRED,
+                         [enable_colord=yes],
+                         [enable_colord=no])
+fi
+if test x$enable_colord = xyes ; then
+       AC_DEFINE(HAVE_COLORD, 1, [Define to 1 if colord support is included])
+fi
+
+AC_SUBST(COLORD_LIBS)
+AC_SUBST(COLORD_CFLAGS)
+AM_CONDITIONAL(ENABLE_COLORD, test "x$enable_colord" = xyes)
+
+dnl ===========================================================================
+
 IT_PROG_INTLTOOL([0.50.1])
 GETTEXT_PACKAGE=gthumb
 AC_SUBST([GETTEXT_PACKAGE])
@@ -808,4 +829,5 @@ Configuration:
        SVG support          : ${enable_librsvg}
        WebP support         : ${enable_libwebp}
        LCMS2 support        : ${enable_lcms2}
+       colord support       : ${enable_colord}
 "
diff --git a/extensions/cairo_io/cairo-image-surface-jpeg.c b/extensions/cairo_io/cairo-image-surface-jpeg.c
index 6ea9ef8..d54ff89 100644
--- a/extensions/cairo_io/cairo-image-surface-jpeg.c
+++ b/extensions/cairo_io/cairo-image-surface-jpeg.c
@@ -208,8 +208,11 @@ _cairo_image_surface_create_from_jpeg (GInputStream  *istream,
        else
                orientation = GTH_TRANSFORM_NONE;
 #if HAVE_LCMS2
-       if (jpeg_info.valid & _JPEG_INFO_ICC_PROFILE)
-               gth_image_set_icc_profile (image, cmsOpenProfileFromMem (jpeg_info.icc_data, 
jpeg_info.icc_data_size));
+       if (jpeg_info.valid & _JPEG_INFO_ICC_PROFILE) {
+               GthICCData *profile = gth_icc_data_new (NULL, cmsOpenProfileFromMem (jpeg_info.icc_data, 
jpeg_info.icc_data_size));
+               gth_image_set_icc_profile (image, profile);
+               g_object_unref (profile);
+       }
 #endif
        _jpeg_info_data_dispose (&jpeg_info);
 
diff --git a/extensions/image_viewer/gth-image-viewer-page.c b/extensions/image_viewer/gth-image-viewer-page.c
index 1ec6a6b..83ec2e9 100644
--- a/extensions/image_viewer/gth-image-viewer-page.c
+++ b/extensions/image_viewer/gth-image-viewer-page.c
@@ -203,20 +203,39 @@ _gth_image_preloader_get_requested_size_for_current_image (GthImageViewerPage *s
 }
 
 
+/* -- _gth_image_viewer_page_load_with_preloader -- */
+
+
+typedef struct {
+       GthImageViewerPage  *self;
+       GthFileData         *file_data;
+       int                  requested_size;
+       GCancellable        *cancellable;
+       GAsyncReadyCallback  callback;
+       gpointer             user_data;
+} ProfileData;
+
+
 static void
-_gth_image_viewer_page_load_with_preloader (GthImageViewerPage  *self,
-                                           GthFileData         *file_data,
-                                           int                  requested_size,
-                                           GCancellable        *cancellable,
-                                           GAsyncReadyCallback  callback,
-                                           gpointer             user_data)
+profile_data_free (ProfileData *profile_data)
 {
-       if (self->priv->apply_icc_profile)
-               gth_image_preloader_set_out_profile (self->priv->preloader, gth_browser_get_screen_profile 
(self->priv->browser));
-       else
-               gth_image_preloader_set_out_profile (self->priv->preloader, NULL);
+       _g_object_unref (profile_data->cancellable);
+       _g_object_unref (profile_data->file_data);
+       _g_object_unref (profile_data->self);
+       g_free (profile_data);
+}
+
 
+static void
+_gth_image_viewer_page_load_with_preloader_step2 (GthImageViewerPage  *self,
+                                                 GthFileData         *file_data,
+                                                 int                  requested_size,
+                                                 GCancellable        *cancellable,
+                                                 GAsyncReadyCallback  callback,
+                                                 gpointer             user_data)
+{
        g_object_ref (self);
+
        gth_image_preloader_load (self->priv->preloader,
                                  file_data,
                                  requested_size,
@@ -231,6 +250,69 @@ _gth_image_viewer_page_load_with_preloader (GthImageViewerPage  *self,
 }
 
 
+static void
+profile_ready_cb (GObject      *source_object,
+                  GAsyncResult *res,
+                  gpointer      user_data)
+{
+       ProfileData        *profile_data = user_data;
+       GthImageViewerPage *self = profile_data->self;
+       GthICCData         *profile;
+
+       profile = gth_color_manager_get_profile_finish (GTH_COLOR_MANAGER (source_object), res, NULL);
+       if (profile == NULL)
+               profile = gth_browser_get_monitor_profile (self->priv->browser);
+       gth_image_preloader_set_out_profile (self->priv->preloader, profile);
+
+       _gth_image_viewer_page_load_with_preloader_step2 (profile_data->self,
+                                                         profile_data->file_data,
+                                                         profile_data->requested_size,
+                                                         profile_data->cancellable,
+                                                         profile_data->callback,
+                                                         profile_data->user_data);
+
+       _g_object_unref (profile);
+       profile_data_free (profile_data);
+}
+
+
+static void
+_gth_image_viewer_page_load_with_preloader (GthImageViewerPage  *self,
+                                           GthFileData         *file_data,
+                                           int                  requested_size,
+                                           GCancellable        *cancellable,
+                                           GAsyncReadyCallback  callback,
+                                           gpointer             user_data)
+{
+       if (self->priv->apply_icc_profile) {
+               char *monitor_name = NULL;
+
+               if (_gtk_window_get_monitor_info (GTK_WINDOW (self->priv->browser), NULL, NULL, 
&monitor_name)) {
+                       ProfileData *profile_data;
+
+                       profile_data = g_new (ProfileData, 1);
+                       profile_data->self = g_object_ref (self);
+                       profile_data->file_data = g_object_ref (file_data);
+                       profile_data->requested_size = requested_size;
+                       profile_data->cancellable = _g_object_ref (cancellable);
+                       profile_data->callback = callback;
+                       profile_data->user_data = user_data;
+
+                       gth_color_manager_get_profile_async (gth_main_get_default_color_manager(),
+                                                            monitor_name,
+                                                            cancellable,
+                                                            profile_ready_cb,
+                                                            profile_data);
+
+                       return;
+               }
+       }
+
+       gth_image_preloader_set_out_profile (self->priv->preloader, NULL);
+       _gth_image_viewer_page_load_with_preloader_step2 (self, file_data, requested_size, cancellable, 
callback, user_data);
+}
+
+
 static gboolean
 _gth_image_viewer_page_load_with_preloader_finish (GthImageViewerPage  *self)
 {
diff --git a/extensions/slideshow/actions.c b/extensions/slideshow/actions.c
index 573a9c9..c94b9b5 100644
--- a/extensions/slideshow/actions.c
+++ b/extensions/slideshow/actions.c
@@ -132,7 +132,7 @@ gth_browser_activate_slideshow (GSimpleAction *action,
                GdkRectangle  monitor_geometry;
                int           monitor_num;
 
-               if (_gtk_window_get_monitor_info (GTK_WINDOW (browser), &monitor_geometry, &monitor_num)) {
+               if (_gtk_window_get_monitor_info (GTK_WINDOW (browser), &monitor_geometry, &monitor_num, 
NULL)) {
                        gtk_window_set_default_size (GTK_WINDOW (slideshow), monitor_geometry.width, 
monitor_geometry.height);
                        gtk_window_fullscreen_on_monitor (GTK_WINDOW (slideshow), gtk_window_get_screen 
(GTK_WINDOW (browser)), monitor_num);
                }
diff --git a/gthumb/Makefile.am b/gthumb/Makefile.am
index 9c3f087..4e1ca2a 100644
--- a/gthumb/Makefile.am
+++ b/gthumb/Makefile.am
@@ -35,6 +35,7 @@ PUBLIC_HEADER_FILES =                                         \
        gth-async-task.h                                \
        gth-buffer-data.h                               \
        gth-browser.h                                   \
+       gth-color-manager.h                             \
        gth-color-scale.h                               \
        gth-delete-task.h                               \
        gth-dumb-notebook.h                             \
@@ -62,6 +63,7 @@ PUBLIC_HEADER_FILES =                                         \
        gth-histogram.h                                 \
        gth-histogram-view.h                            \
        gth-hook.h                                      \
+       gth-icc-profile.h                               \
        gth-icon-cache.h                                \
        gth-image.h                                     \
        gth-image-dragger.h                             \
@@ -179,6 +181,7 @@ gthumb_SOURCES =                                    \
        gth-browser.c                                   \
        gth-browser-actions-callbacks.c                 \
        gth-buffer-data.c                               \
+       gth-color-manager.c                             \
        gth-color-scale.c                               \
        gth-delete-task.c                               \
        gth-dumb-notebook.c                             \
@@ -207,6 +210,7 @@ gthumb_SOURCES =                                    \
        gth-histogram.c                                 \
        gth-histogram-view.c                            \
        gth-hook.c                                      \
+       gth-icc-profile.c                               \
        gth-icon-cache.c                                \
        gth-image.c                                     \
        gth-image-dragger.c                             \
@@ -305,6 +309,7 @@ gthumb_LDADD =                                              \
        $(JSON_GLIB_LIBS)                               \
        $(WEBKIT2_LIBS)                                 \
        $(LCMS2_LIBS)                                   \
+       $(COLORD_LIBS)                                  \
        $(NULL)
 
 if RUN_IN_PLACE
@@ -336,6 +341,7 @@ gthumb_CFLAGS =                                                     \
        $(LIBCHAMPLAIN_CFLAGS)                                  \
        $(SMCLIENT_CFLAGS)                                      \
        $(LCMS2_CFLAGS)                                         \
+       $(COLORD_CFLAGS)                                        \
        -DGTHUMB_LOCALEDIR=\"$(localedir)\"                     \
        -DGTHUMB_UI_DIR=\"$(ui_dir)\"                           \
        -DGTHUMB_ICON_DIR=\"$(icon_dir)\"                       \
diff --git a/gthumb/gth-browser.c b/gthumb/gth-browser.c
index 55d99e4..ee343a1 100644
--- a/gthumb/gth-browser.c
+++ b/gthumb/gth-browser.c
@@ -2599,7 +2599,7 @@ gth_browser_finalize (GObject *object)
        g_free (browser->priv->list_attributes);
        _g_object_unref (browser->priv->folder_popup_file_data);
        _g_object_unref (browser->priv->history_menu);
-       gth_icc_profile_free (browser->priv->screen_profile);
+       _g_object_unref (browser->priv->screen_profile);
        gtk_tree_path_free (browser->priv->folder_tree_last_dest_row);
 
        G_OBJECT_CLASS (gth_browser_parent_class)->finalize (object);
@@ -6951,36 +6951,45 @@ gth_browser_apply_editor_changes (GthBrowser *browser)
 }
 
 
-GthICCProfile
-gth_browser_get_screen_profile (GthBrowser *browser)
+GthICCData *
+gth_browser_get_monitor_profile (GthBrowser *browser)
 {
 #if HAVE_LCMS2
        if (browser->priv->screen_profile == NULL) {
-               GdkScreen *screen;
-               char      *atom_name;
-               GdkAtom    type    = GDK_NONE;
-               int        format  = 0;
-               int        nitems  = 0;
-               guchar    *data    = NULL;
+               int monitor_num;
 
-               screen = gtk_widget_get_screen (GTK_WIDGET (browser));
-               if (gdk_screen_get_number (screen) > 0)
-                       atom_name = g_strdup_printf ("_ICC_PROFILE_%d", gdk_screen_get_number (screen));
-               else
-                       atom_name = g_strdup ("_ICC_PROFILE");
+               if (_gtk_window_get_monitor_info (GTK_WINDOW (browser), NULL, &monitor_num, NULL)) {
+                       char      *atom_name;
+                       GdkAtom    type    = GDK_NONE;
+                       int        format  = 0;
+                       int        nitems  = 0;
+                       guchar    *data    = NULL;
 
-               if (gdk_property_get (gdk_screen_get_root_window (screen),
-                                     gdk_atom_intern (atom_name, FALSE),
-                                     GDK_NONE,
-                                     0, 64 * 1024 * 1024, FALSE,
-                                     &type, &format, &nitems, &data) && nitems > 0)
-               {
-                       browser->priv->screen_profile = cmsOpenProfileFromMem (data, nitems);
-                       g_free (data);
-               }
+                       if (monitor_num > 0)
+                               atom_name = g_strdup_printf ("_ICC_PROFILE_%d", monitor_num);
+                       else
+                               atom_name = g_strdup ("_ICC_PROFILE");
 
-               g_free (atom_name);
+                       if (gdk_property_get (gdk_screen_get_root_window (gtk_widget_get_screen (GTK_WIDGET 
(browser))),
+                                             gdk_atom_intern (atom_name, FALSE),
+                                             GDK_NONE,
+                                             0, 64 * 1024 * 1024, FALSE,
+                                             &type, &format, &nitems, &data) && nitems > 0)
+                       {
+                               GthICCProfile profile;
+
+                               profile = cmsOpenProfileFromMem (data, nitems);
+                               if (profile != NULL) {
+                                       char *filename = g_strdup_printf ("icc-profile://%d", monitor_num);
+                                       browser->priv->screen_profile = gth_icc_data_new (filename, profile);
+                                       g_free (filename);
+                               }
 
+                               g_free (data);
+                       }
+
+                       g_free (atom_name);
+               }
        }
 #endif
        return browser->priv->screen_profile;
diff --git a/gthumb/gth-browser.h b/gthumb/gth-browser.h
index 9f76b1a..68d60cd 100644
--- a/gthumb/gth-browser.h
+++ b/gthumb/gth-browser.h
@@ -272,7 +272,7 @@ void             gth_browser_ask_whether_to_save    (GthBrowser       *browser,
 void             gth_browser_save_state             (GthBrowser       *browser);
 gboolean         gth_browser_restore_state          (GthBrowser       *browser);
 void            gth_browser_apply_editor_changes   (GthBrowser       *browser);
-GthICCProfile    gth_browser_get_screen_profile     (GthBrowser              *browser);
+GthICCData *     gth_browser_get_monitor_profile    (GthBrowser              *browser);
 GtkWidget *      gth_browser_get_fullscreen_headerbar
                                                    (GthBrowser       *browser);
 void             gth_browser_keep_mouse_visible     (GthBrowser              *browser,
diff --git a/gthumb/gth-color-manager.c b/gthumb/gth-color-manager.c
new file mode 100644
index 0000000..078aa77
--- /dev/null
+++ b/gthumb/gth-color-manager.c
@@ -0,0 +1,500 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#ifdef HAVE_LCMS2
+#include <lcms2.h>
+#endif /* HAVE_LCMS2 */
+#ifdef HAVE_COLORD
+#include <colord.h>
+#endif /* HAVE_COLORD */
+#include "gio-utils.h"
+#include "glib-utils.h"
+#include "gth-color-manager.h"
+
+
+typedef struct {
+       GthICCData      *in_profile;
+       GthICCData      *out_profile;
+       GthICCTransform  transform;
+} TransformData;
+
+
+static TransformData *
+transform_data_new (void)
+{
+       TransformData *data;
+
+       data = g_new (TransformData, 1);
+       data->in_profile = NULL;
+       data->out_profile = NULL;
+       data->transform = NULL;
+
+       return data;
+}
+
+
+static void
+transform_data_free (TransformData *data)
+{
+       _g_object_unref (data->in_profile);
+       _g_object_unref (data->out_profile);
+       gth_icc_transform_free (data->transform);
+       g_free (data);
+}
+
+
+/* Signals */
+enum {
+        CHANGED,
+        LAST_SIGNAL
+};
+
+
+struct _GthColorManagerPrivate {
+       GHashTable *profile_cache;
+       GHashTable *transform_cache;
+#if HAVE_COLORD
+       CdClient   *cd_client;
+#else
+       GObject    *cd_client;
+#endif
+};
+
+
+static guint gth_color_manager_signals[LAST_SIGNAL] = { 0 };
+
+
+G_DEFINE_TYPE (GthColorManager, gth_color_manager, G_TYPE_OBJECT)
+
+
+static void
+gth_color_manager_finalize (GObject *object)
+{
+       GthColorManager *self;
+
+       self = GTH_COLOR_MANAGER (object);
+
+       g_hash_table_unref (self->priv->profile_cache);
+       g_hash_table_unref (self->priv->transform_cache);
+       _g_object_unref (self->priv->cd_client);
+
+       G_OBJECT_CLASS (gth_color_manager_parent_class)->finalize (object);
+}
+
+
+static void
+gth_color_manager_class_init (GthColorManagerClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (GthColorManagerPrivate));
+
+       object_class = (GObjectClass*) klass;
+       object_class->finalize = gth_color_manager_finalize;
+
+       /* signals */
+
+       gth_color_manager_signals[CHANGED] =
+                g_signal_new ("changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GthColorManagerClass, changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+}
+
+
+static void
+gth_color_manager_init (GthColorManager *self)
+{
+       self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_COLOR_MANAGER, GthColorManagerPrivate);
+       self->priv->profile_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+       self->priv->transform_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, 
(GDestroyNotify) transform_data_free);
+       self->priv->cd_client = NULL;
+}
+
+
+GthColorManager *
+gth_color_manager_new (void)
+{
+       return (GthColorManager*) g_object_new (GTH_TYPE_COLOR_MANAGER, NULL);
+}
+
+
+#if HAVE_LCMS2 && HAVE_COLORD
+
+
+typedef struct {
+       GthColorManager *color_manager;
+       char            *monitor_name;
+       char            *cache_id;
+       GFile           *icc_file;
+} ProfilesData;
+
+
+static void
+profiles_data_free (ProfilesData *data)
+{
+       _g_object_unref (data->color_manager);
+       _g_object_unref (data->icc_file);
+       g_free (data->monitor_name);
+       g_free (data->cache_id);
+       g_slice_free (ProfilesData, data);
+}
+
+
+static void
+profile_buffer_ready_cb (void     **buffer,
+                        gsize      count,
+                        GError    *error,
+                        gpointer   user_data)
+{
+       GTask *task = user_data;
+
+       if (error != NULL) {
+               g_task_return_error (task, g_error_copy (error));
+       }
+       else {
+               ProfilesData *data = g_task_get_task_data (task);
+               char         *filename;
+               GthICCData   *icc_data;
+
+               filename = g_file_get_path (data->icc_file);
+               icc_data = gth_icc_data_new (filename, (GthICCProfile) cmsOpenProfileFromMem (*buffer, 
count));
+               gth_color_manager_add_profile (data->color_manager, data->cache_id, icc_data);
+               g_task_return_pointer (task, g_object_ref (icc_data), g_object_unref);
+
+               g_object_unref (icc_data);
+               g_free (filename);
+       }
+
+       g_object_unref (task);
+}
+
+
+static void
+cd_profile_connected_cb (GObject      *source_object,
+                        GAsyncResult *res,
+                        gpointer      user_data)
+{
+       CdProfile    *profile = CD_PROFILE (source_object);
+       GTask        *task = user_data;
+       ProfilesData *data = g_task_get_task_data (task);
+
+       if (! cd_profile_connect_finish (profile, res, NULL)) {
+               g_task_return_pointer (task, NULL, NULL);
+               g_object_unref (task);
+       }
+       else {
+               data->icc_file = g_file_new_for_path (cd_profile_get_filename (profile));
+               _g_file_load_async (data->icc_file,
+                                   G_PRIORITY_DEFAULT,
+                                   g_task_get_cancellable (task),
+                                   profile_buffer_ready_cb,
+                                   task);
+       }
+
+       g_object_unref (profile);
+}
+
+
+static void
+cd_device_connected_cb (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+       CdDevice *device = CD_DEVICE (source_object);
+       GTask    *task = user_data;
+
+       if (! cd_device_connect_finish (device, res, NULL)) {
+               g_task_return_pointer (task, NULL, NULL);
+               g_object_unref (task);
+       }
+       else {
+               CdProfile *profile = cd_device_get_default_profile (device);
+               if (profile != NULL) {
+                       cd_profile_connect (profile,
+                                           g_task_get_cancellable (task),
+                                           cd_profile_connected_cb,
+                                           task);
+               }
+               else {
+                       g_task_return_pointer (task, NULL, NULL);
+                       g_object_unref (task);
+               }
+       }
+
+       g_object_unref (device);
+}
+
+
+static void
+find_device_by_property_cb (GObject      *source_object,
+                           GAsyncResult *res,
+                           gpointer      user_data)
+{
+       GTask    *task = user_data;
+       CdDevice *device;
+
+       device = cd_client_find_device_by_property_finish (CD_CLIENT (source_object), res, NULL);
+       if (device == NULL) {
+               g_task_return_pointer (task, NULL, NULL);
+               g_object_unref (task);
+               return;
+       }
+
+       cd_device_connect (device,
+                          g_task_get_cancellable (task),
+                          cd_device_connected_cb,
+                          task);
+}
+
+
+static void
+_cd_client_find_device_for_task (CdClient *cd_client,
+                                GTask    *task)
+{
+       ProfilesData *data = g_task_get_task_data (task);
+
+       cd_client_find_device_by_property (cd_client,
+                                          CD_DEVICE_METADATA_XRANDR_NAME,
+                                          data->monitor_name,
+                                          g_task_get_cancellable (task),
+                                          find_device_by_property_cb,
+                                          task);
+}
+
+
+static void
+cd_client_connected_cb (GObject      *source_object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+       CdClient *cd_client = CD_CLIENT (source_object);
+       GTask    *task = user_data;
+
+       if (! cd_client_connect_finish (cd_client, res, NULL)) {
+               g_task_return_pointer (task, NULL, NULL);
+               g_object_unref (task);
+               return;
+       }
+
+       _cd_client_find_device_for_task (cd_client, task);
+}
+
+
+#endif /* HAVE_LCMS2 && HAVE_COLORD */
+
+
+void
+gth_color_manager_get_profile_async (GthColorManager    *self,
+                                    char                *monitor_name,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+       GTask *task;
+
+       task = g_task_new (self, cancellable, callback, user_data);
+
+#if HAVE_LCMS2
+
+       {
+               char         *id;
+               GthICCData   *profile;
+               ProfilesData *data;
+
+               id = g_strdup_printf ("monitor://%s", monitor_name);
+               profile = gth_color_manager_get_profile (self, id);
+               if (profile != NULL) {
+                       g_task_return_pointer (task, g_object_ref (profile), g_object_unref);
+                       g_object_unref (task);
+                       g_object_unref (profile);
+                       g_free (id);
+                       return;
+               }
+
+#if HAVE_COLORD
+
+               {
+                       data = g_slice_new (ProfilesData);
+                       data->color_manager = g_object_ref (self);
+                       data->monitor_name = g_strdup (monitor_name);
+                       data->cache_id = g_strdup (id);
+                       data->icc_file = NULL;
+                       g_task_set_task_data (task, data, (GDestroyNotify) profiles_data_free);
+
+                       if (self->priv->cd_client == NULL)
+                               self->priv->cd_client = cd_client_new ();
+
+                       if (! cd_client_get_connected (self->priv->cd_client))
+                               cd_client_connect (self->priv->cd_client,
+                                                  g_task_get_cancellable (task),
+                                                  cd_client_connected_cb,
+                                                  task);
+                       else
+                               _cd_client_find_device_for_task (self->priv->cd_client, task);
+
+                       g_free (id);
+                       return;
+               }
+
+#endif /* HAVE_COLORD */
+
+               g_free (id);
+       }
+
+#endif /* HAVE_LCMS2 */
+
+       g_task_return_pointer (task, NULL, NULL);
+       g_object_unref (task);
+}
+
+
+GthICCData *
+gth_color_manager_get_profile_finish (GthColorManager   *self,
+                                     GAsyncResult       *result,
+                                     GError            **error)
+{
+       g_return_val_if_fail (g_task_is_valid (result, self), NULL);
+       return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+
+void
+gth_color_manager_add_profile (GthColorManager  *self,
+                              const char        *id,
+                              GthICCData        *profile)
+{
+       g_return_if_fail (self != NULL);
+       g_return_if_fail (id != NULL);
+       g_return_if_fail (profile != NULL);
+
+       g_hash_table_insert (self->priv->profile_cache, g_strdup (id), g_object_ref (profile));
+}
+
+
+GthICCData *
+gth_color_manager_get_profile (GthColorManager  *self,
+                              const char        *id)
+{
+       GthICCData *profile;
+
+       g_return_val_if_fail (self != NULL, NULL);
+       g_return_val_if_fail (id != NULL, NULL);
+
+       profile = g_hash_table_lookup (self->priv->profile_cache, id);
+       return _g_object_ref (profile);
+}
+
+
+#if HAVE_LCMS2
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN /* BGRA */
+#define _LCMS2_CAIRO_FORMAT TYPE_BGRA_8
+#elif G_BYTE_ORDER == G_BIG_ENDIAN /* ARGB */
+#define _LCMS2_CAIRO_FORMAT TYPE_ARGB_8
+#else
+#define _LCMS2_CAIRO_FORMAT TYPE_ABGR_8
+#endif
+#endif
+
+
+static char *
+create_transform_id (GthICCData *from_profile,
+                    GthICCData *to_profile)
+{
+       const char *from_filename = gth_icc_data_get_filename (from_profile);
+       const char *to_filename = gth_icc_data_get_filename (to_profile);
+
+       if ((g_strcmp0 (from_filename, "") != 0) && (g_strcmp0 (to_filename, "") != 0))
+               return g_strdup_printf ("%s -> %s", from_filename, to_filename);
+       else
+               return NULL;
+}
+
+
+GthICCTransform
+gth_color_manager_create_transform (GthColorManager *self,
+                                   GthICCData      *from_profile,
+                                   GthICCData      *to_profile)
+{
+#if HAVE_LCMS2
+       g_print ("gth_color_manager_create_transform (%p, %p)\n", gth_icc_data_get_profile (from_profile), 
gth_icc_data_get_profile (to_profile));
+
+       return (GthICCTransform) cmsCreateTransform ((cmsHPROFILE) gth_icc_data_get_profile (from_profile),
+                                                    _LCMS2_CAIRO_FORMAT,
+                                                    (cmsHPROFILE) gth_icc_data_get_profile (to_profile),
+                                                    _LCMS2_CAIRO_FORMAT,
+                                                    INTENT_PERCEPTUAL,
+                                                    0);
+#else
+       return NULL;
+#endif
+}
+
+
+GthICCTransform
+gth_color_manager_get_transform (GthColorManager *self,
+                                GthICCData      *from_profile,
+                                GthICCData      *to_profile)
+{
+#if HAVE_LCMS2
+
+       GthICCTransform  transform = NULL;
+       char            *transform_id;
+
+       g_return_val_if_fail (self != NULL, NULL);
+
+       transform_id = create_transform_id (from_profile, to_profile);
+       if (transform_id != NULL) {
+               TransformData *transform_data = g_hash_table_lookup (self->priv->transform_cache, 
transform_id);
+
+               if (transform_data == NULL) {
+                       transform_data = transform_data_new ();
+                       transform_data->in_profile = g_object_ref (from_profile);
+                       transform_data->out_profile = g_object_ref (to_profile);
+                       transform_data->transform = gth_color_manager_create_transform (self, 
transform_data->in_profile, transform_data->out_profile);
+
+                       if (transform_data->transform != NULL) {
+                               g_hash_table_insert (self->priv->transform_cache, g_strdup (transform_id), 
transform_data);
+                       }
+                       else {
+                               transform_data_free (transform_data);
+                               transform_data = NULL;
+                       }
+               }
+
+               if (transform_data != NULL)
+                       transform = transform_data->transform;
+
+               g_free (transform_id);
+       }
+
+       return transform;
+
+#endif
+
+       return NULL;
+}
diff --git a/gthumb/gth-color-manager.h b/gthumb/gth-color-manager.h
new file mode 100644
index 0000000..71a7770
--- /dev/null
+++ b/gthumb/gth-color-manager.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2017 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_COLOR_MANAGER_H
+#define GTH_COLOR_MANAGER_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+#include "gth-icc-profile.h"
+
+G_BEGIN_DECLS
+
+#define GTH_TYPE_COLOR_MANAGER         (gth_color_manager_get_type ())
+#define GTH_COLOR_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_COLOR_MANAGER, 
GthColorManager))
+#define GTH_COLOR_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_COLOR_MANAGER, 
GthColorManagerClass))
+#define GTH_IS_COLOR_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_COLOR_MANAGER))
+#define GTH_IS_COLOR_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_COLOR_MANAGER))
+#define GTH_COLOR_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_COLOR_MANAGER, 
GthColorManagerClass))
+
+typedef struct _GthColorManager         GthColorManager;
+typedef struct _GthColorManagerPrivate  GthColorManagerPrivate;
+typedef struct _GthColorManagerClass    GthColorManagerClass;
+
+struct _GthColorManager {
+       GObject __parent;
+       GthColorManagerPrivate *priv;
+};
+
+struct _GthColorManagerClass {
+       GObjectClass __parent_class;
+
+       /*< signals >*/
+
+       void    (*changed)      (GthColorManager *color_manager);
+};
+
+GType                  gth_color_manager_get_type              (void) G_GNUC_CONST;
+GthColorManager *      gth_color_manager_new                   (void);
+void                   gth_color_manager_get_profile_async     (GthColorManager         *color_manager,
+                                                                char                    *monitor_name,
+                                                                GCancellable            *cancellable,
+                                                                GAsyncReadyCallback      callback,
+                                                                gpointer                 user_data);
+GthICCData *           gth_color_manager_get_profile_finish    (GthColorManager         *color_manager,
+                                                                GAsyncResult            *result,
+                                                                GError                 **error);
+void                   gth_color_manager_add_profile           (GthColorManager         *color_manager,
+                                                                const char              *id,
+                                                                GthICCData              *profile);
+GthICCData *           gth_color_manager_get_profile           (GthColorManager         *color_manager,
+                                                                const char              *id);
+GthICCTransform                gth_color_manager_create_transform      (GthColorManager         *self,
+                                                                GthICCData              *from_profile,
+                                                                GthICCData              *to_profile);
+GthICCTransform         gth_color_manager_get_transform         (GthColorManager        *color_manager,
+                                                                GthICCData              *from_profile,
+                                                                GthICCData              *to_profile);
+
+G_END_DECLS
+
+#endif /* GTH_COLOR_MANAGER_H */
diff --git a/gthumb/gth-icc-profile.c b/gthumb/gth-icc-profile.c
new file mode 100644
index 0000000..35af682
--- /dev/null
+++ b/gthumb/gth-icc-profile.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2017 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-object.h>
+#ifdef HAVE_LCMS2
+#include <lcms2.h>
+#endif /* HAVE_LCMS2 */
+#include "gth-icc-profile.h"
+
+
+struct _GthICCDataPrivate {
+       GthICCProfile icc_profile;
+       char *filename;
+};
+
+
+G_DEFINE_TYPE (GthICCData, gth_icc_data, G_TYPE_OBJECT)
+
+
+static void
+gth_icc_data_finalize (GObject *object)
+{
+       GthICCData *icc_data;
+
+       g_return_if_fail (object != NULL);
+       g_return_if_fail (GTH_IS_ICC_DATA (object));
+
+       icc_data = GTH_ICC_DATA (object);
+       gth_icc_profile_free (icc_data->priv->icc_profile);
+       g_free (icc_data->priv->filename);
+
+       /* Chain up */
+       G_OBJECT_CLASS (gth_icc_data_parent_class)->finalize (object);
+}
+
+
+static void
+gth_icc_data_class_init (GthICCDataClass *klass)
+{
+       GObjectClass *gobject_class;
+
+       g_type_class_add_private (klass, sizeof (GthICCDataPrivate));
+
+       gobject_class = (GObjectClass*) klass;
+       gobject_class->finalize = gth_icc_data_finalize;
+}
+
+
+static void
+gth_icc_data_init (GthICCData *self)
+{
+       self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GTH_TYPE_ICC_DATA, GthICCDataPrivate);
+       self->priv->icc_profile = NULL;
+       self->priv->filename = NULL;
+}
+
+
+void
+gth_icc_profile_free (GthICCProfile icc_profile)
+{
+#ifdef HAVE_LCMS2
+       if (icc_profile != NULL)
+               cmsCloseProfile ((cmsHPROFILE) icc_profile);
+#endif
+}
+
+
+static char *
+gth_icc_profile_get_id (GthICCProfile icc_profile)
+{
+       GString *str;
+
+       str = g_string_new ("");
+
+#ifdef HAVE_LCMS2
+       {
+               /* taken from colord:
+                * Copyright (C) 2013 Richard Hughes <richard hughsie com>
+                * Licensed under the GNU Lesser General Public License Version 2.1
+                * */
+               cmsUInt8Number  icc_id[16];
+               gboolean        md5_precooked = FALSE;
+               gchar          *md5 = NULL;
+               guint           i;
+
+               cmsGetHeaderProfileID ((cmsHPROFILE) icc_profile, icc_id);
+               for (i = 0; i < 16; i++) {
+                       if (icc_id[i] != 0) {
+                               md5_precooked = TRUE;
+                               break;
+                       }
+               }
+               if (md5_precooked) {
+                       /* convert to a hex string */
+                       md5 = g_new0 (gchar, 32 + 1);
+                       for (i = 0; i < 16; i++)
+                               g_snprintf (md5 + i * 2, 3, "%02x", icc_id[i]);
+               }
+
+               if (md5 != NULL) {
+                       g_string_append (str, "md5://");
+                       g_string_append (str, md5);
+               }
+       }
+#endif
+
+       return g_string_free (str, FALSE);
+}
+
+void
+gth_icc_transform_free (GthICCTransform transform)
+{
+#ifdef HAVE_LCMS2
+       if (transform != NULL)
+               cmsDeleteTransform ((cmsHTRANSFORM) transform);
+#endif
+}
+
+
+GthICCData *
+gth_icc_data_new (const char    *filename,
+                 GthICCProfile  profile)
+{
+       GthICCData *icc_data;
+
+       icc_data = g_object_new (GTH_TYPE_ICC_DATA, NULL);
+       if (filename != NULL)
+               icc_data->priv->filename = g_strdup (filename);
+       else
+               icc_data->priv->filename = gth_icc_profile_get_id (profile);
+       icc_data->priv->icc_profile = profile;
+
+       return icc_data;
+}
+
+
+const char *
+gth_icc_data_get_filename (GthICCData *self)
+{
+       g_return_val_if_fail (self != NULL, NULL);
+       return self->priv->filename;
+}
+
+
+GthICCProfile
+gth_icc_data_get_profile (GthICCData *self)
+{
+       g_return_val_if_fail (self != NULL, NULL);
+       return self->priv->icc_profile;
+}
+
+
+gboolean
+gth_icc_data_equal (GthICCData *a,
+                   GthICCData *b)
+{
+       g_return_val_if_fail ((a == NULL) || (b == NULL), NULL);
+       return g_strcmp0 (a->priv->filename, b->priv->filename) == 0;
+}
diff --git a/gthumb/gth-icc-profile.h b/gthumb/gth-icc-profile.h
new file mode 100644
index 0000000..9562423
--- /dev/null
+++ b/gthumb/gth-icc-profile.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8;   indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2017 The Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTH_ICC_PROFILE_H
+#define GTH_ICC_PROFILE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef gpointer GthICCProfile;
+typedef gpointer GthICCTransform;
+
+#define GTH_TYPE_ICC_DATA            (gth_icc_data_get_type ())
+#define GTH_ICC_DATA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_ICC_DATA, GthICCData))
+#define GTH_ICC_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_ICC_DATA, GthICCDataClass))
+#define GTH_IS_ICC_DATA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_ICC_DATA))
+#define GTH_IS_ICC_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_ICC_DATA))
+#define GTH_ICC_DATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_ICC_DATA, GthICCDataClass))
+
+typedef struct _GthICCData         GthICCData;
+typedef struct _GthICCDataPrivate  GthICCDataPrivate;
+typedef struct _GthICCDataClass    GthICCDataClass;
+
+struct _GthICCData {
+       GObject __parent;
+       GthICCDataPrivate *priv;
+};
+
+struct _GthICCDataClass {
+       GObjectClass __parent_class;
+};
+
+void           gth_icc_profile_free            (GthICCProfile   icc_profile);
+void           gth_icc_transform_free          (GthICCTransform transform);
+
+GType          gth_icc_data_get_type           (void);
+GthICCData *   gth_icc_data_new                (const char     *filename,
+                                                GthICCProfile   profile);
+const char *   gth_icc_data_get_filename       (GthICCData     *icc_data);
+GthICCProfile  gth_icc_data_get_profile        (GthICCData     *icc_data);
+gboolean       gth_icc_data_equal              (GthICCData     *a,
+                                                GthICCData     *b);
+
+G_END_DECLS
+
+#endif /* GTH_ICC_PROFILE_H */
diff --git a/gthumb/gth-image-loader.c b/gthumb/gth-image-loader.c
index db2fbd0..595dcfa 100644
--- a/gthumb/gth-image-loader.c
+++ b/gthumb/gth-image-loader.c
@@ -33,7 +33,7 @@ struct _GthImageLoaderPrivate {
        gboolean           as_animation;  /* Whether to load the image in a
                                           * GdkPixbufAnimation structure. */
        GthImageFormat     preferred_format;
-       GthICCProfile      out_profile;
+       GthICCData        *out_profile;
        GthImageLoaderFunc loader_func;
        gpointer           loader_data;
 };
@@ -45,6 +45,10 @@ G_DEFINE_TYPE (GthImageLoader, gth_image_loader, G_TYPE_OBJECT)
 static void
 gth_image_loader_finalize (GObject *object)
 {
+       GthImageLoader *self = GTH_IMAGE_LOADER (object);
+
+       _g_object_unref (self->priv->out_profile);
+
        G_OBJECT_CLASS (gth_image_loader_parent_class)->finalize (object);
 }
 
@@ -110,9 +114,12 @@ gth_image_loader_set_preferred_format (GthImageLoader *self,
 
 void
 gth_image_loader_set_out_profile (GthImageLoader *self,
-                                 GthICCProfile   out_profile)
+                                 GthICCData     *out_profile)
 {
        g_return_if_fail (self != NULL);
+
+       _g_object_ref (out_profile);
+       _g_object_unref (self->priv->out_profile);
        self->priv->out_profile = out_profile;
 }
 
diff --git a/gthumb/gth-image-loader.h b/gthumb/gth-image-loader.h
index 187db5b..2877501 100644
--- a/gthumb/gth-image-loader.h
+++ b/gthumb/gth-image-loader.h
@@ -58,7 +58,7 @@ void              gth_image_loader_set_loader_func        (GthImageLoader
 void              gth_image_loader_set_preferred_format   (GthImageLoader       *loader,
                                                           GthImageFormat        preferred_format);
 void              gth_image_loader_set_out_profile        (GthImageLoader       *loader,
-                                                          GthICCProfile         profile);
+                                                          GthICCData           *profile);
 void              gth_image_loader_load                   (GthImageLoader       *loader,
                                                           GthFileData          *file_data,
                                                           int                   requested_size,
diff --git a/gthumb/gth-image-preloader.c b/gthumb/gth-image-preloader.c
index 607bb46..3079f33 100644
--- a/gthumb/gth-image-preloader.c
+++ b/gthumb/gth-image-preloader.c
@@ -78,7 +78,7 @@ struct _GthImagePreloaderPrivate {
        GthImageLoader          *loader;
        GQueue                  *cache;
        guint                    load_next_id;
-       GthICCProfile            out_profile;
+       GthICCData              *out_profile;
 };
 
 
@@ -251,6 +251,7 @@ gth_image_preloader_finalize (GObject *object)
        g_list_free (self->priv->requests);
 
        g_object_unref (self->priv->loader);
+       _g_object_unref (self->priv->out_profile);
        g_queue_free_full (self->priv->cache, (GDestroyNotify) cache_data_unref);
 
        G_OBJECT_CLASS (gth_image_preloader_parent_class)->finalize (object);
@@ -292,9 +293,12 @@ gth_image_preloader_new (void)
 
 void
 gth_image_preloader_set_out_profile (GthImagePreloader *self,
-                                    GthICCProfile      out_profile)
+                                    GthICCData        *out_profile)
 {
        g_return_if_fail (self != NULL);
+
+       _g_object_ref (out_profile);
+       _g_object_unref (self->priv->out_profile);
        self->priv->out_profile = out_profile;
 }
 
diff --git a/gthumb/gth-image-preloader.h b/gthumb/gth-image-preloader.h
index d5de045..aa59ba3 100644
--- a/gthumb/gth-image-preloader.h
+++ b/gthumb/gth-image-preloader.h
@@ -52,7 +52,7 @@ struct _GthImagePreloaderClass {
 GType               gth_image_preloader_get_type                (void) G_GNUC_CONST;
 GthImagePreloader * gth_image_preloader_new                     (void);
 void                gth_image_preloader_set_out_profile                 (GthImagePreloader              
*loader,
-                                                                 GthICCProfile                   profile);
+                                                                 GthICCData                     *profile);
 void                gth_image_preloader_load                    (GthImagePreloader              *self,
                                                                  GthFileData                    *requested,
                                                                  int                             
requested_size,
diff --git a/gthumb/gth-image.c b/gthumb/gth-image.c
index a53cf12..6f8dd82 100644
--- a/gthumb/gth-image.c
+++ b/gthumb/gth-image.c
@@ -30,91 +30,10 @@
 #include "cairo-utils.h"
 #include "glib-utils.h"
 #include "gth-image.h"
+#include "gth-main.h"
 #include "pixbuf-utils.h"
 
 
-/* -- GthICCData -- */
-
-
-#define GTH_TYPE_ICC_DATA            (gth_icc_data_get_type ())
-#define GTH_ICC_DATA(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_ICC_DATA, GthICCData))
-#define GTH_ICC_DATA_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_ICC_DATA, GthICCDataClass))
-#define GTH_IS_ICC_DATA(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTH_TYPE_ICC_DATA))
-#define GTH_IS_ICC_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTH_TYPE_ICC_DATA))
-#define GTH_ICC_DATA_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GTH_TYPE_ICC_DATA, GthICCDataClass))
-
-typedef struct _GthICCData         GthICCData;
-typedef struct _GthICCDataClass    GthICCDataClass;
-
-struct _GthICCData {
-       GObject __parent;
-       GthICCProfile icc_profile;
-};
-
-struct _GthICCDataClass {
-       GObjectClass __parent_class;
-};
-
-
-static GType gth_icc_data_get_type (void);
-
-
-G_DEFINE_TYPE (GthICCData, gth_icc_data, G_TYPE_OBJECT)
-
-
-static void
-gth_icc_data_finalize (GObject *object)
-{
-       GthICCData *icc_data;
-
-       g_return_if_fail (object != NULL);
-       g_return_if_fail (GTH_IS_ICC_DATA (object));
-
-       icc_data = GTH_ICC_DATA (object);
-       gth_icc_profile_free (icc_data->icc_profile);
-
-       /* Chain up */
-       G_OBJECT_CLASS (gth_icc_data_parent_class)->finalize (object);
-}
-
-
-static void
-gth_icc_data_class_init (GthICCDataClass *klass)
-{
-       GObjectClass *gobject_class;
-
-       gobject_class = (GObjectClass*) klass;
-       gobject_class->finalize = gth_icc_data_finalize;
-}
-
-
-static void
-gth_icc_data_init (GthICCData *self)
-{
-       self->icc_profile = NULL;
-}
-
-
-static GthICCData *
-gth_icc_data_new (GthICCProfile profile)
-{
-       GthICCData *icc_data;
-
-       icc_data = g_object_new (GTH_TYPE_ICC_DATA, NULL);
-       icc_data->icc_profile = profile;
-
-       return icc_data;
-}
-
-
-static GthICCProfile
-gth_icc_data_get_profile (GthICCData *self)
-{
-       g_return_val_if_fail (self != NULL, NULL);
-       return self->icc_profile;
-}
-
-
 /* -- GthImage -- */
 
 
@@ -502,55 +421,43 @@ gth_image_get_is_animation (GthImage *image)
 
 
 void
-gth_image_set_icc_profile (GthImage     *image,
-                          GthICCProfile  profile)
+gth_image_set_icc_profile (GthImage   *image,
+                          GthICCData *profile)
 {
+       _g_object_ref (profile);
        _gth_image_free_icc_profile (image);
-       if (profile != NULL)
-               image->priv->icc_data = gth_icc_data_new (profile);
+       image->priv->icc_data = profile;
 }
 
 
-GthICCProfile
+GthICCData *
 gth_image_get_icc_profile (GthImage *image)
 {
        g_return_val_if_fail (image != NULL, NULL);
-       return (image->priv->icc_data != NULL) ? gth_icc_data_get_profile (image->priv->icc_data) : NULL;
+       return image->priv->icc_data;
 }
 
 
 /* -- gth_image_apply_icc_profile -- */
 
 
-#if HAVE_LCMS2
-#if G_BYTE_ORDER == G_LITTLE_ENDIAN /* BGRA */
-#define _LCMS2_CAIRO_FORMAT TYPE_BGRA_8
-#elif G_BYTE_ORDER == G_BIG_ENDIAN /* ARGB */
-#define _LCMS2_CAIRO_FORMAT TYPE_ARGB_8
-#else
-#define _LCMS2_CAIRO_FORMAT TYPE_ABGR_8
-#endif
-#endif
-
-
 void
 gth_image_apply_icc_profile (GthImage      *image,
-                            GthICCProfile  out_profile,
+                            GthICCData    *out_profile,
                             GCancellable  *cancellable)
 {
 #if HAVE_LCMS2
 
-       GthICCProfile    image_profile;
-       cmsHTRANSFORM    hTransform;
        cairo_surface_t *surface;
+       cmsHTRANSFORM    hTransform;
+       gboolean         delete_transform = FALSE;
 
        g_return_if_fail (image != NULL);
 
        if (out_profile == NULL)
                return;
 
-       image_profile = gth_image_get_icc_profile (image);
-       if (image_profile == NULL)
+       if (image->priv->icc_data == NULL)
                return;
 
        if (image->priv->format != GTH_IMAGE_FORMAT_CAIRO_SURFACE)
@@ -560,11 +467,16 @@ gth_image_apply_icc_profile (GthImage      *image,
        if (surface == NULL)
                return;
 
-       hTransform = cmsCreateTransform ((cmsHPROFILE) image_profile,
-                                        _LCMS2_CAIRO_FORMAT,
-                                        (cmsHPROFILE) out_profile,
-                                        _LCMS2_CAIRO_FORMAT,
-                                        INTENT_PERCEPTUAL, 0);
+       hTransform = (cmsHTRANSFORM) gth_color_manager_get_transform (gth_main_get_default_color_manager (),
+                                                                     image->priv->icc_data,
+                                                                     out_profile);
+
+       if (hTransform == NULL) {
+               hTransform = (cmsHTRANSFORM) gth_color_manager_create_transform 
(gth_main_get_default_color_manager (),
+                                                                                image->priv->icc_data,
+                                                                                out_profile);
+               delete_transform = (hTransform != NULL);
+       }
 
        if (hTransform != NULL) {
                unsigned char   *surface_row;
@@ -587,9 +499,10 @@ gth_image_apply_icc_profile (GthImage      *image,
                cairo_surface_mark_dirty (surface);
 
                cairo_surface_destroy (surface);
-               cmsDeleteTransform (hTransform);
        }
 
+       if (delete_transform)
+               cmsDeleteTransform (hTransform);
 #endif
 }
 
@@ -598,8 +511,8 @@ gth_image_apply_icc_profile (GthImage      *image,
 
 
 typedef struct {
-       GthImage        *image;
-       GthICCProfile    out_profile;
+       GthImage   *image;
+       GthICCData *out_profile;
 } ApplyProfileData;
 
 
@@ -609,6 +522,7 @@ apply_profile_data_free (gpointer user_data)
        ApplyProfileData *apd = user_data;
 
        g_object_unref (apd->image);
+       _g_object_unref (apd->out_profile);
        g_free (apd);
 }
 
@@ -636,7 +550,7 @@ _gth_image_apply_icc_profile_thread (GSimpleAsyncResult *result,
 
 void
 gth_image_apply_icc_profile_async (GthImage            *image,
-                                  GthICCProfile         out_profile,
+                                  GthICCData           *out_profile,
                                   GCancellable         *cancellable,
                                   GAsyncReadyCallback   callback,
                                   gpointer              user_data)
@@ -653,7 +567,7 @@ gth_image_apply_icc_profile_async (GthImage         *image,
 
        apd = g_new (ApplyProfileData, 1);
        apd->image = g_object_ref (image);
-       apd->out_profile = out_profile;
+       apd->out_profile = _g_object_ref (out_profile);
        g_simple_async_result_set_op_res_gpointer (result, apd, apply_profile_data_free);
        g_simple_async_result_run_in_thread (result,
                                             _gth_image_apply_icc_profile_thread,
@@ -671,13 +585,3 @@ gth_image_apply_icc_profile_finish (GAsyncResult    *result,
        g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, 
gth_image_apply_icc_profile_async), FALSE);
        return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
 }
-
-
-void
-gth_icc_profile_free (GthICCProfile icc_profile)
-{
-#ifdef HAVE_LCMS2
-       if (icc_profile != NULL)
-               cmsCloseProfile ((cmsHPROFILE) icc_profile);
-#endif
-}
diff --git a/gthumb/gth-image.h b/gthumb/gth-image.h
index c55f9d8..566c123 100644
--- a/gthumb/gth-image.h
+++ b/gthumb/gth-image.h
@@ -28,6 +28,7 @@
 #include <gio/gio.h>
 #include <cairo.h>
 #include "gth-file-data.h"
+#include "gth-icc-profile.h"
 
 G_BEGIN_DECLS
 
@@ -38,8 +39,6 @@ typedef enum {
        GTH_IMAGE_N_FORMATS
 } GthImageFormat;
 
-typedef gpointer GthICCProfile;
-
 #define GTH_TYPE_IMAGE            (gth_image_get_type ())
 #define GTH_IMAGE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTH_TYPE_IMAGE, GthImage))
 #define GTH_IMAGE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GTH_TYPE_IMAGE, GthImageClass))
@@ -104,21 +103,19 @@ void                  gth_image_set_pixbuf_animation        (GthImage
 GdkPixbufAnimation *  gth_image_get_pixbuf_animation        (GthImage           *image);
 gboolean              gth_image_get_is_animation            (GthImage           *image);
 void                 gth_image_set_icc_profile             (GthImage           *image,
-                                                            GthICCProfile       profile);
-GthICCProfile        gth_image_get_icc_profile             (GthImage           *image);
+                                                            GthICCData         *profile);
+GthICCData *         gth_image_get_icc_profile             (GthImage           *image);
 void                 gth_image_apply_icc_profile           (GthImage           *image,
-                                                            GthICCProfile       out_profile,
+                                                            GthICCData         *out_profile,
                                                             GCancellable       *cancellable);
 void                 gth_image_apply_icc_profile_async     (GthImage           *image,
-                                                            GthICCProfile       out_profile,
+                                                            GthICCData         *out_profile,
                                                             GCancellable       *cancellable,
                                                             GAsyncReadyCallback callback,
                                                             gpointer            user_data);
 gboolean             gth_image_apply_icc_profile_finish    (GAsyncResult       *result,
                                                             GError            **error);
 
-void                 gth_icc_profile_free                  (GthICCProfile       icc_profile);
-
 G_END_DECLS
 
 #endif /* GTH_IMAGE_H */
diff --git a/gthumb/gth-main.c b/gthumb/gth-main.c
index 38a5f8f..63569c3 100644
--- a/gthumb/gth-main.c
+++ b/gthumb/gth-main.c
@@ -110,6 +110,7 @@ struct _GthMainPrivate
        GthTagsFile         *tags;
        GthMonitor          *monitor;
        GthExtensionManager *extension_manager;
+       GthColorManager     *color_manager;
 };
 
 
@@ -146,6 +147,7 @@ gth_main_finalize (GObject *object)
 
                _g_object_unref (gth_main->priv->monitor);
                _g_object_unref (gth_main->priv->extension_manager);
+               _g_object_unref (gth_main->priv->color_manager);
                gth_filter_file_free (gth_main->priv->filters);
                gth_tags_file_free (gth_main->priv->tags);
 
@@ -180,6 +182,7 @@ gth_main_init (GthMain *main)
        main->priv->metadata_info = g_ptr_array_new ();
        main->priv->metadata_info_hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
        main->priv->metadata_info_sorted = FALSE;
+       main->priv->extension_manager = gth_extension_manager_new ();
 }
 
 
@@ -1199,6 +1202,15 @@ gth_main_get_default_extension_manager (void)
 }
 
 
+GthColorManager *
+gth_main_get_default_color_manager (void)
+{
+       if (Main->priv->color_manager == NULL)
+               Main->priv->color_manager = gth_color_manager_new ();
+       return Main->priv->color_manager;
+}
+
+
 void
 gth_main_activate_extensions (void)
 {
diff --git a/gthumb/gth-main.h b/gthumb/gth-main.h
index 78f21d1..8ba74d9 100644
--- a/gthumb/gth-main.h
+++ b/gthumb/gth-main.h
@@ -25,6 +25,7 @@
 #include <glib.h>
 #include <glib-object.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
+#include "gth-color-manager.h"
 #include "gth-extensions.h"
 #include "gth-file-data.h"
 #include "gth-file-source.h"
@@ -118,6 +119,7 @@ char **                gth_main_get_all_tags                  (void);
 void                   gth_main_tags_changed                  (void);
 GthMonitor *           gth_main_get_default_monitor           (void);
 GthExtensionManager *  gth_main_get_default_extension_manager (void);
+GthColorManager *      gth_main_get_default_color_manager     (void);
 void                   gth_main_register_default_hooks        (void);
 void                   gth_main_register_default_tests        (void);
 void                   gth_main_register_default_types        (void);
diff --git a/gthumb/gtk-utils.c b/gthumb/gtk-utils.c
index d99e907..d714978 100644
--- a/gthumb/gtk-utils.c
+++ b/gthumb/gtk-utils.c
@@ -1151,9 +1151,10 @@ _gtk_window_get_is_maximized (GtkWindow *window)
 
 
 gboolean
-_gtk_window_get_monitor_info (GtkWindow           *window,
-                             GdkRectangle *geometry,
-                             int          *number)
+_gtk_window_get_monitor_info (GtkWindow            *window,
+                             GdkRectangle  *geometry,
+                             int           *number,
+                             char         **name)
 {
 #if GTK_CHECK_VERSION(3, 22, 0)
 
@@ -1171,9 +1172,10 @@ _gtk_window_get_monitor_info (GtkWindow     *window,
        if (geometry != NULL)
                gdk_monitor_get_geometry (monitor, geometry);
 
-       if (number != NULL) {
+       if ((number != NULL) || (name != NULL)) {
                GdkDisplay *display;
                int         monitor_num;
+               const char *monitor_name;
                int         i;
 
                display = gdk_monitor_get_display (monitor);
@@ -1182,12 +1184,15 @@ _gtk_window_get_monitor_info (GtkWindow    *window,
                        GdkMonitor *m = gdk_display_get_monitor (display, i);
                        if (m == monitor) {
                                monitor_num = i;
+                               monitor_name = gdk_monitor_get_model (monitor);
                                break;
                        }
                        if (m == NULL)
                                break;
                }
-               *number = monitor_num;
+
+               if (number != NULL) *number = monitor_num;
+               if (name != NULL) *name = g_strdup (monitor_name);
        }
 
 #else
@@ -1207,9 +1212,11 @@ _gtk_window_get_monitor_info (GtkWindow     *window,
        monitor_num = gdk_screen_get_monitor_at_window (screen, win);
        if (number != NULL)
                *number = monitor_num;
-
        if (geometry != NULL)
                gdk_screen_get_monitor_geometry (screen, monitor_num, geometry);
+       if (name != NULL)
+               *name = gdk_screen_get_monitor_plug_name (screen, monitor_num);
+
 #endif
 
        return TRUE;
diff --git a/gthumb/gtk-utils.h b/gthumb/gtk-utils.h
index 4ddc0ce..c2431a4 100644
--- a/gthumb/gtk-utils.h
+++ b/gthumb/gtk-utils.h
@@ -152,7 +152,8 @@ void                _gtk_window_add_accelerators_from_menu     (GtkWindow           
*window,
 gboolean       _gtk_window_get_is_maximized               (GtkWindow           *window);
 gboolean        _gtk_window_get_monitor_info               (GtkWindow          *window,
                                                            GdkRectangle        *geometry,
-                                                           int                 *number);
+                                                           int                 *number,
+                                                           char               **name);
 GdkDevice *     _gtk_widget_get_client_pointer            (GtkWidget           *widget);
 void            _gtk_list_box_add_separator               (GtkListBox          *list_box);
 gboolean        _gtk_settings_get_dialogs_use_header       (void);


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