Mercurial > geeqie.yaz
view src/color-man.c @ 1554:a96a20c7feb1
improved remote protocol to allow bidirectional communication
author | nadvornik |
---|---|
date | Mon, 20 Apr 2009 19:44:50 +0000 |
parents | 7302ff300a52 |
children | f1d22c2174c1 |
line wrap: on
line source
/* * Geeqie * (C) 2006 John Ellis * Copyright (C) 2008 - 2009 The Geeqie Team * * 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 "main.h" #include "color-man.h" #include "image.h" #include "ui_fileops.h" #ifdef HAVE_LCMS /*** color support enabled ***/ #include <lcms.h> 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; gboolean has_alpha; gint refcount; }; /* pixels to transform per idle call */ #define COLOR_MAN_CHUNK_SIZE 81900 static void color_man_lib_init(void) { static gboolean init_done = FALSE; if (init_done) return; init_done = TRUE; cmsErrorAction(LCMS_ERROR_IGNORE); } static cmsHPROFILE color_man_create_adobe_comp(void) { /* ClayRGB1998 is AdobeRGB compatible */ #include "ClayRGB1998_icc.h" return cmsOpenProfileFromMem(ClayRGB1998_icc, ClayRGB1998_icc_len); } /* *------------------------------------------------------------------- * 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, guchar *data, guint data_len) { 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_ADOBERGB: profile = color_man_create_adobe_comp(); break; case COLOR_PROFILE_MEM: if (data) { profile = cmsOpenProfileFromMem(data, data_len); } break; case COLOR_PROFILE_NONE: default: break; } return profile; } static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file, guchar *in_data, guint in_data_len, ColorManProfileType out_type, const gchar *out_file, guchar *out_data, guint out_data_len, gboolean 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, in_data, in_data_len); cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file, out_data, out_data_len); if (!cc->profile_in || !cc->profile_out) { DEBUG_1("failed to load color profile for %s: %d %s", (!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) { DEBUG_1("failed to create color profile transform"); color_man_cache_unref(cc); return NULL; } if (cc->profile_in_type != COLOR_PROFILE_MEM && cc->profile_out_type != COLOR_PROFILE_MEM ) { cm_cache_list = g_list_append(cm_cache_list, cc); color_man_cache_ref(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, gboolean has_alpha) { GList *work; work = cm_cache_list; while (work) { ColorManCache *cc; gboolean 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, guchar *in_data, guint in_data_len, ColorManProfileType out_type, const gchar *out_file, guchar *out_data, guint out_data_len, gboolean has_alpha) { ColorManCache *cc; cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha); if (cc) { color_man_cache_ref(cc); return cc; } return color_man_cache_new(in_type, in_file, in_data, in_data_len, out_type, out_file, out_data, out_data_len, has_alpha); } /* *------------------------------------------------------------------- * color manager *------------------------------------------------------------------- */ static void color_man_done(ColorMan *cm, ColorManReturnType type) { if (cm->func_done) { cm->func_done(cm, type, cm->func_done_data); } } void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h) { ColorManCache *cc; guchar *pix; gint rs; gint i; gint pixbuf_width, pixbuf_height; pixbuf_width = gdk_pixbuf_get_width(pixbuf); pixbuf_height = gdk_pixbuf_get_height(pixbuf); cc = cm->profile; pix = gdk_pixbuf_get_pixels(pixbuf); rs = gdk_pixbuf_get_rowstride(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); } } static gboolean color_man_idle_cb(gpointer data) { ColorMan *cm = data; gint width, height; gint rh; if (!cm->pixbuf) return FALSE; if (cm->imd && cm->pixbuf != image_get_pixbuf(cm->imd)) { cm->idle_id = 0; 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) { if (!cm->incremental_sync && cm->imd) { image_area_changed(cm->imd, 0, 0, width, height); } cm->idle_id = 0; color_man_done(cm, COLOR_RETURN_SUCCESS); return FALSE; } rh = COLOR_MAN_CHUNK_SIZE / width + 1; color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh); if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh); cm->row += rh; return TRUE; } static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf, ColorManProfileType input_type, const gchar *input_file, guchar *input_data, guint input_data_len, ColorManProfileType screen_type, const gchar *screen_file, guchar *screen_data, guint screen_data_len) { ColorMan *cm; gboolean has_alpha; if (imd) pixbuf = image_get_pixbuf(imd); cm = g_new0(ColorMan, 1); cm->imd = imd; cm->pixbuf = pixbuf; if (cm->pixbuf) g_object_ref(cm->pixbuf); has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE; cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len, screen_type, screen_file, screen_data, screen_data_len, has_alpha); if (!cm->profile) { color_man_free(cm); return NULL; } return cm; } ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf, ColorManProfileType input_type, const gchar *input_file, ColorManProfileType screen_type, const gchar *screen_file, guchar *screen_data, guint screen_data_len) { return color_man_new_real(imd, pixbuf, input_type, input_file, NULL, 0, screen_type, screen_file, screen_data, screen_data_len); } void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data) { cm->func_done = done_func; cm->func_done_data = done_data; cm->idle_id = g_idle_add(color_man_idle_cb, cm); } ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf, guchar *input_data, guint input_data_len, ColorManProfileType screen_type, const gchar *screen_file, guchar *screen_data, guint screen_data_len) { return color_man_new_real(imd, pixbuf, COLOR_PROFILE_MEM, NULL, input_data, input_data_len, screen_type, screen_file, screen_data, screen_data_len); } static gchar *color_man_get_profile_name(ColorManProfileType type, cmsHPROFILE profile) { switch (type) { case COLOR_PROFILE_SRGB: return g_strdup(_("sRGB")); case COLOR_PROFILE_ADOBERGB: return g_strdup(_("Adobe RGB compatible")); break; case COLOR_PROFILE_MEM: case COLOR_PROFILE_FILE: if (profile) { return g_strdup(cmsTakeProductName(profile)); } return g_strdup(_("Custom profile")); break; case COLOR_PROFILE_NONE: default: return g_strdup(""); } } gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile) { ColorManCache *cc; if (!cm) return FALSE; cc = cm->profile; if (image_profile) *image_profile = color_man_get_profile_name(cc->profile_in_type, cc->profile_in); if (screen_profile) *screen_profile = color_man_get_profile_name(cc->profile_out_type, cc->profile_out); return TRUE; } void color_man_free(ColorMan *cm) { if (!cm) return; if (cm->idle_id) g_source_remove(cm->idle_id); if (cm->pixbuf) g_object_unref(cm->pixbuf); color_man_cache_unref(cm->profile); g_free(cm); } void color_man_update(void) { color_man_cache_reset(); } #else /* define HAVE_LCMS */ /*** color support not enabled ***/ ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf, ColorManProfileType input_type, const gchar *input_file, ColorManProfileType screen_type, const gchar *screen_file, guchar *screen_data, guint screen_data_len) { /* no op */ return NULL; } ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf, guchar *input_data, guint input_data_len, ColorManProfileType screen_type, const gchar *screen_file, guchar *screen_data, guint screen_data_len) { /* no op */ return NULL; } void color_man_free(ColorMan *cm) { /* no op */ } void color_man_update(void) { /* no op */ } void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h) { /* no op */ } void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data) { /* no op */ } gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile) { return FALSE; } #endif /* define HAVE_LCMS */ /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */