Mercurial > geeqie
diff src/color-man.c @ 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 | |
children | 50fc73e08550 |
line wrap: on
line diff
--- /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 + +