113
|
1 /*
|
|
2 * GQview
|
|
3 * (C) 2006 John Ellis
|
|
4 *
|
|
5 * Author: John Ellis
|
|
6 *
|
|
7 * This software is released under the GNU General Public License (GNU GPL).
|
|
8 * Please read the included file COPYING for more information.
|
|
9 * This software comes with no warranty of any kind, use at your own risk!
|
|
10 */
|
|
11
|
|
12
|
|
13 #include "gqview.h"
|
|
14 #include "color-man.h"
|
|
15
|
|
16 #include "image.h"
|
|
17 #include "ui_fileops.h"
|
|
18
|
|
19
|
|
20 #ifdef HAVE_LCMS
|
|
21 /*** color support enabled ***/
|
|
22
|
|
23 #ifdef HAVE_LCMS_LCMS_H
|
|
24 #include <lcms/lcms.h>
|
|
25 #else
|
|
26 #include <lcms.h>
|
|
27 #endif
|
|
28
|
|
29
|
|
30 typedef struct _ColorManCache ColorManCache;
|
|
31 struct _ColorManCache {
|
|
32 cmsHPROFILE profile_in;
|
|
33 cmsHPROFILE profile_out;
|
|
34 cmsHTRANSFORM transform;
|
|
35
|
|
36 ColorManProfileType profile_in_type;
|
|
37 gchar *profile_in_file;
|
|
38
|
|
39 ColorManProfileType profile_out_type;
|
|
40 gchar *profile_out_file;
|
|
41
|
|
42 gint has_alpha;
|
|
43
|
|
44 gint refcount;
|
|
45 };
|
|
46
|
|
47 /* pixels to transform per idle call */
|
|
48 #define COLOR_MAN_CHUNK_SIZE 81900
|
|
49
|
|
50
|
|
51 static void color_man_lib_init(void)
|
|
52 {
|
|
53 static gint init_done = FALSE;
|
|
54
|
|
55 if (init_done) return;
|
|
56 init_done = TRUE;
|
|
57
|
|
58 cmsErrorAction(LCMS_ERROR_IGNORE);
|
|
59 }
|
|
60
|
|
61
|
|
62 /*
|
|
63 *-------------------------------------------------------------------
|
|
64 * color transform cache
|
|
65 *-------------------------------------------------------------------
|
|
66 */
|
|
67
|
|
68 static GList *cm_cache_list = NULL;
|
|
69
|
|
70
|
|
71 static void color_man_cache_ref(ColorManCache *cc)
|
|
72 {
|
|
73 if (!cc) return;
|
|
74
|
|
75 cc->refcount++;
|
|
76 }
|
|
77
|
|
78 static void color_man_cache_unref(ColorManCache *cc)
|
|
79 {
|
|
80 if (!cc) return;
|
|
81
|
|
82 cc->refcount--;
|
|
83 if (cc->refcount < 1)
|
|
84 {
|
|
85 if (cc->transform) cmsDeleteTransform(cc->transform);
|
|
86 if (cc->profile_in) cmsCloseProfile(cc->profile_in);
|
|
87 if (cc->profile_out) cmsCloseProfile(cc->profile_out);
|
|
88
|
|
89 g_free(cc->profile_in_file);
|
|
90 g_free(cc->profile_out_file);
|
|
91
|
|
92 g_free(cc);
|
|
93 }
|
|
94 }
|
|
95
|
|
96 static cmsHPROFILE color_man_cache_load_profile(ColorManProfileType type, const gchar *file)
|
|
97 {
|
|
98 cmsHPROFILE profile = NULL;
|
|
99
|
|
100 switch (type)
|
|
101 {
|
|
102 case COLOR_PROFILE_FILE:
|
|
103 if (file)
|
|
104 {
|
|
105 gchar *pathl;
|
|
106
|
|
107 pathl = path_from_utf8(file);
|
|
108 profile = cmsOpenProfileFromFile(pathl, "r");
|
|
109 g_free(pathl);
|
|
110 }
|
|
111 break;
|
|
112 case COLOR_PROFILE_SRGB:
|
|
113 profile = cmsCreate_sRGBProfile();
|
|
114 break;
|
|
115 case COLOR_PROFILE_NONE:
|
|
116 default:
|
|
117 break;
|
|
118 }
|
|
119
|
|
120 return profile;
|
|
121 }
|
|
122
|
|
123 static ColorManCache *color_man_cache_new(ColorManProfileType in_type, const gchar *in_file,
|
|
124 ColorManProfileType out_type, const gchar *out_file,
|
|
125 gint has_alpha)
|
|
126 {
|
|
127 ColorManCache *cc;
|
|
128
|
|
129 color_man_lib_init();
|
|
130
|
|
131 cc = g_new0(ColorManCache, 1);
|
|
132 cc->refcount = 1;
|
|
133
|
|
134 cc->profile_in_type = in_type;
|
|
135 cc->profile_in_file = g_strdup(in_file);
|
|
136
|
|
137 cc->profile_out_type = out_type;
|
|
138 cc->profile_out_file = g_strdup(out_file);
|
|
139
|
|
140 cc->has_alpha = has_alpha;
|
|
141
|
|
142 cc->profile_in = color_man_cache_load_profile(cc->profile_in_type, cc->profile_in_file);
|
|
143 cc->profile_out = color_man_cache_load_profile(cc->profile_out_type, cc->profile_out_file);
|
|
144
|
|
145 if (!cc->profile_in || !cc->profile_out)
|
|
146 {
|
|
147 if (debug) printf("failed to load color profile for %s: %d %s\n",
|
|
148 (!cc->profile_in) ? "input" : "screen",
|
|
149 (!cc->profile_in) ? cc->profile_in_type : cc->profile_out_type,
|
|
150 (!cc->profile_in) ? cc->profile_in_file : cc->profile_out_file);
|
|
151
|
|
152 color_man_cache_unref(cc);
|
|
153 return NULL;
|
|
154 }
|
|
155
|
|
156 cc->transform = cmsCreateTransform(cc->profile_in,
|
|
157 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
|
|
158 cc->profile_out,
|
|
159 (has_alpha) ? TYPE_RGBA_8 : TYPE_RGB_8,
|
|
160 INTENT_PERCEPTUAL, 0);
|
|
161
|
|
162 if (!cc->transform)
|
|
163 {
|
|
164 if (debug) printf("failed to create color profile transform\n");
|
|
165
|
|
166 color_man_cache_unref(cc);
|
|
167 return NULL;
|
|
168 }
|
|
169
|
|
170 cm_cache_list = g_list_append(cm_cache_list, cc);
|
|
171
|
|
172 return cc;
|
|
173 }
|
|
174
|
|
175 static void color_man_cache_free(ColorManCache *cc)
|
|
176 {
|
|
177 if (!cc) return;
|
|
178
|
|
179 cm_cache_list = g_list_remove(cm_cache_list, cc);
|
|
180 color_man_cache_unref(cc);
|
|
181 }
|
|
182
|
|
183 static void color_man_cache_reset(void)
|
|
184 {
|
|
185 while (cm_cache_list)
|
|
186 {
|
|
187 ColorManCache *cc;
|
|
188
|
|
189 cc = cm_cache_list->data;
|
|
190 color_man_cache_free(cc);
|
|
191 }
|
|
192 }
|
|
193
|
|
194 static ColorManCache *color_man_cache_find(ColorManProfileType in_type, const gchar *in_file,
|
|
195 ColorManProfileType out_type, const gchar *out_file,
|
|
196 gint has_alpha)
|
|
197 {
|
|
198 GList *work;
|
|
199
|
|
200 work = cm_cache_list;
|
|
201 while (work)
|
|
202 {
|
|
203 ColorManCache *cc;
|
|
204 gint match = FALSE;
|
|
205
|
|
206 cc = work->data;
|
|
207 work = work->next;
|
|
208
|
|
209 if (cc->profile_in_type == in_type &&
|
|
210 cc->profile_out_type == out_type &&
|
|
211 cc->has_alpha == has_alpha)
|
|
212 {
|
|
213 match = TRUE;
|
|
214 }
|
|
215
|
|
216 if (match && cc->profile_in_type == COLOR_PROFILE_FILE)
|
|
217 {
|
|
218 match = (cc->profile_in_file && in_file &&
|
|
219 strcmp(cc->profile_in_file, in_file) == 0);
|
|
220 }
|
|
221 if (match && cc->profile_out_type == COLOR_PROFILE_FILE)
|
|
222 {
|
|
223 match = (cc->profile_out_file && out_file &&
|
|
224 strcmp(cc->profile_out_file, out_file) == 0);
|
|
225 }
|
|
226
|
|
227 if (match) return cc;
|
|
228 }
|
|
229
|
|
230 return NULL;
|
|
231 }
|
|
232
|
|
233 static ColorManCache *color_man_cache_get(ColorManProfileType in_type, const gchar *in_file,
|
|
234 ColorManProfileType out_type, const gchar *out_file,
|
|
235 gint has_alpha)
|
|
236 {
|
|
237 ColorManCache *cc;
|
|
238
|
|
239 cc = color_man_cache_find(in_type, in_file, out_type, out_file, has_alpha);
|
|
240
|
|
241 if (!cc)
|
|
242 {
|
|
243 cc = color_man_cache_new(in_type, in_file, out_type, out_file, has_alpha);
|
|
244 }
|
|
245
|
|
246 return cc;
|
|
247 }
|
|
248
|
|
249
|
|
250 /*
|
|
251 *-------------------------------------------------------------------
|
|
252 * color manager
|
|
253 *-------------------------------------------------------------------
|
|
254 */
|
|
255
|
|
256 static void color_man_done(ColorMan *cm, ColorManReturnType type)
|
|
257 {
|
|
258 if (cm->func_done)
|
|
259 {
|
|
260 cm->func_done(cm, type, cm->func_done_data);
|
|
261 }
|
|
262 }
|
|
263
|
|
264 static void color_man_correct_region(ColorMan *cm, gint x, gint y, gint w, gint h,
|
|
265 gint pixbuf_width, gint pixbuf_height)
|
|
266 {
|
|
267 ColorManCache *cc;
|
|
268 guchar *pix;
|
|
269 gint rs;
|
|
270 gint i;
|
|
271
|
|
272 cc = cm->profile;
|
|
273
|
|
274 pix = gdk_pixbuf_get_pixels(cm->pixbuf);
|
|
275 rs = gdk_pixbuf_get_rowstride(cm->pixbuf);
|
|
276
|
|
277 w = MIN(w, pixbuf_width - x);
|
|
278 h = MIN(h, pixbuf_height - y);
|
|
279
|
|
280 pix += x * ((cc->has_alpha) ? 4 : 3);
|
|
281 for (i = 0; i < h; i++)
|
|
282 {
|
|
283 guchar *pbuf;
|
|
284
|
|
285 pbuf = pix + ((y + i) * rs);
|
|
286 cmsDoTransform(cc->transform, pbuf, pbuf, w);
|
|
287 }
|
|
288
|
|
289 image_area_changed(cm->imd, x, y, w, h);
|
|
290 }
|
|
291
|
|
292 static gint color_man_idle_cb(gpointer data)
|
|
293 {
|
|
294 ColorMan *cm = data;
|
|
295 gint width, height;
|
|
296 gint rh;
|
|
297
|
|
298 if (cm->pixbuf != image_get_pixbuf(cm->imd))
|
|
299 {
|
|
300 cm->idle_id = -1;
|
|
301 color_man_done(cm, COLOR_RETURN_IMAGE_CHANGED);
|
|
302 return FALSE;
|
|
303 }
|
|
304
|
|
305 width = gdk_pixbuf_get_width(cm->pixbuf);
|
|
306 height = gdk_pixbuf_get_height(cm->pixbuf);
|
|
307
|
|
308 if (cm->row > height)
|
|
309 {
|
|
310 cm->idle_id = -1;
|
|
311 color_man_done(cm, COLOR_RETURN_SUCCESS);
|
|
312 return FALSE;
|
|
313 }
|
|
314
|
|
315 rh = COLOR_MAN_CHUNK_SIZE / width + 1;
|
|
316 color_man_correct_region(cm, 0, cm->row, width, rh, width, height);
|
|
317 cm->row += rh;
|
|
318
|
|
319 return TRUE;
|
|
320 }
|
|
321
|
|
322 ColorMan *color_man_new(ImageWindow *imd,
|
|
323 ColorManProfileType input_type, const gchar *input_file,
|
|
324 ColorManProfileType screen_type, const gchar *screen_file,
|
|
325 ColorManDoneFunc done_func, gpointer done_data)
|
|
326 {
|
|
327 ColorMan *cm;
|
|
328 GdkPixbuf *pixbuf;
|
|
329 gint has_alpha;
|
|
330
|
|
331 if (!imd) return NULL;
|
|
332 if (input_type == COLOR_PROFILE_NONE || screen_type == COLOR_PROFILE_NONE) return NULL;
|
|
333
|
|
334 pixbuf = image_get_pixbuf(imd);
|
|
335 if (!pixbuf) return NULL;
|
|
336
|
|
337 cm = g_new0(ColorMan, 1);
|
|
338 cm->imd = imd;
|
|
339 cm->pixbuf = pixbuf;
|
|
340 cm->row = 0;
|
|
341 cm->idle_id = -1;
|
|
342
|
|
343 cm->func_done = done_func;
|
|
344 cm->func_done_data = done_data;
|
|
345
|
|
346 has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
|
|
347 cm->profile = color_man_cache_get(input_type, input_file, screen_type, screen_file, has_alpha);
|
|
348 if (!cm->profile)
|
|
349 {
|
|
350 color_man_free(cm);
|
|
351 return NULL;
|
|
352 }
|
|
353
|
|
354 color_man_cache_ref(cm->profile);
|
|
355
|
|
356 cm->idle_id = g_idle_add(color_man_idle_cb, cm);
|
|
357
|
|
358 return cm;
|
|
359 }
|
|
360
|
|
361 void color_man_free(ColorMan *cm)
|
|
362 {
|
|
363 if (!cm) return;
|
|
364
|
|
365 if (cm->idle_id != -1) g_source_remove(cm->idle_id);
|
|
366
|
|
367 color_man_cache_unref(cm->profile);
|
|
368
|
|
369 g_free(cm);
|
|
370 }
|
|
371
|
|
372 void color_man_update(void)
|
|
373 {
|
|
374 color_man_cache_reset();
|
|
375 }
|
|
376
|
|
377 #else
|
|
378 /*** color support not enabled ***/
|
|
379
|
|
380
|
|
381 ColorMan *color_man_new(ImageWindow *imd,
|
|
382 ColorManProfileType input_type, const gchar *input_file,
|
|
383 ColorManProfileType screen_type, const gchar *screen_file,
|
|
384 ColorManDoneFunc don_func, gpointer done_data)
|
|
385 {
|
|
386 /* no op */
|
|
387 return NULL;
|
|
388 }
|
|
389
|
|
390 void color_man_free(ColorMan *cm)
|
|
391 {
|
|
392 /* no op */
|
|
393 }
|
|
394
|
|
395 void color_man_update(void)
|
|
396 {
|
|
397 /* no op */
|
|
398 }
|
|
399
|
|
400
|
|
401 #endif
|
|
402
|
|
403
|