4 * Copyright (C) 2008 - 2012 The Geeqie Team
8 * This software is released under the GNU General Public License (GNU GPL).
9 * Please read the included file COPYING for more information.
10 * This software comes with no warranty of any kind, use at your own risk!
15 #include "color-man.h"
18 #include "ui_fileops.h"
22 /*** color support enabled ***/
31 typedef struct _ColorManCache ColorManCache;
32 struct _ColorManCache {
33 cmsHPROFILE profile_in;
34 cmsHPROFILE profile_out;
35 cmsHTRANSFORM transform;
37 ColorManProfileType profile_in_type;
38 gchar *profile_in_file;
40 ColorManProfileType profile_out_type;
41 gchar *profile_out_file;
48 /* pixels to transform per idle call */
49 #define COLOR_MAN_CHUNK_SIZE 81900
52 static void color_man_lib_init(void)
54 static gboolean init_done = FALSE;
56 if (init_done) return;
60 cmsErrorAction(LCMS_ERROR_IGNORE);
64 static cmsHPROFILE color_man_create_adobe_comp(void)
66 /* ClayRGB1998 is AdobeRGB compatible */
67 #include "ClayRGB1998_icc.h"
68 return cmsOpenProfileFromMem(ClayRGB1998_icc, ClayRGB1998_icc_len);
72 *-------------------------------------------------------------------
73 * color transform cache
74 *-------------------------------------------------------------------
77 static GList *cm_cache_list = NULL;
80 static void color_man_cache_ref(ColorManCache *cc)
87 static void color_man_cache_unref(ColorManCache *cc)
94 if (cc->transform) cmsDeleteTransform(cc->transform);
95 if (cc->profile_in) cmsCloseProfile(cc->profile_in);
96 if (cc->profile_out) cmsCloseProfile(cc->profile_out);
98 g_free(cc->profile_in_file);
99 g_free(cc->profile_out_file);
105 static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file,
106 guchar *data, guint data_len)
108 cmsHPROFILE profile = NULL;
112 case COLOR_PROFILE_FILE:
117 pathl = path_from_utf8(file);
118 profile = cmsOpenProfileFromFile(pathl, "r");
122 case COLOR_PROFILE_SRGB:
123 profile = cmsCreate_sRGBProfile();
125 case COLOR_PROFILE_ADOBERGB:
126 profile = color_man_create_adobe_comp();
128 case COLOR_PROFILE_MEM:
131 profile = cmsOpenProfileFromMem(data, data_len);
134 case COLOR_PROFILE_NONE:
142 static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
143 guchar *in_data, guint in_data_len,
144 ColorManProfileType out_type, const gchar *out_file,
145 guchar *out_data, guint out_data_len,
150 color_man_lib_init();
152 cc = g_new0(ColorManCache, 1);
155 cc->profile_in_type = in_type;
156 cc->profile_in_file = g_strdup(in_file);
158 cc->profile_out_type = out_type;
159 cc->profile_out_file = g_strdup(out_file);
161 cc->has_alpha = has_alpha;
163 cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file,
164 in_data, in_data_len);
165 cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file,
166 out_data, out_data_len);
168 if (!cc->profile_in || !cc->profile_out)
170 DEBUG_1("failed to load color profile for %s: %d %s",
171 (!cc->profile_in) ? "input" : "screen",
172 (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
173 (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
175 color_man_cache_unref(cc);
179 cc->transform = cmsCreateTransform(cc->profile_in,
180 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
182 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
183 INTENT_PERCEPTUAL, 0);
187 DEBUG_1("failed to create color profile transform");
189 color_man_cache_unref(cc);
193 if (cc->profile_in_type != COLOR_PROFILE_MEM && cc->profile_out_type != COLOR_PROFILE_MEM )
195 cm_cache_list = g_list_append(cm_cache_list, cc);
196 color_man_cache_ref(cc);
202 static void color_man_cache_free(ColorManCache *cc)
206 cm_cache_list = g_list_remove(cm_cache_list, cc);
207 color_man_cache_unref(cc);
210 static void color_man_cache_reset(void)
212 while (cm_cache_list)
216 cc = cm_cache_list->data;
217 color_man_cache_free(cc);
221 static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
222 ColorManProfileType out_type, const gchar *out_file,
227 work = cm_cache_list;
231 gboolean match = FALSE;
236 if (cc->profile_in_type == in_type &&
237 cc->profile_out_type == out_type &&
238 cc->has_alpha == has_alpha)
243 if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
245 match = (cc->profile_in_file && in_file &&
246 strcmp(cc->profile_in_file, in_file) == 0);
248 if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
250 match = (cc->profile_out_file && out_file &&
251 strcmp(cc->profile_out_file, out_file) == 0);
254 if (match) return cc;
260 static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
261 guchar *in_data, guint in_data_len,
262 ColorManProfileType out_type, const gchar *out_file,
263 guchar *out_data, guint out_data_len,
268 cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
271 color_man_cache_ref(cc);
275 return color_man_cache_new(in_type, in_file, in_data, in_data_len,
276 out_type, out_file, out_data, out_data_len, has_alpha);
281 *-------------------------------------------------------------------
283 *-------------------------------------------------------------------
286 static void color_man_done(ColorMan *cm, ColorManReturnType type)
290 cm->func_done(cm, type, cm->func_done_data);
294 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
300 gint pixbuf_width, pixbuf_height;
303 pixbuf_width = gdk_pixbuf_get_width(pixbuf);
304 pixbuf_height = gdk_pixbuf_get_height(pixbuf);
308 pix = gdk_pixbuf_get_pixels(pixbuf);
309 rs = gdk_pixbuf_get_rowstride(pixbuf);
311 w = MIN(w, pixbuf_width - x);
312 h = MIN(h, pixbuf_height - y);
314 pix += x * ((cc->has_alpha) ? 4 : 3);
315 for (i = 0; i < h; i++)
319 pbuf = pix + ((y + i) * rs);
321 cmsDoTransform(cc->transform, pbuf, pbuf, w);
326 static gboolean color_man_idle_cb(gpointer data)
332 if (!cm->pixbuf) return FALSE;
335 cm->pixbuf != image_get_pixbuf(cm->imd))
338 color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
342 width = gdk_pixbuf_get_width(cm->pixbuf);
343 height = gdk_pixbuf_get_height(cm->pixbuf);
345 if (cm->row > height)
347 if (!cm->incremental_sync && cm->imd)
349 image_area_changed(cm->imd, 0, 0, width, height);
353 color_man_done(cm, COLOR_RETURN_SUCCESS);
357 rh = COLOR_MAN_CHUNK_SIZE / width + 1;
358 color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh);
359 if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh);
365 static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf,
366 ColorManProfileType input_type, const gchar *input_file,
367 guchar *input_data, guint input_data_len,
368 ColorManProfileType screen_type, const gchar *screen_file,
369 guchar *screen_data, guint screen_data_len)
374 if (imd) pixbuf = image_get_pixbuf(imd);
376 cm = g_new0(ColorMan, 1);
379 if (cm->pixbuf) g_object_ref(cm->pixbuf);
381 has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE;
383 cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
384 screen_type, screen_file, screen_data, screen_data_len, has_alpha);
394 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
395 ColorManProfileType input_type, const gchar *input_file,
396 ColorManProfileType screen_type, const gchar *screen_file,
397 guchar *screen_data, guint screen_data_len)
399 return color_man_new_real(imd, pixbuf,
400 input_type, input_file, NULL, 0,
401 screen_type, screen_file, screen_data, screen_data_len);
404 void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
406 cm->func_done = done_func;
407 cm->func_done_data = done_data;
408 cm->idle_id = g_idle_add(color_man_idle_cb, cm);
411 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
412 guchar *input_data, guint input_data_len,
413 ColorManProfileType screen_type, const gchar *screen_file,
414 guchar *screen_data, guint screen_data_len)
416 return color_man_new_real(imd, pixbuf,
417 COLOR_PROFILE_MEM, NULL, input_data, input_data_len,
418 screen_type, screen_file, screen_data, screen_data_len);
421 static gchar *color_man_get_profile_name(ColorManProfileType type, cmsHPROFILE profile)
425 case COLOR_PROFILE_SRGB:
426 return g_strdup(_("sRGB"));
427 case COLOR_PROFILE_ADOBERGB:
428 return g_strdup(_("Adobe RGB compatible"));
430 case COLOR_PROFILE_MEM:
431 case COLOR_PROFILE_FILE:
435 cmsUInt8Number profileID[17];
436 profileID[16] = '\0';
437 cmsGetHeaderProfileID(profile, profileID);
438 return g_strdup((gchar *) profileID);
440 return g_strdup(cmsTakeProductName(profile));
443 return g_strdup(_("Custom profile"));
445 case COLOR_PROFILE_NONE:
451 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
454 if (!cm) return FALSE;
458 if (image_profile) *image_profile = color_man_get_profile_name(cc->profile_in_type, cc->profile_in);
459 if (screen_profile) *screen_profile = color_man_get_profile_name(cc->profile_out_type, cc->profile_out);
463 void color_man_free(ColorMan *cm)
467 if (cm->idle_id) g_source_remove(cm->idle_id);
468 if (cm->pixbuf) g_object_unref(cm->pixbuf);
470 color_man_cache_unref(cm->profile);
475 void color_man_update(void)
477 color_man_cache_reset();
480 #else /* define HAVE_LCMS */
481 /*** color support not enabled ***/
484 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
485 ColorManProfileType input_type, const gchar *input_file,
486 ColorManProfileType screen_type, const gchar *screen_file,
487 guchar *screen_data, guint screen_data_len)
493 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
494 guchar *input_data, guint input_data_len,
495 ColorManProfileType screen_type, const gchar *screen_file,
496 guchar *screen_data, guint screen_data_len)
502 void color_man_free(ColorMan *cm)
507 void color_man_update(void)
512 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
517 void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
522 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
527 #endif /* define HAVE_LCMS */
528 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */