2 * Copyright (C) 2006 John Ellis
3 * Copyright (C) 2008 - 2016 The Geeqie Team
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "color-man.h"
26 #include "ui_fileops.h"
30 /*** color support enabled ***/
39 typedef struct _ColorManCache ColorManCache;
40 struct _ColorManCache {
41 cmsHPROFILE profile_in;
42 cmsHPROFILE profile_out;
43 cmsHTRANSFORM transform;
45 ColorManProfileType profile_in_type;
46 gchar *profile_in_file;
48 ColorManProfileType profile_out_type;
49 gchar *profile_out_file;
56 /* pixels to transform per idle call */
57 #define COLOR_MAN_CHUNK_SIZE 81900
60 static void color_man_lib_init(void)
62 static gboolean init_done = FALSE;
64 if (init_done) return;
68 cmsErrorAction(LCMS_ERROR_IGNORE);
72 static cmsHPROFILE color_man_create_adobe_comp(void)
74 /* ClayRGB1998 is AdobeRGB compatible */
75 #include "ClayRGB1998_icc.h"
76 return cmsOpenProfileFromMem(ClayRGB1998_icc, ClayRGB1998_icc_len);
80 *-------------------------------------------------------------------
81 * color transform cache
82 *-------------------------------------------------------------------
85 static GList *cm_cache_list = NULL;
88 static void color_man_cache_ref(ColorManCache *cc)
95 static void color_man_cache_unref(ColorManCache *cc)
100 if (cc->refcount < 1)
102 if (cc->transform) cmsDeleteTransform(cc->transform);
103 if (cc->profile_in) cmsCloseProfile(cc->profile_in);
104 if (cc->profile_out) cmsCloseProfile(cc->profile_out);
106 g_free(cc->profile_in_file);
107 g_free(cc->profile_out_file);
113 static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file,
114 guchar *data, guint data_len)
116 cmsHPROFILE profile = NULL;
120 case COLOR_PROFILE_FILE:
125 pathl = path_from_utf8(file);
126 profile = cmsOpenProfileFromFile(pathl, "r");
130 case COLOR_PROFILE_SRGB:
131 profile = cmsCreate_sRGBProfile();
133 case COLOR_PROFILE_ADOBERGB:
134 profile = color_man_create_adobe_comp();
136 case COLOR_PROFILE_MEM:
139 profile = cmsOpenProfileFromMem(data, data_len);
142 case COLOR_PROFILE_NONE:
150 static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
151 guchar *in_data, guint in_data_len,
152 ColorManProfileType out_type, const gchar *out_file,
153 guchar *out_data, guint out_data_len,
158 color_man_lib_init();
160 cc = g_new0(ColorManCache, 1);
163 cc->profile_in_type = in_type;
164 cc->profile_in_file = g_strdup(in_file);
166 cc->profile_out_type = out_type;
167 cc->profile_out_file = g_strdup(out_file);
169 cc->has_alpha = has_alpha;
171 cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file,
172 in_data, in_data_len);
173 cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file,
174 out_data, out_data_len);
176 if (!cc->profile_in || !cc->profile_out)
178 DEBUG_1("failed to load color profile for %s: %d %s",
179 (!cc->profile_in) ? "input" : "screen",
180 (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
181 (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
183 color_man_cache_unref(cc);
187 cc->transform = cmsCreateTransform(cc->profile_in,
188 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
190 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
191 INTENT_PERCEPTUAL, 0);
195 DEBUG_1("failed to create color profile transform");
197 color_man_cache_unref(cc);
201 if (cc->profile_in_type != COLOR_PROFILE_MEM && cc->profile_out_type != COLOR_PROFILE_MEM )
203 cm_cache_list = g_list_append(cm_cache_list, cc);
204 color_man_cache_ref(cc);
210 static void color_man_cache_free(ColorManCache *cc)
214 cm_cache_list = g_list_remove(cm_cache_list, cc);
215 color_man_cache_unref(cc);
218 static void color_man_cache_reset(void)
220 while (cm_cache_list)
224 cc = cm_cache_list->data;
225 color_man_cache_free(cc);
229 static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
230 ColorManProfileType out_type, const gchar *out_file,
235 work = cm_cache_list;
239 gboolean match = FALSE;
244 if (cc->profile_in_type == in_type &&
245 cc->profile_out_type == out_type &&
246 cc->has_alpha == has_alpha)
251 if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
253 match = (cc->profile_in_file && in_file &&
254 strcmp(cc->profile_in_file, in_file) == 0);
256 if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
258 match = (cc->profile_out_file && out_file &&
259 strcmp(cc->profile_out_file, out_file) == 0);
262 if (match) return cc;
268 static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
269 guchar *in_data, guint in_data_len,
270 ColorManProfileType out_type, const gchar *out_file,
271 guchar *out_data, guint out_data_len,
276 cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
279 color_man_cache_ref(cc);
283 return color_man_cache_new(in_type, in_file, in_data, in_data_len,
284 out_type, out_file, out_data, out_data_len, has_alpha);
289 *-------------------------------------------------------------------
291 *-------------------------------------------------------------------
294 static void color_man_done(ColorMan *cm, ColorManReturnType type)
298 cm->func_done(cm, type, cm->func_done_data);
302 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
308 gint pixbuf_width, pixbuf_height;
311 pixbuf_width = gdk_pixbuf_get_width(pixbuf);
312 pixbuf_height = gdk_pixbuf_get_height(pixbuf);
316 pix = gdk_pixbuf_get_pixels(pixbuf);
317 rs = gdk_pixbuf_get_rowstride(pixbuf);
319 w = MIN(w, pixbuf_width - x);
320 h = MIN(h, pixbuf_height - y);
322 pix += x * ((cc->has_alpha) ? 4 : 3);
323 for (i = 0; i < h; i++)
327 pbuf = pix + ((y + i) * rs);
329 cmsDoTransform(cc->transform, pbuf, pbuf, w);
334 static gboolean color_man_idle_cb(gpointer data)
340 if (!cm->pixbuf) return FALSE;
343 cm->pixbuf != image_get_pixbuf(cm->imd))
346 color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
350 width = gdk_pixbuf_get_width(cm->pixbuf);
351 height = gdk_pixbuf_get_height(cm->pixbuf);
353 if (cm->row > height)
355 if (!cm->incremental_sync && cm->imd)
357 image_area_changed(cm->imd, 0, 0, width, height);
361 color_man_done(cm, COLOR_RETURN_SUCCESS);
365 rh = COLOR_MAN_CHUNK_SIZE / width + 1;
366 color_man_correct_region(cm, cm->pixbuf, 0, cm->row, width, rh);
367 if (cm->incremental_sync && cm->imd) image_area_changed(cm->imd, 0, cm->row, width, rh);
373 static ColorMan *color_man_new_real(ImageWindow *imd, GdkPixbuf *pixbuf,
374 ColorManProfileType input_type, const gchar *input_file,
375 guchar *input_data, guint input_data_len,
376 ColorManProfileType screen_type, const gchar *screen_file,
377 guchar *screen_data, guint screen_data_len)
382 if (imd) pixbuf = image_get_pixbuf(imd);
384 cm = g_new0(ColorMan, 1);
387 if (cm->pixbuf) g_object_ref(cm->pixbuf);
389 has_alpha = pixbuf ? gdk_pixbuf_get_has_alpha(pixbuf) : FALSE;
391 cm->profile = color_man_cache_get(input_type, input_file, input_data, input_data_len,
392 screen_type, screen_file, screen_data, screen_data_len, has_alpha);
402 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
403 ColorManProfileType input_type, const gchar *input_file,
404 ColorManProfileType screen_type, const gchar *screen_file,
405 guchar *screen_data, guint screen_data_len)
407 return color_man_new_real(imd, pixbuf,
408 input_type, input_file, NULL, 0,
409 screen_type, screen_file, screen_data, screen_data_len);
412 void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
414 cm->func_done = done_func;
415 cm->func_done_data = done_data;
416 cm->idle_id = g_idle_add(color_man_idle_cb, cm);
419 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
420 guchar *input_data, guint input_data_len,
421 ColorManProfileType screen_type, const gchar *screen_file,
422 guchar *screen_data, guint screen_data_len)
424 return color_man_new_real(imd, pixbuf,
425 COLOR_PROFILE_MEM, NULL, input_data, input_data_len,
426 screen_type, screen_file, screen_data, screen_data_len);
429 static gchar *color_man_get_profile_name(ColorManProfileType type, cmsHPROFILE profile)
433 case COLOR_PROFILE_SRGB:
434 return g_strdup(_("sRGB"));
435 case COLOR_PROFILE_ADOBERGB:
436 return g_strdup(_("Adobe RGB compatible"));
438 case COLOR_PROFILE_MEM:
439 case COLOR_PROFILE_FILE:
446 r = cmsGetProfileInfoASCII(profile, cmsInfoDescription, "en", "US", buffer, 20);
447 buffer[19] = '\0'; /* Just to be sure */
448 return g_strdup(buffer);
450 return g_strdup(cmsTakeProductName(profile));
453 return g_strdup(_("Custom profile"));
455 case COLOR_PROFILE_NONE:
461 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
464 if (!cm) return FALSE;
468 if (image_profile) *image_profile = color_man_get_profile_name(cc->profile_in_type, cc->profile_in);
469 if (screen_profile) *screen_profile = color_man_get_profile_name(cc->profile_out_type, cc->profile_out);
473 void color_man_free(ColorMan *cm)
477 if (cm->idle_id) g_source_remove(cm->idle_id);
478 if (cm->pixbuf) g_object_unref(cm->pixbuf);
480 color_man_cache_unref(cm->profile);
485 void color_man_update(void)
487 color_man_cache_reset();
490 #else /* define HAVE_LCMS */
491 /*** color support not enabled ***/
494 ColorMan *color_man_new(ImageWindow *imd, GdkPixbuf *pixbuf,
495 ColorManProfileType input_type, const gchar *input_file,
496 ColorManProfileType screen_type, const gchar *screen_file,
497 guchar *screen_data, guint screen_data_len)
503 ColorMan *color_man_new_embedded(ImageWindow *imd, GdkPixbuf *pixbuf,
504 guchar *input_data, guint input_data_len,
505 ColorManProfileType screen_type, const gchar *screen_file,
506 guchar *screen_data, guint screen_data_len)
512 void color_man_free(ColorMan *cm)
517 void color_man_update(void)
522 void color_man_correct_region(ColorMan *cm, GdkPixbuf *pixbuf, gint x, gint y, gint w, gint h)
527 void color_man_start_bg(ColorMan *cm, ColorManDoneFunc done_func, gpointer done_data)
532 gboolean color_man_get_status(ColorMan *cm, gchar **image_profile, gchar **screen_profile)
537 #endif /* define HAVE_LCMS */
538 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */