Mercurial > geeqie
comparison 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 |
comparison
equal
deleted
inserted
replaced
112:b15d4c18168f | 113:55166d93498d |
---|---|
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 |