changeset 113:55166d93498d

Fri Nov 24 21:37:01 2006 John Ellis <johne@verizon.net> * configure.in: Add test for lcms (little cms). * Makefile.am: Add color-man.[ch]: * color-man.[ch]: New files for color management support. * globals.c, gqview.h, main.c, rcfile.c, typedefs.h: Add color profile variables and option saving. * image.[ch]: Add color profile functions. * layout.c, layout_image.[ch]: Add color profile icon, popup menu, and fix sort menu to use radio buttons. * menu.c: Use radio buttons for sort menu when appropriate. * preferences.c: Add color profile options to preferences. * ui_menu.[ch]: Add menu_item_add_radio() for radio item menus. * ui_misc.c: Fix gtk_table_attach() arg for vertical expansion. * view_file_icon.c, view_file_list.c: Check for active state in sort menu callbacks. * README: Add info about lcms, and how to disable.
author gqview
date Sat, 25 Nov 2006 03:00:33 +0000
parents b15d4c18168f
children 50fc73e08550
files ChangeLog README TODO configure.in src/Makefile.am src/color-man.c src/color-man.h src/globals.c src/gqview.h src/image.c src/image.h src/layout.c src/layout_image.c src/layout_image.h src/main.c src/menu.c src/preferences.c src/rcfile.c src/typedefs.h src/ui_menu.c src/ui_menu.h src/ui_misc.c src/view_file_icon.c src/view_file_list.c
diffstat 24 files changed, 1199 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Nov 18 00:12:22 2006 +0000
+++ b/ChangeLog	Sat Nov 25 03:00:33 2006 +0000
@@ -1,3 +1,21 @@
+Fri Nov 24 21:37:01 2006  John Ellis  <johne@verizon.net>
+
+	* configure.in: Add test for lcms (little cms).
+	* Makefile.am: Add color-man.[ch]:
+	* color-man.[ch]: New files for color management support.
+	* globals.c, gqview.h, main.c, rcfile.c, typedefs.h: Add color profile
+	variables and option saving.
+	* image.[ch]: Add color profile functions.
+	* layout.c, layout_image.[ch]: Add color profile icon, popup menu, and
+	fix sort menu to use radio buttons.
+	* menu.c: Use radio buttons for sort menu when appropriate.
+	* preferences.c: Add color profile options to preferences.
+	* ui_menu.[ch]: Add menu_item_add_radio() for radio item menus.
+	* ui_misc.c: Fix gtk_table_attach() arg for vertical expansion.
+	* view_file_icon.c, view_file_list.c: Check for active state in sort
+	menu callbacks.
+	* README: Add info about lcms, and how to disable.
+
 Fri Nov 17 19:06:19 2006  John Ellis  <johne@verizon.net>
 
 	* ui_fileops.[ch]: Add path_list_lstat() to obtain a path listing that
--- a/README	Sat Nov 18 00:12:22 2006 +0000
+++ b/README	Sat Nov 25 03:00:33 2006 +0000
@@ -38,7 +38,12 @@
 
 ======== Requirements
 
-  GTK+ 2.4.x: ftp://ftp.gtk.org/pub/gtk
+  Required libraries:
+    GTK+ 2.4.x: ftp://ftp.gtk.org/pub/gtk
+
+  Optional libraries:
+    lcms, for color management support: http://www.littlecms.com
+        (disable with configure option: '--without-lcms')
 
 ======== Notes and changes for this release            [section:release_notes]
 
@@ -81,6 +86,10 @@
 
     Version in perenthesis indicates first appearance of feature or change.
 
+    (2.1.5) Add support for color profiles when lcms is installed. To
+    disable color profiles and use of lcms, run configure
+    with '--without-lcms'.
+
     (2.1.1) Add support for viewing jpeg images and EXIF embedded within
     raw files for Canon (.crw, .cr2) Fujifilm (.raf), and Nikon (.nef).
     Note that not all cameras that support a raw format will necessarily
--- a/TODO	Sat Nov 18 00:12:22 2006 +0000
+++ b/TODO	Sat Nov 25 03:00:33 2006 +0000
@@ -21,6 +21,10 @@
  > cache-load.c:
    > should honor enable_thumbnails setting
 
+ > color profiles:
+   > support profiles embedded in images
+   > add support in img-view.c
+
   ---
 
  >raw + exif formats:
@@ -101,6 +105,7 @@
    > [Control]+V now shows image in new window
    > [Shift]+P print shortcut added to collection and img-view windows.
      (fixme, forgot to add it to find dialog).
+   > add color profile page
 
    > add blurb about moving images between collections with shift+drag
 
@@ -113,6 +118,8 @@
 
 d> update icon used for window to the (not so) new icon
 
+ > fix gtk_table_attach use to not use FALSE for fill vertical arg.
+
  > xv and xpaint are hardly used or even installed by any distro anymore - time
    to remove these (and find alternates?) seems silly to only have gimp.
 
--- a/configure.in	Sat Nov 18 00:12:22 2006 +0000
+++ b/configure.in	Sat Nov 25 03:00:33 2006 +0000
@@ -25,6 +25,33 @@
 dnl checks for functions
 AC_CHECK_FUNCS(strverscmp)
 
+dnl check for little cms (lcms, this test pulled from gimp)
+AC_ARG_WITH(lcms, [  --without-lcms          build without lcms support])
+
+have_lcms=no
+if test "x$with_lcms" != "xno"; then
+  AC_CHECK_LIB(lcms, cmsCreate_sRGBProfile, [
+    AC_CHECK_HEADER(lcms.h,
+      have_lcms=yes, [
+      AC_CHECK_HEADER(lcms/lcms.h,
+        have_lcms=yes
+        AC_DEFINE(HAVE_LCMS_LCMS_H, 1,
+          [Define to 1 if the lcms header must be included as lcms/lcms.h]))
+      ])
+  ])
+  if test "$have_lcms" = "yes"; then
+    LCMS_LIBS="-llcms"
+    AC_DEFINE(HAVE_LCMS, 1, [define to enable use of color profiles with lcms])
+  else
+    have_lcms="no (lcms not found or unusable)"
+  fi
+else
+  have_lcms="no (lcms support disabled)"
+fi
+
+AC_SUBST(LCMS_LIBS)
+AM_CONDITIONAL(HAVE_LCMS, test "$have_lcms" = "yes")
+
 ALL_LINGUAS="ar be bg ca cs da de eo es et eu fi fr hu id it ja ko nl no pl pt_BR ro ru sk sl sv th tr uk vi zh_CN.GB2312 zh_TW"
 GETTEXT_PACKAGE=$PACKAGE
 AC_SUBST(GETTEXT_PACKAGE)
--- a/src/Makefile.am	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/Makefile.am	Sat Nov 25 03:00:33 2006 +0000
@@ -74,6 +74,8 @@
 	collect-io.h	\
 	collect-table.c	\
 	collect-table.h	\
+	color-man.c	\
+	color-man.h	\
 	dnd.c		\
 	dnd.h		\
 	dupe.c		\
@@ -164,7 +166,7 @@
 	view_file_icon.c	\
 	view_file_icon.h
 
-gqview_LDADD = $(GTK_LIBS) $(INTLLIBS)
+gqview_LDADD = $(GTK_LIBS) $(INTLLIBS) $(LCMS_LIBS)
 
 EXTRA_DIST = \
 	$(extra_SLIK)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/color-man.c	Sat Nov 25 03:00:33 2006 +0000
@@ -0,0 +1,403 @@
+/*
+ * GQview
+ * (C) 2006 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#include "gqview.h"
+#include "color-man.h"
+
+#include "image.h"
+#include "ui_fileops.h"
+
+
+#ifdef HAVE_LCMS
+/*** color support enabled ***/
+
+#ifdef HAVE_LCMS_LCMS_H
+  #include <lcms/lcms.h>
+#else
+  #include <lcms.h>
+#endif
+
+
+typedef struct _ColorManCache ColorManCache;
+struct _ColorManCache {
+	cmsHPROFILE   profile_in;
+	cmsHPROFILE   profile_out;
+	cmsHTRANSFORM transform;
+
+	ColorManProfileType profile_in_type;
+	gchar *profile_in_file;
+
+	ColorManProfileType profile_out_type;
+	gchar *profile_out_file;
+
+	gint has_alpha;
+
+	gint refcount;
+};
+
+/* pixels to transform per idle call */
+#define COLOR_MAN_CHUNK_SIZE 81900
+
+
+static void color_man_lib_init(void)
+{
+	static gint init_done = FALSE;
+
+	if (init_done) return;
+	init_done = TRUE;
+
+	cmsErrorAction(LCMS_ERROR_IGNORE);
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * color transform cache
+ *-------------------------------------------------------------------
+ */
+
+static GList *cm_cache_list = NULL;
+
+
+static void color_man_cache_ref(ColorManCache *cc)
+{
+	if (!cc) return;
+
+	cc->refcount++;
+}
+
+static void color_man_cache_unref(ColorManCache *cc)
+{
+	if (!cc) return;
+
+	cc->refcount--;
+	if (cc->refcount < 1)
+		{
+		if (cc->transform) cmsDeleteTransform(cc->transform);
+		if (cc->profile_in) cmsCloseProfile(cc->profile_in);
+		if (cc->profile_out) cmsCloseProfile(cc->profile_out);
+
+		g_free(cc->profile_in_file);
+		g_free(cc->profile_out_file);
+
+		g_free(cc);
+		}
+}
+
+static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file)
+{
+	cmsHPROFILE profile = NULL;
+
+	switch (type)
+		{
+		case COLOR_PROFILE_FILE:
+			if (file)
+				{
+				gchar *pathl;
+
+				pathl = path_from_utf8(file);
+				profile = cmsOpenProfileFromFile(pathl, "r");
+				g_free(pathl);
+				}
+			break;
+		case COLOR_PROFILE_SRGB:
+			profile = cmsCreate_sRGBProfile();
+			break;
+		case COLOR_PROFILE_NONE:
+		default:
+			break;
+		}
+
+	return profile;
+}
+
+static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
+					  ColorManProfileType out_type, const gchar *out_file,
+					  gint has_alpha)
+{
+	ColorManCache *cc;
+
+	color_man_lib_init();
+
+	cc = g_new0(ColorManCache, 1);
+	cc->refcount = 1;
+
+	cc->profile_in_type = in_type;
+	cc->profile_in_file = g_strdup(in_file);
+
+	cc->profile_out_type = out_type;
+	cc->profile_out_file = g_strdup(out_file);
+
+	cc->has_alpha = has_alpha;
+
+	cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file);
+	cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file);
+
+	if (!cc->profile_in || !cc->profile_out)
+		{
+		if (debug) printf("failed to load color profile for %s: %d %s\n",
+				  (!cc->profile_in) ? "input" : "screen",
+				  (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
+				  (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
+
+		color_man_cache_unref(cc);
+		return NULL;
+		}
+
+	cc->transform = cmsCreateTransform(cc->profile_in,
+					   (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
+					   cc->profile_out,
+					   (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
+					   INTENT_PERCEPTUAL, 0);
+
+	if (!cc->transform)
+		{
+		if (debug) printf("failed to create color profile transform\n");
+
+		color_man_cache_unref(cc);
+		return NULL;
+		}
+
+	cm_cache_list = g_list_append(cm_cache_list, cc);
+
+	return cc;
+}
+
+static void color_man_cache_free(ColorManCache *cc)
+{
+	if (!cc) return;
+
+	cm_cache_list = g_list_remove(cm_cache_list, cc);
+	color_man_cache_unref(cc);
+}
+
+static void color_man_cache_reset(void)
+{
+	while (cm_cache_list)
+		{
+		ColorManCache *cc;
+
+		cc = cm_cache_list->data;
+		color_man_cache_free(cc);
+		}
+}
+
+static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
+					   ColorManProfileType out_type, const gchar *out_file,
+					   gint has_alpha)
+{
+	GList *work;
+
+	work = cm_cache_list;
+	while (work)
+		{
+		ColorManCache *cc;
+		gint match = FALSE;
+
+		cc = work->data;
+		work = work->next;
+
+		if (cc->profile_in_type == in_type &&
+		    cc->profile_out_type == out_type &&
+		    cc->has_alpha == has_alpha)
+			{
+			match = TRUE;
+			}
+
+		if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
+			{
+			match = (cc->profile_in_file && in_file &&
+				 strcmp(cc->profile_in_file, in_file) == 0);
+			}
+		if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
+			{
+			match = (cc->profile_out_file && out_file &&
+				 strcmp(cc->profile_out_file, out_file) == 0);
+			}
+
+		if (match) return cc;
+		}
+
+	return NULL;
+}
+
+static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
+					  ColorManProfileType out_type, const gchar *out_file,
+					  gint has_alpha)
+{
+	ColorManCache *cc;
+
+	cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
+
+	if (!cc)
+		{
+		cc = color_man_cache_new(in_type, in_file, out_type, out_file, has_alpha);
+		}
+
+	return cc;
+}
+
+
+/*
+ *-------------------------------------------------------------------
+ * color manager
+ *-------------------------------------------------------------------
+ */
+
+static void color_man_done(ColorMan *cm, ColorManReturnType type)
+{
+	if (cm->func_done)
+		{
+		cm->func_done(cm, type, cm->func_done_data);
+		}
+}
+
+static void color_man_correct_region(ColorMan *cm, gint x, gint y, gint w, gint h,
+				     gint pixbuf_width, gint pixbuf_height)
+{
+	ColorManCache *cc;
+	guchar *pix;
+	gint rs;
+	gint i;
+
+	cc = cm->profile;
+
+	pix = gdk_pixbuf_get_pixels(cm->pixbuf);
+	rs = gdk_pixbuf_get_rowstride(cm->pixbuf);
+
+	w = MIN(w, pixbuf_width - x);
+	h = MIN(h, pixbuf_height - y);
+
+	pix += x * ((cc->has_alpha) ? 4 : 3);
+	for (i = 0; i < h; i++)
+		{
+		guchar *pbuf;
+
+		pbuf = pix + ((y + i) * rs);
+		cmsDoTransform(cc->transform, pbuf, pbuf, w);
+		}
+
+	image_area_changed(cm->imd, x, y, w, h);
+}
+
+static gint color_man_idle_cb(gpointer data)
+{
+	ColorMan *cm = data;
+	gint width, height;
+	gint rh;
+
+	if (cm->pixbuf != image_get_pixbuf(cm->imd))
+		{
+		cm->idle_id = -1;
+		color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
+		return FALSE;
+		}
+
+	width = gdk_pixbuf_get_width(cm->pixbuf);
+	height = gdk_pixbuf_get_height(cm->pixbuf);
+
+	if (cm->row > height)
+		{
+		cm->idle_id = -1;
+		color_man_done(cm, COLOR_RETURN_SUCCESS);
+		return FALSE;
+		}
+
+	rh = COLOR_MAN_CHUNK_SIZE / width + 1;
+	color_man_correct_region(cm, 0, cm->row, width, rh, width, height);
+	cm->row += rh;
+
+	return TRUE;
+}
+
+ColorMan *color_man_new(ImageWindow *imd,
+			ColorManProfileType input_type, const gchar *input_file,
+			ColorManProfileType screen_type, const gchar *screen_file,
+			ColorManDoneFunc done_func, gpointer done_data)
+{
+	ColorMan *cm;
+	GdkPixbuf *pixbuf;
+	gint has_alpha;
+
+	if (!imd) return NULL;
+	if (input_type == COLOR_PROFILE_NONE || screen_type == COLOR_PROFILE_NONE) return NULL;
+
+	pixbuf = image_get_pixbuf(imd);
+	if (!pixbuf) return NULL;
+
+	cm = g_new0(ColorMan, 1);
+	cm->imd = imd;
+	cm->pixbuf = pixbuf;
+	cm->row = 0;
+	cm->idle_id = -1;
+
+	cm->func_done = done_func;
+	cm->func_done_data = done_data;
+
+	has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+	cm->profile = color_man_cache_get(input_type, input_file, screen_type, screen_file, has_alpha);
+	if (!cm->profile)
+		{
+		color_man_free(cm);
+		return NULL;
+		}
+
+	color_man_cache_ref(cm->profile);
+
+	cm->idle_id = g_idle_add(color_man_idle_cb, cm);
+
+	return cm;
+}
+
+void color_man_free(ColorMan *cm)
+{
+	if (!cm) return;
+
+	if (cm->idle_id != -1) g_source_remove(cm->idle_id);
+
+	color_man_cache_unref(cm->profile);
+
+	g_free(cm);
+}
+
+void color_man_update(void)
+{
+	color_man_cache_reset();
+}
+
+#else
+/*** color support not enabled ***/
+
+
+ColorMan *color_man_new(ImageWindow *imd,
+			ColorManProfileType input_type, const gchar *input_file,
+			ColorManProfileType screen_type, const gchar *screen_file,
+			ColorManDoneFunc don_func, gpointer done_data)
+{
+	/* no op */
+	return NULL;
+}
+
+void color_man_free(ColorMan *cm)
+{
+	/* no op */
+}
+
+void color_man_update(void)
+{
+	/* no op */
+}
+
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/color-man.h	Sat Nov 25 03:00:33 2006 +0000
@@ -0,0 +1,56 @@
+/*
+ * GQview
+ * (C) 2006 John Ellis
+ *
+ * Author: John Ellis
+ *
+ * This software is released under the GNU General Public License (GNU GPL).
+ * Please read the included file COPYING for more information.
+ * This software comes with no warranty of any kind, use at your own risk!
+ */
+
+
+#ifndef COLOR_MAN_H
+#define COLOR_MAN_H
+
+typedef enum {
+	COLOR_PROFILE_NONE = 0,
+	COLOR_PROFILE_FILE,
+	COLOR_PROFILE_SRGB,
+} ColorManProfileType;
+
+typedef enum {
+	COLOR_RETURN_SUCCESS = 0,
+	COLOR_RETURN_ERROR,
+	COLOR_RETURN_IMAGE_CHANGED
+} ColorManReturnType;
+
+typedef struct _ColorMan ColorMan;
+typedef void (* ColorManDoneFunc)(ColorMan *cm, ColorManReturnType success, gpointer data);
+
+
+struct _ColorMan {
+	ImageWindow *imd;
+	GdkPixbuf *pixbuf;
+	gint row;
+
+	gpointer profile;
+
+	gint idle_id;
+
+	ColorManDoneFunc func_done;
+	gpointer func_done_data;
+};
+
+
+ColorMan *color_man_new(ImageWindow *imd,
+			ColorManProfileType input_type, const gchar *input_file,
+			ColorManProfileType screen_type, const gchar *screen_file,
+			ColorManDoneFunc done_func, gpointer done_data);
+void color_man_free(ColorMan *cm);
+
+void color_man_update(void);
+
+
+#endif
+
--- a/src/globals.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/globals.c	Sat Nov 25 03:00:33 2006 +0000
@@ -117,3 +117,13 @@
 gint update_on_time_change = TRUE;
 gint exif_rotate_enable = FALSE;
 
+/* color profiles */
+gint color_profile_enabled = FALSE;
+gint color_profile_input_type = 0;
+gchar *color_profile_input_file[COLOR_PROFILE_INPUTS];
+gchar *color_profile_input_name[COLOR_PROFILE_INPUTS];
+gint color_profile_screen_type = 0;
+gchar *color_profile_screen_file = NULL;
+gint color_profile_use_image = TRUE;
+
+
--- a/src/gqview.h	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/gqview.h	Sat Nov 25 03:00:33 2006 +0000
@@ -79,6 +79,8 @@
 
 #define GQVIEW_EDITOR_SLOTS 10
 
+#define COLOR_PROFILE_INPUTS 4
+
 /*
  *----------------------------------------------------------------------------
  * globals
@@ -194,6 +196,14 @@
 extern gint update_on_time_change;
 extern gint exif_rotate_enable;
 
+extern gint color_profile_enabled;
+extern gint color_profile_input_type;
+extern gchar *color_profile_input_file[];
+extern gchar *color_profile_input_name[];
+extern gint color_profile_screen_type;
+extern gchar *color_profile_screen_file;
+extern gint color_profile_use_image;
+
 /*
  *----------------------------------------------------------------------------
  * main.c
--- a/src/image.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/image.c	Sat Nov 25 03:00:33 2006 +0000
@@ -16,6 +16,7 @@
 
 #include "image-load.h"
 #include "collect.h"
+#include "color-man.h"
 #include "exif.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
@@ -43,6 +44,7 @@
 
 
 static void image_update_title(ImageWindow *imd);
+static void image_post_process(ImageWindow *imd, gint clamp);
 
 /*
  *-------------------------------------------------------------------
@@ -247,10 +249,95 @@
 		}
 }
 
+static void image_post_process_color_cb(ColorMan *cm, ColorManReturnType type, gpointer data)
+{
+	ImageWindow *imd = data;
+
+	color_man_free((ColorMan *)imd->cm);
+	imd->cm = NULL;
+	imd->state |= IMAGE_STATE_COLOR_ADJ;
+
+	if (type != COLOR_RETURN_IMAGE_CHANGED)
+		{
+		image_post_process(imd, FALSE);
+		}
+}
+
+static gint image_post_process_color(ImageWindow *imd, gint start_row)
+{
+	ColorMan *cm;
+	ColorManProfileType input_type;
+	ColorManProfileType screen_type;
+	const gchar *input_file;
+	const gchar *screen_file;
+
+	if (imd->cm) return FALSE;
+
+	if (imd->color_profile_input >= 1 &&
+	    imd->color_profile_input <= COLOR_PROFILE_INPUTS)
+		{
+		gint n;
+
+		n = imd->color_profile_input - 1;
+		if (!color_profile_input_file[n]) return FALSE;
+
+		input_type = COLOR_PROFILE_FILE;
+		input_file = color_profile_input_file[n];
+		}
+	else if (imd->color_profile_input == 0)
+		{
+		input_type = COLOR_PROFILE_SRGB;
+		input_file = NULL;
+		}
+	else
+		{
+		return FALSE;
+		}
+
+	if (imd->color_profile_screen == 1 &&
+	    color_profile_screen_file)
+		{
+		screen_type = COLOR_PROFILE_FILE;
+		screen_file = color_profile_screen_file;
+		}
+	else if (imd->color_profile_screen == 0)
+		{
+		screen_type = COLOR_PROFILE_SRGB;
+		screen_file = NULL;
+		}
+	else
+		{
+		return FALSE;
+		}
+
+	cm = color_man_new(imd,
+			   input_type, input_file,
+			   screen_type, screen_file,
+			   image_post_process_color_cb, imd);
+	if (cm)
+		{
+		if (start_row > 0) cm->row = start_row;
+
+		imd->cm = (gpointer)cm;
+		return TRUE;
+		}
+
+	return FALSE;
+}
+
 static void image_post_process(ImageWindow *imd, gint clamp)
 {
 	gint exif_rotated = FALSE;
 
+	if (imd->color_profile_enable &&
+	    !(imd->state & IMAGE_STATE_COLOR_ADJ))
+		{
+		if (image_post_process_color(imd, 0)) return;
+
+		/* fixme: note error to user */
+		imd->state |= IMAGE_STATE_COLOR_ADJ;
+		}
+
 	if (exif_rotate_enable && image_get_pixbuf(imd))
 		{
 		ExifData *ed;
@@ -405,7 +492,7 @@
  *-------------------------------------------------------------------
  */
 
-static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf)
+static void image_post_buffer_set(ImageWindow *imd, const gchar *path, GdkPixbuf *pixbuf, gint color_row)
 {
 	g_free(imd->prev_path);
 	if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
@@ -416,11 +503,13 @@
 			
 		g_object_ref(pixbuf);
 		imd->prev_pixbuf = pixbuf;
+		imd->prev_color_row = color_row;
 		}
 	else
 		{
 		imd->prev_path = NULL;
 		imd->prev_pixbuf = NULL;
+		imd->prev_color_row = -1;
 		}
 
 	if (debug) printf("post buffer set: %s\n", path);
@@ -434,6 +523,10 @@
 	    imd->image_path && imd->prev_path && strcmp(imd->image_path, imd->prev_path) == 0)
 		{
 		image_change_pixbuf(imd, imd->prev_pixbuf, image_zoom_get(imd));
+		if (imd->prev_color_row >= 0)
+			{
+			image_post_process_color(imd, imd->prev_color_row);
+			}
 		success = TRUE;
 		}
 	else
@@ -656,7 +749,12 @@
 	image_loader_free(imd->il);
 	imd->il = NULL;
 
+	color_man_free((ColorMan *)imd->cm);
+	imd->cm = NULL;
+
 	imd->delay_alter_type = ALTER_NONE;
+
+	imd->state = IMAGE_STATE_NONE;
 }
 
 /*
@@ -723,6 +821,7 @@
 	GdkPixbuf *prev_pixbuf = NULL;
 	gchar *prev_path = NULL;
 	gint prev_clear = FALSE;
+	gint prev_color_row = -1;
 
 	imd->collection = cd;
 	imd->collection_info = info;
@@ -741,6 +840,14 @@
 			prev_path = g_strdup(imd->image_path);
 			prev_pixbuf = pixbuf;
 			g_object_ref(prev_pixbuf);
+
+			if (imd->cm)
+				{
+				ColorMan *cm;
+
+				cm = (ColorMan *)imd->cm;
+				prev_color_row = cm->row;
+				}
 			}
 		}
 
@@ -752,13 +859,13 @@
 
 	if (prev_pixbuf)
 		{
-		image_post_buffer_set(imd, prev_path, prev_pixbuf);
+		image_post_buffer_set(imd, prev_path, prev_pixbuf, prev_color_row);
 		g_free(prev_path);
 		g_object_unref(prev_pixbuf);
 		}
 	else if (prev_clear)
 		{
-		image_post_buffer_set(imd, NULL, NULL);
+		image_post_buffer_set(imd, NULL, NULL, -1);
 		}
 
 	image_update_title(imd);
@@ -1040,6 +1147,24 @@
 		source->delay_alter_type = ALTER_NONE;
 		}
 
+	imd->color_profile_enable = source->color_profile_enable;
+	imd->color_profile_input = source->color_profile_input;
+	imd->color_profile_screen = source->color_profile_screen;
+	imd->color_profile_use_image = source->color_profile_use_image;
+	color_man_free((ColorMan *)imd->cm);
+	imd->cm = NULL;
+	if (source->cm)
+		{
+		ColorMan *cm;
+
+		imd->cm = source->cm;
+		source->cm = NULL;
+
+		cm = (ColorMan *)imd->cm;
+		cm->imd = imd;
+		cm->func_done_data = imd;
+		}
+
 	image_loader_free(imd->read_ahead_il);
 	imd->read_ahead_il = source->read_ahead_il;
 	source->read_ahead_il = NULL;
@@ -1056,12 +1181,16 @@
 	if (imd->prev_pixbuf) g_object_unref(imd->prev_pixbuf);
 	imd->prev_pixbuf = source->prev_pixbuf;
 	source->prev_pixbuf = NULL;
+	imd->prev_color_row = source->prev_color_row;
+	source->prev_color_row = -1;
 
 	g_free(imd->prev_path);
 	imd->prev_path = source->prev_path;
 	source->prev_path = NULL;
 
 	imd->completed = source->completed;
+	imd->state = source->state;
+	source->state = IMAGE_STATE_NONE;
 
 	pixbuf_renderer_move(PIXBUF_RENDERER(imd->pr), PIXBUF_RENDERER(source->pr));
 }
@@ -1095,7 +1224,7 @@
 {
 	if (pixbuf_renderer_get_tiles((PixbufRenderer *)imd->pr)) return;
 
-	if (imd->il)
+	if (imd->il || imd->cm)
 		{
 		/* still loading, wait till done */
 		imd->delay_alter_type = type;
@@ -1304,6 +1433,52 @@
 	pixbuf_renderer_set_color((PixbufRenderer *)imd->pr, color);
 }
 
+void image_color_profile_set(ImageWindow *imd,
+			     gint input_type, gint screen_type,
+			     gint use_image)
+{
+	if (!imd) return;
+
+	if (input_type < 0 || input_type > COLOR_PROFILE_INPUTS ||
+	    screen_type < 0 || screen_type > 1)
+		{
+		return;
+		}
+
+	imd->color_profile_input = input_type;
+	imd->color_profile_screen = screen_type;
+	imd->color_profile_use_image = use_image;
+}
+
+gint image_color_profile_get(ImageWindow *imd,
+			     gint *input_type, gint *screen_type,
+			     gint *use_image)
+{
+	if (!imd) return FALSE;
+
+	if (input_type) *input_type = imd->color_profile_input;
+	if (screen_type) *screen_type = imd->color_profile_screen;
+	if (use_image) *use_image = imd->color_profile_use_image;
+
+	return TRUE;
+}
+
+void image_color_profile_set_use(ImageWindow *imd, gint enable)
+{
+	if (!imd) return;
+
+	if (imd->color_profile_enable == enable) return;
+
+	imd->color_profile_enable = enable;
+}
+
+gint image_color_profile_get_use(ImageWindow *imd)
+{
+	if (!imd) return FALSE;
+
+	return imd->color_profile_enable;
+}
+
 void image_set_delay_flip(ImageWindow *imd, gint delay)
 {
 	if (!imd ||
@@ -1415,7 +1590,7 @@
 	image_reset(imd);
 
 	image_read_ahead_cancel(imd);
-	image_post_buffer_set(imd, NULL, NULL);
+	image_post_buffer_set(imd, NULL, NULL, -1);
 	image_auto_refresh(imd, -1);
 
 	g_free(imd->image_path);
@@ -1454,6 +1629,12 @@
 	imd->read_ahead_path = NULL;
 
 	imd->completed = FALSE;
+	imd->state = IMAGE_STATE_NONE;
+
+	imd->color_profile_enable = FALSE;
+	imd->color_profile_input = 0;
+	imd->color_profile_screen = 0;
+	imd->color_profile_use_image = FALSE;
 
 	imd->auto_refresh_id = -1;
 	imd->auto_refresh_interval = -1;
--- a/src/image.h	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/image.h	Sat Nov 25 03:00:33 2006 +0000
@@ -86,6 +86,16 @@
 void image_background_set_black(ImageWindow *imd, gint black);
 void image_background_set_color(ImageWindow *imd, GdkColor *color);
 
+/* color profiles */
+void image_color_profile_set(ImageWindow *imd,
+			     gint input_type, gint screen_type,
+			     gint use_embedded);
+gint image_color_profile_get(ImageWindow *imd,
+			     gint *input_type, gint *screen_type,
+			     gint *use_image);
+void image_color_profile_set_use(ImageWindow *imd, gint enable);
+gint image_color_profile_get_use(ImageWindow *imd);
+
 /* set delayed page flipping */
 void image_set_delay_flip(ImageWindow *imd, gint delay);
 
--- a/src/layout.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/layout.c	Sat Nov 25 03:00:33 2006 +0000
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -19,6 +19,7 @@
 #include "menu.h"
 #include "pixbuf-renderer.h"
 #include "pixbuf_util.h"
+#include "utilops.h"
 #include "view_dir_list.h"
 #include "view_dir_tree.h"
 #include "view_file_list.h"
@@ -226,6 +227,8 @@
 	LayoutWindow *lw;
 	SortType type;
 
+	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
 	lw = submenu_item_get_data(widget);
 	if (!lw) return;
 
@@ -301,6 +304,180 @@
 
 /*
  *-----------------------------------------------------------------------------
+ * color profile button (and menu)
+ *-----------------------------------------------------------------------------
+ */
+
+static void layout_color_menu_enable_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+
+	layout_image_color_profile_set_use(lw, (!layout_image_color_profile_get_use(lw)));
+	layout_image_refresh(lw);
+}
+
+#define COLOR_MENU_KEY "color_menu_key"
+
+static void layout_color_menu_input_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	gint type;
+	gint input, screen, use_image;
+
+	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+	type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
+	if (type < 0 || type > COLOR_PROFILE_INPUTS) return;
+
+	if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+	if (type == input) return;
+
+	layout_image_color_profile_set(lw, type, screen, use_image);
+	layout_image_refresh(lw);
+}
+
+static void layout_color_menu_screen_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	gint type;
+	gint input, screen, use_image;
+
+	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
+
+	type = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), COLOR_MENU_KEY));
+	if (type < 0 || type > 1) return;
+
+	if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+	if (type == screen) return;
+
+	layout_image_color_profile_set(lw, input, type, use_image);
+	layout_image_refresh(lw);
+}
+
+static gchar *layout_color_name_parse(const gchar *name)
+{
+	gchar *result;
+	gchar *p;
+
+	if (!name) name = _("Empty");
+
+	result = g_strdup(name);
+	p = result;
+	while (*p != '\0')
+		{
+		if (*p == '_') *p = '-';
+		p++;
+		}
+	return result;
+}
+
+static void layout_color_button_press_cb(GtkWidget *widget, gpointer data)
+{
+	LayoutWindow *lw = data;
+	GtkWidget *menu;
+	GtkWidget *item;
+	gchar *buf;
+	gchar *front;
+	gchar *end;
+	gint active;
+	gint input = 0;
+	gint screen = 0;
+	gint use_image = 0;
+	gint i;
+
+#ifndef HAVE_LCMS
+	file_util_warning_dialog(_("Color profiles not supported"),
+				 _("This installation of GQview was not built with support for color profiles."),
+				 GTK_STOCK_DIALOG_INFO, widget);
+	return;
+#endif
+
+	active = layout_image_color_profile_get_use(lw);
+	if (!layout_image_color_profile_get(lw, &input, &screen, &use_image)) return;
+
+	menu = popup_menu_short_lived();
+
+	menu_item_add_check(menu, _("Use _color profiles"), active,
+			    G_CALLBACK(layout_color_menu_enable_cb), lw);
+
+	menu_item_add_divider(menu);
+
+	front = g_strdup_printf(_("Input _%d:"), 0);
+	buf = g_strdup_printf("%s %s", front, "sRGB");
+	g_free(front);
+	item = menu_item_add_radio(menu, NULL,
+				   buf, (color_profile_input_type == 0),
+				   G_CALLBACK(layout_color_menu_input_cb), lw);
+	g_free(buf);
+	g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(0));
+	gtk_widget_set_sensitive(item, active);
+
+	for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+		{
+		const gchar *name;
+
+		name = color_profile_input_name[i];
+		if (!name) name = filename_from_path(color_profile_input_file[i]);
+
+		front = g_strdup_printf(_("Input _%d:"), i + 1);
+		end = layout_color_name_parse(name);
+		buf = g_strdup_printf("%s %s", front, end);
+		g_free(front);
+		g_free(end);
+
+		item = menu_item_add_radio(menu, item,
+					   buf, (i + 1 == input),
+					   G_CALLBACK(layout_color_menu_input_cb), lw);
+		g_free(buf);
+		g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(i + 1));
+		gtk_widget_set_sensitive(item, active && color_profile_input_file[i]);
+		}
+
+	menu_item_add_divider(menu);
+
+	buf = g_strdup_printf("%s sRGB", _("Screen"));
+	item = menu_item_add_radio(menu, NULL,
+				   buf, (screen == 0),
+				   G_CALLBACK(layout_color_menu_screen_cb), lw);
+	g_free(buf);
+	g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(0));
+	gtk_widget_set_sensitive(item, active);
+
+	item = menu_item_add_radio(menu, item,
+				   _("_Screen profile"), (screen == 1),
+				   G_CALLBACK(layout_color_menu_screen_cb), lw);
+	g_object_set_data(G_OBJECT(item), COLOR_MENU_KEY, GINT_TO_POINTER(1));
+	gtk_widget_set_sensitive(item, active && color_profile_screen_file);
+
+	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME);
+}
+
+static GtkWidget *layout_color_button(LayoutWindow *lw)
+{
+	GtkWidget *button;
+	GtkWidget *image;
+	gint enable;
+
+	button = gtk_button_new();
+	image = gtk_image_new_from_stock(GTK_STOCK_SELECT_COLOR, GTK_ICON_SIZE_MENU);
+	gtk_container_add(GTK_CONTAINER(button), image);
+	gtk_widget_show(image);
+	g_signal_connect(G_OBJECT(button), "clicked",
+			 G_CALLBACK(layout_color_button_press_cb), lw);
+        gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
+
+	enable = (lw->image) ? lw->image->color_profile_enable : FALSE;
+#ifndef HAVE_LCMS
+	enable = FALSE;
+#endif
+	gtk_widget_set_sensitive(image, enable);
+
+	return button;
+}
+
+
+/*
+ *-----------------------------------------------------------------------------
  * status bar
  *-----------------------------------------------------------------------------
  */
@@ -486,6 +663,11 @@
 	gtk_box_pack_start(GTK_BOX(hbox), lw->info_sort, FALSE, FALSE, 0);
 	gtk_widget_show(lw->info_sort);
 
+	lw->info_color = layout_color_button(lw);
+	gtk_widget_show(lw->info_color);
+
+	if (small_format) gtk_box_pack_end(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
+
 	lw->info_status = layout_status_label(NULL, lw->info_box, TRUE, 0, (!small_format));
 
 	if (small_format)
@@ -499,6 +681,7 @@
 		hbox = lw->info_box;
 		}
 	lw->info_details = layout_status_label(NULL, hbox, TRUE, 0, TRUE);
+	if (!small_format) gtk_box_pack_start(GTK_BOX(hbox), lw->info_color, FALSE, FALSE, 0);
 	lw->info_zoom = layout_status_label(NULL, hbox, FALSE, ZOOM_LABEL_WIDTH, FALSE);
 }
 
@@ -1396,6 +1579,7 @@
 	lw->info_box = NULL;
 	lw->info_progress_bar = NULL;
 	lw->info_sort = NULL;
+	lw->info_color = NULL;
 	lw->info_status = NULL;
 	lw->info_details = NULL;
 	lw->info_zoom = NULL;
--- a/src/layout_image.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/layout_image.c	Sat Nov 25 03:00:33 2006 +0000
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -1140,6 +1140,46 @@
 	image_reload(lw->image);
 }
 
+void layout_image_color_profile_set(LayoutWindow *lw,
+				    gint input_type, gint screen_type,
+				    gint use_image)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_color_profile_set(lw->image, input_type, screen_type, use_image);
+}
+
+gint layout_image_color_profile_get(LayoutWindow *lw,
+				    gint *input_type, gint *screen_type,
+				    gint *use_image)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	return image_color_profile_get(lw->image, input_type, screen_type, use_image);
+}
+
+void layout_image_color_profile_set_use(LayoutWindow *lw, gint enable)
+{
+	if (!layout_valid(&lw)) return;
+
+	image_color_profile_set_use(lw->image, enable);
+
+	if (lw->info_color)
+		{
+#ifndef HAVE_LCMS
+		enable = FALSE;
+#endif
+		gtk_widget_set_sensitive(GTK_BIN(lw->info_color)->child, enable);
+		}
+}
+
+gint layout_image_color_profile_get_use(LayoutWindow *lw)
+{
+	if (!layout_valid(&lw)) return FALSE;
+
+	return image_color_profile_get_use(lw->image);
+}
+
 /*
  *----------------------------------------------------------------------------
  * list walkers
@@ -1393,6 +1433,11 @@
 		image_attach_window(lw->image, lw->window, NULL, "GQview", FALSE);
 
 		image_auto_refresh(lw->image, 0);
+
+		image_color_profile_set(lw->image,
+					color_profile_input_type, color_profile_screen_type,
+					color_profile_use_image);
+		image_color_profile_set_use(lw->image, color_profile_enabled);
 		}
 
 	return lw->image->widget;
--- a/src/layout_image.h	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/layout_image.h	Sat Nov 25 03:00:33 2006 +0000
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -23,6 +23,15 @@
 
 void layout_image_refresh(LayoutWindow *lw);
 
+void layout_image_color_profile_set(LayoutWindow *lw,
+				    gint input_type, gint screen_type,
+				    gint use_image);
+gint layout_image_color_profile_get(LayoutWindow *lw,
+				    gint *input_type, gint *screen_type,
+				    gint *use_image);
+void layout_image_color_profile_set_use(LayoutWindow *lw, gint enable);
+gint layout_image_color_profile_get_use(LayoutWindow *lw);
+
 
 const gchar *layout_image_get_path(LayoutWindow *lw);
 const gchar *layout_image_get_name(LayoutWindow *lw);
--- a/src/main.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/main.c	Sat Nov 25 03:00:33 2006 +0000
@@ -1127,6 +1127,12 @@
 
 	g_free(safe_delete_path);
 	safe_delete_path = concat_dir_and_file(homedir(), GQVIEW_RC_DIR_TRASH);
+
+	for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+		{
+		color_profile_input_file[i] = NULL;
+		color_profile_input_name[i] = NULL;
+		}
 }
 
 static void exit_gqview_final(void)
@@ -1161,6 +1167,11 @@
 	layout_tools_float_get(NULL, &tools_float, &tools_hidden);
 	toolbar_hidden = layout_toolbar_hidden(NULL);
 
+	color_profile_enabled = layout_image_color_profile_get_use(NULL);
+	layout_image_color_profile_get(NULL,
+				       &color_profile_input_type, &color_profile_screen_type,
+				       &color_profile_use_image);
+
 	save_options();
 	keys_save();
 
--- a/src/menu.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/menu.c	Sat Nov 25 03:00:33 2006 +0000
@@ -135,19 +135,25 @@
 	return "";
 }
 
-static void submenu_add_sort_item(GtkWidget *menu, GCallback func, SortType type,
-				  gint show_current, SortType show_type)
+static GtkWidget *submenu_add_sort_item(GtkWidget *menu, GtkWidget *parent,
+					GCallback func, SortType type,
+					gint show_current, SortType show_type)
 {
+	GtkWidget *item;
+
 	if (show_current)
 		{
-		menu_item_add_check(menu, sort_type_get_text(type), (type == show_type),
-				    func, GINT_TO_POINTER((gint)type));
+		item = menu_item_add_radio(menu, parent,
+					   sort_type_get_text(type), (type == show_type),
+					   func, GINT_TO_POINTER((gint)type));
 		}
 	else
 		{
-		menu_item_add(menu, sort_type_get_text(type),
-			      func, GINT_TO_POINTER((gint)type));
+		item = menu_item_add(menu, sort_type_get_text(type),
+			 	     func, GINT_TO_POINTER((gint)type));
 		}
+
+	return item;
 }
 
 GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
@@ -155,18 +161,19 @@
 			    gint show_current, SortType type)
 {
 	GtkWidget *submenu;
+	GtkWidget *parent;
 
 	submenu = gtk_menu_new();
 	g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
 
-	submenu_add_sort_item(submenu, func, SORT_NAME, show_current, type);
+	parent = submenu_add_sort_item(submenu, NULL, func, SORT_NAME, show_current, type);
 #ifdef HAVE_STRVERSCMP
-	submenu_add_sort_item(submenu, func, SORT_NUMBER, show_current, type);
+	submenu_add_sort_item(submenu, parent, func, SORT_NUMBER, show_current, type);
 #endif
-	submenu_add_sort_item(submenu, func, SORT_TIME, show_current, type);
-	submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type);
-	if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type);
-	if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
+	submenu_add_sort_item(submenu, parent, func, SORT_TIME, show_current, type);
+	submenu_add_sort_item(submenu, parent, func, SORT_SIZE, show_current, type);
+	if (include_path) submenu_add_sort_item(submenu, parent, func, SORT_PATH, show_current, type);
+	if (include_none) submenu_add_sort_item(submenu, parent, func, SORT_NONE, show_current, type);
 
 	if (menu)
 		{
--- a/src/preferences.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/preferences.c	Sat Nov 25 03:00:33 2006 +0000
@@ -138,6 +138,10 @@
 static gint update_on_time_change_c;
 static gint exif_rotate_enable_c;
 
+static GtkWidget *color_profile_input_file_entry[COLOR_PROFILE_INPUTS];
+static GtkWidget *color_profile_input_name_entry[COLOR_PROFILE_INPUTS];
+static GtkWidget *color_profile_screen_file_entry;
+
 
 /*
  *-----------------------------------------------------------------------------
@@ -288,6 +292,25 @@
 
 	tree_descend_subdirs = tree_descend_subdirs_c;
 
+#ifdef HAVE_LCMS
+	for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+		{
+		g_free(color_profile_input_name[i]);
+		color_profile_input_name[i] = NULL;
+		buf = gtk_entry_get_text(GTK_ENTRY(color_profile_input_name_entry[i]));
+		if (buf && strlen(buf) > 0) color_profile_input_name[i] = g_strdup(buf);
+
+		g_free(color_profile_input_file[i]);
+		color_profile_input_file[i] = NULL;
+		buf = gtk_entry_get_text(GTK_ENTRY(color_profile_input_file_entry[i]));
+		if (buf && strlen(buf) > 0) color_profile_input_file[i] = g_strdup(buf);
+		}
+	g_free(color_profile_screen_file);
+	color_profile_screen_file = NULL;
+	buf = gtk_entry_get_text(GTK_ENTRY(color_profile_screen_file_entry));
+	if (buf && strlen(buf) > 0) color_profile_screen_file = g_strdup(buf);
+#endif
+
 	l_conf = layout_config_get(layout_widget, &new_style);
 
 	if (new_style != layout_style ||
@@ -403,7 +426,7 @@
 			 G_CALLBACK(quality_menu_cb), option_c);
 
 	gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-			 GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+			 GTK_EXPAND | GTK_FILL, 0, 0, 0);
 	gtk_widget_show(combo);
 }
 
@@ -514,7 +537,7 @@
 			 G_CALLBACK(thumb_size_menu_cb), NULL);
 
 	gtk_table_attach(GTK_TABLE(table), combo, column + 1, column + 2, row, row + 1,
-			 GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+			 GTK_EXPAND | GTK_FILL, 0, 0, 0);
 	gtk_widget_show(combo);
 }
 
@@ -1103,7 +1126,7 @@
 		gtk_widget_set_size_request(editor_name_entry[i],80,-1);
 		if (editor_name[i]) gtk_entry_set_text(GTK_ENTRY(editor_name_entry[i]),editor_name[i]);
 		gtk_table_attach(GTK_TABLE (table),editor_name_entry[i],1,2,i+1,i+2,
-				 GTK_FILL | GTK_EXPAND, FALSE, 0, 0);
+				 GTK_FILL | GTK_EXPAND, 0, 0, 0);
 		gtk_widget_show(editor_name_entry[i]);
 
 		editor_command_entry[i] = gtk_entry_new();
@@ -1112,7 +1135,7 @@
 		tab_completion_add_to_entry(editor_command_entry[i], NULL, NULL);
 		if (editor_command[i]) gtk_entry_set_text(GTK_ENTRY(editor_command_entry[i]), editor_command[i]);
 		gtk_table_attach(GTK_TABLE (table),editor_command_entry[i],2,3,i+1,i+2,
-				 GTK_FILL | GTK_EXPAND, FALSE, 0, 0);
+				 GTK_FILL | GTK_EXPAND, 0, 0, 0);
 		gtk_widget_show(editor_command_entry[i]);
 		}
 
@@ -1227,6 +1250,60 @@
 	pref_spin_new_int(group, _("Offscreen cache size (Mb per image):"), NULL,
 			  0, 128, 1, tile_cache_max, &tile_cache_max_c);
 
+	group =  pref_group_new(vbox, FALSE, _("Color profiles"), GTK_ORIENTATION_VERTICAL);
+#ifndef HAVE_LCMS
+	gtk_widget_set_sensitive(pref_group_parent(group), FALSE);
+#endif
+
+	table = pref_table_new(group, 3, COLOR_PROFILE_INPUTS + 2, FALSE, FALSE);
+	gtk_table_set_col_spacings(GTK_TABLE(table), PREF_PAD_GAP);
+
+	label = pref_table_label(table, 0, 0, _("Type"), 0.0);
+	pref_label_bold(label, TRUE, FALSE);
+
+	label = pref_table_label(table, 1, 0, _("Menu name"), 0.0);
+	pref_label_bold(label, TRUE, FALSE);
+
+	label = pref_table_label(table, 2, 0, _("File"), 0.0);
+	pref_label_bold(label, TRUE, FALSE);
+
+	for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+		{
+		GtkWidget *entry;
+		gchar *buf;
+
+		buf = g_strdup_printf("Input %d:", i + 1);
+		pref_table_label(table, 0, i + 1, buf, 1.0);
+		g_free(buf);
+
+		entry = gtk_entry_new();
+		gtk_entry_set_max_length(GTK_ENTRY(entry), EDITOR_NAME_MAX_LENGTH);
+		gtk_widget_set_size_request(editor_name_entry[i], 30, -1);
+		if (color_profile_input_name[i]) gtk_entry_set_text(GTK_ENTRY(entry), color_profile_input_name[i]);
+		gtk_table_attach(GTK_TABLE(table), entry, 1, 2, i + 1, i + 2,
+				 GTK_FILL | GTK_EXPAND, 0, 0, 0);
+		gtk_widget_show(entry);
+		color_profile_input_name_entry[i] = entry;
+
+		tabcomp = tab_completion_new(&entry, color_profile_input_file[i], NULL, NULL);
+		tab_completion_add_select_button(entry, _("Select color profile"), FALSE);
+		gtk_widget_set_size_request(entry, 160, -1);
+		gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3, i + 1, i + 2,
+				 GTK_FILL | GTK_EXPAND, 0, 0, 0);
+		gtk_widget_show(tabcomp);
+		color_profile_input_file_entry[i] = entry;
+		}
+
+	pref_table_label(table, 0, COLOR_PROFILE_INPUTS + 1, _("Screen:"), 1.0);
+	tabcomp = tab_completion_new(&color_profile_screen_file_entry,
+				     color_profile_screen_file, NULL, NULL);
+	tab_completion_add_select_button(color_profile_screen_file_entry, _("Select color profile"), FALSE);
+	gtk_widget_set_size_request(color_profile_screen_file_entry, 160, -1);
+	gtk_table_attach(GTK_TABLE(table), tabcomp, 2, 3,
+			 COLOR_PROFILE_INPUTS + 1, COLOR_PROFILE_INPUTS + 2,
+			 GTK_FILL | GTK_EXPAND, 0, 0, 0);
+	gtk_widget_show(tabcomp);
+
 	gtk_widget_show(notebook);
 
 	gtk_widget_show(configwindow);
--- a/src/rcfile.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/rcfile.c	Sat Nov 25 03:00:33 2006 +0000
@@ -1,6 +1,6 @@
 /*
  * GQview
- * (C) 2004 John Ellis
+ * (C) 2006 John Ellis
  *
  * Author: John Ellis
  *
@@ -305,6 +305,32 @@
 	write_bool_option(f, "disable_filtering", file_filter_disable);
 	filter_write_list(f);
 
+	fprintf(f,"\n##### Color Profiles #####\n\n");
+
+#ifndef HAVE_LCMS
+	fprintf(f,"# NOTICE: GQview was not built with support for color profiles,\n"
+		  "#         color profile options will have no effect.\n\n");
+#endif
+
+	write_bool_option(f, "color_profile_enabled", color_profile_enabled);
+	write_bool_option(f, "color_profile_use_image", color_profile_use_image);
+	write_int_option(f, "color_profile_input_type", color_profile_input_type);
+	for (i = 0; i < COLOR_PROFILE_INPUTS; i++)
+		{
+		gchar *buf;
+
+		buf = g_strdup_printf("color_profile_input_file_%d", i + 1);
+		write_char_option(f, buf, color_profile_input_file[i]);
+		g_free(buf);
+
+		buf = g_strdup_printf("color_profile_input_name_%d", i + 1);
+		write_char_option(f, buf, color_profile_input_name[i]);
+		g_free(buf);
+		}
+	fprintf(f,"\n");
+	write_int_option(f, "color_profile_screen_type", color_profile_screen_type);
+	write_char_option(f, "color_profile_screen_file_1", color_profile_screen_file);
+
 	fprintf(f,"\n##### External Programs #####\n");
 	fprintf(f,"# Maximum of 10 programs (external_1 through external_10)\n");
 	fprintf(f,"# format: external_n: \"menu name\" \"command line\"\n\n");
@@ -555,6 +581,39 @@
 			filter_parse(value_all);
 			}
 
+		/* Color Profiles */
+
+		color_profile_enabled = read_bool_option(f, option,
+			"color_profile_enabled", value, color_profile_enabled);
+		color_profile_use_image = read_bool_option(f, option,
+			"color_profile_use_image", value, color_profile_use_image);
+		color_profile_input_type = read_int_option(f, option,
+			"color_profile_input_type", value, color_profile_input_type);
+
+		if (strncasecmp(option, "color_profile_input_file_", 25) == 0)
+                        {
+                        i = strtol(option + 25, NULL, 0) - 1;
+			if (i >= 0 && i < COLOR_PROFILE_INPUTS)
+				{
+				color_profile_input_file[i] = read_char_option(f, option,
+					option, value, color_profile_input_file[i]);
+				}
+			}
+		if (strncasecmp(option, "color_profile_input_name_", 25) == 0)
+                        {
+                        i = strtol(option + 25, NULL, 0) - 1;
+			if (i >= 0 && i < COLOR_PROFILE_INPUTS)
+				{
+				color_profile_input_name[i] = read_char_option(f, option,
+					option, value, color_profile_input_name[i]);
+				}
+			}
+
+		color_profile_screen_type = read_int_option(f, option,
+			"color_profile_screen_type", value, color_profile_screen_type);
+		color_profile_screen_file = read_char_option(f, option,
+			"color_profile_screen_file_1", value, color_profile_screen_file);
+
 		/* External Programs */
 
 		if (strncasecmp(option, "external_", 9) == 0)
--- a/src/typedefs.h	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/typedefs.h	Sat Nov 25 03:00:33 2006 +0000
@@ -42,6 +42,17 @@
 } LayoutLocation;
 
 
+typedef enum {
+	IMAGE_STATE_NONE	= 0,
+	IMAGE_STATE_IMAGE	= 1 << 0,
+	IMAGE_STATE_LOADING	= 1 << 1,
+	IMAGE_STATE_ERROR	= 1 << 2,
+	IMAGE_STATE_COLOR_ADJ	= 1 << 3,
+	IMAGE_STATE_ROTATE_AUTO	= 1 << 4,
+	IMAGE_STATE_ROTATE_USER	= 1 << 5,
+	IMAGE_STATE_DELAY_FLIP	= 1 << 6
+} ImageState;
+
 typedef struct _ImageLoader ImageLoader;
 typedef struct _ThumbLoader ThumbLoader;
 
@@ -242,6 +253,7 @@
 	gint title_show_zoom;	/* option to include zoom in window title */
 
 	gint completed;
+	ImageState state;	/* mask of IMAGE_STATE_* flags about current image */
 
 	void (*func_update)(ImageWindow *, gpointer);
 	void (*func_complete)(ImageWindow *, gint preload, gpointer);
@@ -272,6 +284,13 @@
 	CollectionData *collection;
 	CollectInfo *collection_info;
 
+	/* color profiles */
+	gint color_profile_enable;
+	gint color_profile_input;
+	gint color_profile_screen;
+	gint color_profile_use_image;
+	gpointer *cm;
+
 	AlterType delay_alter_type;
 
 	ImageLoader *read_ahead_il;
@@ -280,6 +299,7 @@
 
 	GdkPixbuf *prev_pixbuf;
 	gchar *prev_path;
+	gint prev_color_row;
 
 	gint auto_refresh_id;
 	gint auto_refresh_interval;
@@ -366,6 +386,7 @@
 	GtkWidget *info_box;
 	GtkWidget *info_progress_bar;
 	GtkWidget *info_sort;
+	GtkWidget *info_color;
 	GtkWidget *info_status;
 	GtkWidget *info_details;
 	GtkWidget *info_zoom;
--- a/src/ui_menu.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/ui_menu.c	Sat Nov 25 03:00:33 2006 +0000
@@ -96,6 +96,22 @@
 	return item;
 }
 
+GtkWidget *menu_item_add_radio(GtkWidget *menu, GtkWidget *parent,
+			       const gchar *label, gint active,
+			       GCallback func, gpointer data)
+{
+	GtkWidget *item;
+	GSList *group = NULL;
+
+	if (parent) group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(parent));
+
+	item = gtk_radio_menu_item_new_with_mnemonic(group, label);
+	if (active) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
+	menu_item_finish(menu, item, func, data);
+
+	return item;
+}
+
 void menu_item_add_divider(GtkWidget *menu)
 {
 	GtkWidget *item = gtk_menu_item_new();
--- a/src/ui_menu.h	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/ui_menu.h	Sat Nov 25 03:00:33 2006 +0000
@@ -24,6 +24,9 @@
 					 GCallback func, gpointer data);
 GtkWidget *menu_item_add_check(GtkWidget *menu, const gchar *label, gint active,
 			       GCallback func, gpointer data);
+GtkWidget *menu_item_add_radio(GtkWidget *menu, GtkWidget *parent,
+			       const gchar *label, gint active,
+			       GCallback func, gpointer data);
 void menu_item_add_divider(GtkWidget *menu);
 
 /* use to avoid mnemonics, for example filenames */
--- a/src/ui_misc.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/ui_misc.c	Sat Nov 25 03:00:33 2006 +0000
@@ -613,7 +613,7 @@
 		}
 
 	gtk_table_attach(GTK_TABLE(table), shell, column, column + 1, row, row + 1,
-			 GTK_EXPAND | GTK_FILL, FALSE, 0, 0);
+			 GTK_EXPAND | GTK_FILL, 0, 0, 0);
 
 	gtk_widget_show(shell);
 
@@ -628,7 +628,7 @@
 
 	align = gtk_alignment_new(alignment, 0.50, 0.0, 0.0);
 	gtk_table_attach(GTK_TABLE(table), align, column, column + 1, row, row + 1,
-			 GTK_FILL, FALSE, 0, 0);
+			 GTK_FILL, 0, 0, 0);
 	gtk_widget_show(align);
 	label = gtk_label_new(text);
 	gtk_container_add(GTK_CONTAINER(align), label);
@@ -645,7 +645,7 @@
 
 	button = pref_button_new(NULL, stock_id, text, hide_stock_text, func, data);
 	gtk_table_attach(GTK_TABLE(table), button, column, column + 1, row, row + 1,
-			 GTK_FILL, FALSE, 0, 0);
+			 GTK_FILL, 0, 0, 0);
 	gtk_widget_show(button);
 
 	return button;
--- a/src/view_file_icon.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/view_file_icon.c	Sat Nov 25 03:00:33 2006 +0000
@@ -206,6 +206,8 @@
 {
 	ViewFileIcon *vfi;
 	SortType type;
+
+	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
 	
 	vfi = submenu_item_get_data(widget);
 	if (!vfi) return;
--- a/src/view_file_list.c	Sat Nov 18 00:12:22 2006 +0000
+++ b/src/view_file_list.c	Sat Nov 25 03:00:33 2006 +0000
@@ -306,6 +306,8 @@
 {
 	ViewFileList *vfl;
 	SortType type;
+
+	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget))) return;
 	
 	vfl = submenu_item_get_data(widget);
 	if (!vfl) return;