Mercurial > geeqie.yaz
annotate src/thumb.c @ 737:8a8873e7a552
Make shell command and its option rc file options instead of hardcoded strings.
This allows users to modify the shell command that execute "editors".
Two new options appear in rc file:
- shell.path (default to "/bin/sh")
- shell.options (default to "-c")
These options can only be changed from the rc file, not at runtime.
Tests are made to check that shell.path is not empty and lead to
an executable file.
author | zas_ |
---|---|
date | Thu, 22 May 2008 20:22:13 +0000 |
parents | 179a7224dde1 |
children | 9bd49e725ad3 |
rev | line source |
---|---|
1 | 1 /* |
196 | 2 * Geeqie |
9 | 3 * (C) 2004 John Ellis |
475 | 4 * Copyright (C) 2008 The Geeqie Team |
1 | 5 * |
6 * Author: John Ellis | |
7 * | |
9 | 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! | |
1 | 11 */ |
12 | |
9 | 13 |
281 | 14 #include "main.h" |
9 | 15 #include "thumb.h" |
1 | 16 |
9 | 17 #include "cache.h" |
18 #include "image-load.h" | |
586 | 19 #include "filedata.h" |
9 | 20 #include "pixbuf_util.h" |
21 #include "thumb_standard.h" | |
22 #include "ui_fileops.h" | |
1 | 23 |
9 | 24 #include <utime.h> |
25 | |
26 | |
27 static void thumb_loader_error_cb(ImageLoader *il, gpointer data); | |
28 static void thumb_loader_setup(ThumbLoader *tl, gchar *path); | |
29 | |
30 static gint normalize_thumb(gint *width, gint *height, gint max_w, gint max_h); | |
31 static GdkPixbuf *get_xv_thumbnail(gchar *thumb_filename, gint max_w, gint max_h); | |
32 | |
1 | 33 |
34 /* | |
35 *----------------------------------------------------------------------------- | |
36 * thumbnail routines: creation, caching, and maintenance (public) | |
37 *----------------------------------------------------------------------------- | |
38 */ | |
39 | |
9 | 40 static gint thumb_loader_save_to_cache(ThumbLoader *tl) |
1 | 41 { |
9 | 42 gchar *cache_dir; |
43 gint success = FALSE; | |
44 mode_t mode = 0755; | |
1 | 45 |
9 | 46 if (!tl || !tl->pixbuf) return FALSE; |
1 | 47 |
9 | 48 cache_dir = cache_get_location(CACHE_TYPE_THUMB, tl->path, FALSE, &mode); |
1 | 49 |
9 | 50 if (cache_ensure_dir_exists(cache_dir, mode)) |
1 | 51 { |
52 gchar *cache_path; | |
9 | 53 gchar *pathl; |
715 | 54 gchar *name = g_strconcat(filename_from_path(tl->path), GQ_CACHE_EXT_THUMB, NULL); |
9 | 55 |
715 | 56 cache_path = g_build_filename(cache_dir, name, NULL); |
57 g_free(name); | |
1 | 58 |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
59 DEBUG_1("Saving thumb: %s", cache_path); |
9 | 60 |
61 pathl = path_from_utf8(cache_path); | |
62 success = pixbuf_to_file_as_png(tl->pixbuf, pathl); | |
63 if (success) | |
1 | 64 { |
9 | 65 struct utimbuf ut; |
66 /* set thumb time to that of source file */ | |
67 | |
68 ut.actime = ut.modtime = filetime(tl->path); | |
69 if (ut.modtime > 0) | |
1 | 70 { |
9 | 71 utime(pathl, &ut); |
1 | 72 } |
73 } | |
74 else | |
75 { | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
76 DEBUG_1("Saving failed: %s", pathl); |
1 | 77 } |
9 | 78 |
79 g_free(pathl); | |
80 g_free(cache_path); | |
81 } | |
82 | |
83 g_free(cache_dir); | |
84 | |
85 return success; | |
86 } | |
1 | 87 |
9 | 88 static gint thumb_loader_mark_failure(ThumbLoader *tl) |
89 { | |
90 gchar *cache_dir; | |
91 gint success = FALSE; | |
92 mode_t mode = 0755; | |
442 | 93 |
9 | 94 if (!tl) return FALSE; |
95 | |
96 cache_dir = cache_get_location(CACHE_TYPE_THUMB, tl->path, FALSE, &mode); | |
1 | 97 |
9 | 98 if (cache_ensure_dir_exists(cache_dir, mode)) |
99 { | |
100 gchar *cache_path; | |
101 gchar *pathl; | |
102 FILE *f; | |
715 | 103 gchar *name = g_strconcat(filename_from_path(tl->path), GQ_CACHE_EXT_THUMB, NULL); |
9 | 104 |
715 | 105 cache_path = g_build_filename(cache_dir, name, NULL); |
106 g_free(name); | |
9 | 107 |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
108 DEBUG_1("marking thumb failure: %s", cache_path); |
9 | 109 |
110 pathl = path_from_utf8(cache_path); | |
111 f = fopen(pathl, "w"); | |
112 if (f) | |
1 | 113 { |
9 | 114 struct utimbuf ut; |
115 | |
512
f9bf33be53ff
Remove whitespace between function name and first parenthesis for the sake of consistency.
zas_
parents:
507
diff
changeset
|
116 fclose(f); |
1 | 117 |
9 | 118 ut.actime = ut.modtime = filetime(tl->path); |
119 if (ut.modtime > 0) | |
1 | 120 { |
9 | 121 utime(pathl, &ut); |
1 | 122 } |
123 | |
9 | 124 success = TRUE; |
125 } | |
126 | |
127 g_free(pathl); | |
128 g_free(cache_path); | |
129 } | |
130 | |
131 g_free(cache_dir); | |
132 return success; | |
133 } | |
134 | |
135 static void thumb_loader_percent_cb(ImageLoader *il, gdouble percent, gpointer data) | |
136 { | |
137 ThumbLoader *tl = data; | |
138 | |
139 tl->percent_done = percent; | |
140 | |
141 if (tl->func_progress) tl->func_progress(tl, tl->data); | |
142 } | |
143 | |
144 static void thumb_loader_done_cb(ImageLoader *il, gpointer data) | |
145 { | |
146 ThumbLoader *tl = data; | |
147 GdkPixbuf *pixbuf; | |
148 gint pw, ph; | |
149 gint save; | |
150 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
151 DEBUG_1("thumb done: %s", tl->path); |
1 | 152 |
9 | 153 pixbuf = image_loader_get_pixbuf(tl->il); |
154 if (!pixbuf) | |
155 { | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
156 DEBUG_1("...but no pixbuf: %s", tl->path); |
9 | 157 thumb_loader_error_cb(tl->il, tl); |
158 return; | |
159 } | |
160 | |
161 pw = gdk_pixbuf_get_width(pixbuf); | |
162 ph = gdk_pixbuf_get_height(pixbuf); | |
163 | |
164 if (tl->cache_hit && pw != tl->max_w && ph != tl->max_h) | |
165 { | |
166 /* requested thumbnail size may have changed, load original */ | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
167 DEBUG_1("thumbnail size mismatch, regenerating: %s", tl->path); |
9 | 168 tl->cache_hit = FALSE; |
169 | |
170 thumb_loader_setup(tl, tl->path); | |
1 | 171 |
9 | 172 if (!image_loader_start(tl->il, thumb_loader_done_cb, tl)) |
173 { | |
174 image_loader_free(tl->il); | |
175 tl->il = NULL; | |
176 | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
177 DEBUG_1("regeneration failure: %s", tl->path); |
9 | 178 thumb_loader_error_cb(tl->il, tl); |
179 } | |
180 return; | |
181 } | |
1 | 182 |
9 | 183 /* scale ?? */ |
184 | |
185 if (pw > tl->max_w || ph > tl->max_h) | |
186 { | |
187 gint w, h; | |
188 | |
14
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
189 if (((double)tl->max_w / pw) < ((double)tl->max_h / ph)) |
9 | 190 { |
191 w = tl->max_w; | |
14
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
192 h = (double)w / pw * ph; |
9 | 193 if (h < 1) h = 1; |
1 | 194 } |
195 else | |
196 { | |
9 | 197 h = tl->max_h; |
14
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
198 w = (double)h / ph * pw; |
9 | 199 if (w < 1) w = 1; |
1 | 200 } |
201 | |
333 | 202 tl->pixbuf = gdk_pixbuf_scale_simple(pixbuf, w, h, (GdkInterpType)options->thumbnails.quality); |
9 | 203 save = TRUE; |
1 | 204 } |
205 else | |
206 { | |
9 | 207 tl->pixbuf = pixbuf; |
208 gdk_pixbuf_ref(tl->pixbuf); | |
14
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
209 save = il->shrunk; |
9 | 210 } |
211 | |
212 /* save it ? */ | |
213 if (tl->cache_enable && save) | |
214 { | |
215 thumb_loader_save_to_cache(tl); | |
216 } | |
217 | |
218 if (tl->func_done) tl->func_done(tl, tl->data); | |
219 } | |
220 | |
221 static void thumb_loader_error_cb(ImageLoader *il, gpointer data) | |
222 { | |
223 ThumbLoader *tl = data; | |
224 | |
225 /* if at least some of the image is available, go to done_cb */ | |
226 if (image_loader_get_pixbuf(tl->il) != NULL) | |
227 { | |
228 thumb_loader_done_cb(il, data); | |
229 return; | |
1 | 230 } |
9 | 231 |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
232 DEBUG_1("thumb error: %s", tl->path); |
9 | 233 |
234 image_loader_free(tl->il); | |
235 tl->il = NULL; | |
236 | |
237 if (tl->func_error) tl->func_error(tl, tl->data); | |
238 } | |
239 | |
240 static gint thumb_loader_done_delay_cb(gpointer data) | |
241 { | |
242 ThumbLoader *tl = data; | |
243 | |
244 tl->idle_done_id = -1; | |
245 | |
246 if (tl->func_done) tl->func_done(tl, tl->data); | |
247 | |
248 return FALSE; | |
249 } | |
250 | |
251 static void thumb_loader_delay_done(ThumbLoader *tl) | |
252 { | |
253 if (tl->idle_done_id == -1) tl->idle_done_id = g_idle_add(thumb_loader_done_delay_cb, tl); | |
254 } | |
255 | |
256 static void thumb_loader_setup(ThumbLoader *tl, gchar *path) | |
257 { | |
258 image_loader_free(tl->il); | |
138 | 259 tl->il = image_loader_new(file_data_new_simple(path)); |
9 | 260 |
333 | 261 if (options->thumbnails.fast) |
14
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
262 { |
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
263 /* this will speed up jpegs by up to 3x in some cases */ |
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
264 image_loader_set_requested_size(tl->il, tl->max_w, tl->max_h); |
25335c62cd9b
##### Note: GQview CVS on sourceforge is not always up to date, please use #####
gqview
parents:
9
diff
changeset
|
265 } |
9 | 266 |
267 image_loader_set_error_func(tl->il, thumb_loader_error_cb, tl); | |
268 if (tl->func_progress) image_loader_set_percent_func(tl->il, thumb_loader_percent_cb, tl); | |
269 } | |
270 | |
271 void thumb_loader_set_callbacks(ThumbLoader *tl, | |
272 ThumbLoaderFunc func_done, | |
273 ThumbLoaderFunc func_error, | |
274 ThumbLoaderFunc func_progress, | |
275 gpointer data) | |
276 { | |
277 if (!tl) return; | |
278 | |
279 if (tl->standard_loader) | |
280 { | |
281 thumb_loader_std_set_callbacks((ThumbLoaderStd *)tl, | |
282 (ThumbLoaderStdFunc) func_done, | |
283 (ThumbLoaderStdFunc) func_error, | |
284 (ThumbLoaderStdFunc) func_progress, | |
285 data); | |
286 return; | |
287 } | |
288 | |
289 tl->func_done = func_done; | |
290 tl->func_error = func_error; | |
291 tl->func_progress = func_progress; | |
292 | |
293 tl->data = data; | |
294 } | |
295 | |
296 void thumb_loader_set_cache(ThumbLoader *tl, gint enable_cache, gint local, gint retry_failed) | |
297 { | |
442 | 298 if (!tl) return; |
9 | 299 |
300 if (tl->standard_loader) | |
301 { | |
302 thumb_loader_std_set_cache((ThumbLoaderStd *)tl, enable_cache, local, retry_failed); | |
303 return; | |
304 } | |
305 | |
306 tl->cache_enable = enable_cache; | |
307 #if 0 | |
308 tl->cache_local = local; | |
309 tl->cache_retry = retry_failed; | |
310 #endif | |
1 | 311 } |
312 | |
313 | |
9 | 314 gint thumb_loader_start(ThumbLoader *tl, const gchar *path) |
315 { | |
316 gchar *cache_path = NULL; | |
1 | 317 |
9 | 318 if (!tl) return FALSE; |
1 | 319 |
9 | 320 if (tl->standard_loader) |
1 | 321 { |
9 | 322 return thumb_loader_std_start((ThumbLoaderStd *)tl, path); |
323 } | |
324 | |
325 if (!tl->path && !path) return FALSE; | |
1 | 326 |
9 | 327 if (!tl->path) tl->path = g_strdup(path); |
1 | 328 |
9 | 329 if (tl->cache_enable) |
330 { | |
331 cache_path = cache_find_location(CACHE_TYPE_THUMB, tl->path); | |
332 | |
333 if (cache_path) | |
1 | 334 { |
9 | 335 if (cache_time_valid(cache_path, tl->path)) |
336 { | |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
337 DEBUG_1("Found in cache:%s", tl->path); |
1 | 338 |
9 | 339 if (filesize(cache_path) == 0) |
1 | 340 { |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
341 DEBUG_1("Broken image mark found:%s", cache_path); |
9 | 342 g_free(cache_path); |
343 return FALSE; | |
1 | 344 } |
9 | 345 |
506
fc9c8a3e1a8b
Handle the newline in DEBUG_N() macro instead of adding one
zas_
parents:
495
diff
changeset
|
346 DEBUG_1("Cache location:%s", cache_path); |
9 | 347 } |
348 else | |
349 { | |
350 g_free(cache_path); | |
351 cache_path = NULL; | |
1 | 352 } |
353 } | |
9 | 354 } |
355 | |
333 | 356 if (!cache_path && options->thumbnails.use_xvpics) |
9 | 357 { |
358 tl->pixbuf = get_xv_thumbnail(tl->path, tl->max_w, tl->max_h); | |
359 if (tl->pixbuf) | |
360 { | |
361 thumb_loader_delay_done(tl); | |
362 return TRUE; | |
363 } | |
364 } | |
365 | |
366 if (cache_path) | |
367 { | |
368 thumb_loader_setup(tl, cache_path); | |
369 g_free(cache_path); | |
370 tl->cache_hit = TRUE; | |
371 } | |
372 else | |
373 { | |
374 thumb_loader_setup(tl, tl->path); | |
375 } | |
376 | |
377 if (!image_loader_start(tl->il, thumb_loader_done_cb, tl)) | |
378 { | |
379 /* try from original if cache attempt */ | |
380 if (tl->cache_hit) | |
381 { | |
382 tl->cache_hit = FALSE; | |
694 | 383 log_printf("%s", _("Thumbnail image in cache failed to load, trying to recreate.\n")); |
9 | 384 |
385 thumb_loader_setup(tl, tl->path); | |
386 if (image_loader_start(tl->il, thumb_loader_done_cb, tl)) return TRUE; | |
387 } | |
388 /* mark failed thumbnail in cache with 0 byte file */ | |
389 if (tl->cache_enable) | |
390 { | |
391 thumb_loader_mark_failure(tl); | |
392 } | |
442 | 393 |
9 | 394 image_loader_free(tl->il); |
395 tl->il = NULL; | |
396 return FALSE; | |
397 } | |
398 | |
399 return TRUE; | |
400 } | |
401 | |
402 #if 0 | |
403 gint thumb_loader_to_pixmap(ThumbLoader *tl, GdkPixmap **pixmap, GdkBitmap **mask) | |
404 { | |
405 if (!tl || !tl->pixbuf) return -1; | |
406 | |
407 gdk_pixbuf_render_pixmap_and_mask(tl->pixbuf, pixmap, mask, 128); | |
408 | |
409 return thumb_loader_get_space(tl); | |
410 } | |
411 #endif | |
412 | |
413 GdkPixbuf *thumb_loader_get_pixbuf(ThumbLoader *tl, gint with_fallback) | |
414 { | |
415 GdkPixbuf *pixbuf; | |
416 | |
417 if (tl && tl->standard_loader) | |
418 { | |
419 return thumb_loader_std_get_pixbuf((ThumbLoaderStd *)tl, with_fallback); | |
420 } | |
421 | |
422 if (tl && tl->pixbuf) | |
423 { | |
424 pixbuf = tl->pixbuf; | |
425 g_object_ref(pixbuf); | |
426 } | |
427 else if (with_fallback) | |
428 { | |
429 gint w, h; | |
430 | |
431 pixbuf = pixbuf_inline(PIXBUF_INLINE_BROKEN); | |
432 w = gdk_pixbuf_get_width(pixbuf); | |
433 h = gdk_pixbuf_get_height(pixbuf); | |
434 if ((w > tl->max_w || h > tl->max_h) && | |
435 normalize_thumb(&w, &h, tl->max_w, tl->max_h)) | |
436 { | |
437 GdkPixbuf *tmp; | |
438 | |
439 tmp = pixbuf; | |
440 pixbuf = gdk_pixbuf_scale_simple(tmp, w, h, GDK_INTERP_NEAREST); | |
441 gdk_pixbuf_unref(tmp); | |
442 } | |
1 | 443 } |
9 | 444 else |
445 { | |
446 pixbuf = NULL; | |
447 } | |
448 | |
449 return pixbuf; | |
450 } | |
451 | |
452 #if 0 | |
453 gint thumb_loader_get_space(ThumbLoader *tl) | |
454 { | |
455 if (!tl) return 0; | |
456 | |
457 if (tl->pixbuf) return (tl->max_w - gdk_pixbuf_get_width(tl->pixbuf)); | |
458 | |
459 return tl->max_w; | |
460 } | |
461 #endif | |
462 | |
463 ThumbLoader *thumb_loader_new(gint width, gint height) | |
464 { | |
465 ThumbLoader *tl; | |
466 | |
333 | 467 if (options->thumbnails.spec_standard) |
9 | 468 { |
469 return (ThumbLoader *)thumb_loader_std_new(width, height); | |
470 } | |
471 | |
472 tl = g_new0(ThumbLoader, 1); | |
473 tl->standard_loader = FALSE; | |
474 tl->path = NULL; | |
333 | 475 tl->cache_enable = options->thumbnails.enable_caching; |
9 | 476 tl->cache_hit = FALSE; |
477 tl->percent_done = 0.0; | |
478 tl->max_w = width; | |
479 tl->max_h = height; | |
480 | |
481 tl->il = NULL; | |
482 | |
483 tl->idle_done_id = -1; | |
484 | |
485 return tl; | |
1 | 486 } |
487 | |
9 | 488 void thumb_loader_free(ThumbLoader *tl) |
489 { | |
490 if (!tl) return; | |
491 | |
492 if (tl->standard_loader) | |
493 { | |
494 thumb_loader_std_free((ThumbLoaderStd *)tl); | |
495 return; | |
496 } | |
497 | |
498 if (tl->pixbuf) gdk_pixbuf_unref(tl->pixbuf); | |
499 image_loader_free(tl->il); | |
500 g_free(tl->path); | |
501 | |
502 if (tl->idle_done_id != -1) g_source_remove(tl->idle_done_id); | |
503 | |
504 g_free(tl); | |
505 } | |
506 | |
507 #if 0 | |
508 gint thumb_from_xpm_d(const char **data, gint max_w, gint max_h, GdkPixmap **pixmap, GdkBitmap **mask) | |
509 { | |
510 GdkPixbuf *pixbuf; | |
511 gint w, h; | |
512 | |
513 pixbuf = gdk_pixbuf_new_from_xpm_data(data); | |
514 w = gdk_pixbuf_get_width(pixbuf); | |
515 h = gdk_pixbuf_get_height(pixbuf); | |
516 | |
517 if ((w > max_w || h > max_h) && | |
518 normalize_thumb(&w, &h, max_w, max_h)) | |
519 { | |
520 /* scale */ | |
521 GdkPixbuf *tmp; | |
522 | |
523 tmp = pixbuf; | |
524 pixbuf = gdk_pixbuf_scale_simple(tmp, w, h, GDK_INTERP_NEAREST); | |
525 gdk_pixbuf_unref(tmp); | |
526 } | |
527 | |
528 gdk_pixbuf_render_pixmap_and_mask(pixbuf, pixmap, mask, 128); | |
529 gdk_pixbuf_unref(pixbuf); | |
530 | |
531 return w; | |
532 } | |
533 #endif | |
534 | |
1 | 535 /* |
536 *----------------------------------------------------------------------------- | |
537 * xvpics thumbnail support, read-only (private) | |
538 *----------------------------------------------------------------------------- | |
539 */ | |
540 | |
541 /* | |
542 * xvpics code originally supplied by: | |
543 * "Diederen Damien" <D.Diederen@student.ulg.ac.be> | |
544 * | |
545 * Note: Code has been modified to fit the style of the other code, and to use | |
546 * a few more glib-isms. | |
9 | 547 * 08-28-2000: Updated to return a gdk_pixbuf, Imlib is dieing a death here. |
1 | 548 */ |
549 | |
550 #define XV_BUFFER 2048 | |
551 static guchar *load_xv_thumbnail(gchar *filename, gint *widthp, gint *heightp) | |
552 { | |
553 FILE *file; | |
554 gchar buffer[XV_BUFFER]; | |
555 guchar *data; | |
556 gint width, height, depth; | |
557 | |
558 file = fopen(filename, "rt"); | |
559 if(!file) return NULL; | |
560 | |
561 fgets(buffer, XV_BUFFER, file); | |
562 if(strncmp(buffer, "P7 332", 6) != 0) | |
563 { | |
564 fclose(file); | |
565 return NULL; | |
566 } | |
567 | |
516 | 568 while (fgets(buffer, XV_BUFFER, file) && buffer[0] == '#') /* do_nothing() */; |
1 | 569 |
570 if(sscanf(buffer, "%d %d %d", &width, &height, &depth) != 3) | |
571 { | |
572 fclose(file); | |
573 return NULL; | |
574 } | |
575 | |
576 data = g_new(guchar, width * height); | |
577 fread(data, 1, width * height, file); | |
578 | |
579 fclose(file); | |
580 *widthp = width; | |
581 *heightp = height; | |
582 return data; | |
583 } | |
584 #undef XV_BUFFER | |
585 | |
9 | 586 static gint normalize_thumb(gint *width, gint *height, gint max_w, gint max_h) |
1 | 587 { |
9 | 588 gdouble scale; |
589 gint new_w, new_h; | |
590 | |
591 scale = MIN((gdouble) max_w / *width, (gdouble) max_h / *height); | |
592 new_w = *width * scale; | |
593 new_h = *height * scale; | |
594 | |
595 if (new_w != *width || new_h != *height) | |
1 | 596 { |
9 | 597 *width = new_w; |
598 *height = new_h; | |
599 return TRUE; | |
1 | 600 } |
9 | 601 |
602 return FALSE; | |
1 | 603 } |
604 | |
9 | 605 static void free_rgb_buffer(guchar *pixels, gpointer data) |
606 { | |
607 g_free(pixels); | |
608 } | |
609 | |
610 static GdkPixbuf *get_xv_thumbnail(gchar *thumb_filename, gint max_w, gint max_h) | |
1 | 611 { |
612 gint width, height; | |
613 gchar *thumb_name; | |
715 | 614 gchar *path; |
615 gchar *directory; | |
616 gchar *name; | |
1 | 617 guchar *packed_data; |
618 | |
715 | 619 path = path_from_utf8(thumb_filename); |
620 directory = g_path_get_dirname(path); | |
621 name = g_path_get_basename(path); | |
622 | |
623 thumb_name = g_build_filename(directory, ".xvpics", name, NULL); | |
624 | |
625 g_free(name); | |
626 g_free(directory); | |
627 g_free(path); | |
1 | 628 |
629 packed_data = load_xv_thumbnail(thumb_name, &width, &height); | |
630 g_free(thumb_name); | |
631 | |
632 if(packed_data) | |
633 { | |
634 guchar *rgb_data; | |
9 | 635 GdkPixbuf *pixbuf; |
1 | 636 gint i; |
637 | |
638 rgb_data = g_new(guchar, width * height * 3); | |
517 | 639 for (i = 0; i < width * height; i++) |
1 | 640 { |
641 rgb_data[i * 3 + 0] = (packed_data[i] >> 5) * 36; | |
642 rgb_data[i * 3 + 1] = ((packed_data[i] & 28) >> 2) * 36; | |
643 rgb_data[i * 3 + 2] = (packed_data[i] & 3) * 85; | |
644 } | |
9 | 645 g_free(packed_data); |
1 | 646 |
9 | 647 pixbuf = gdk_pixbuf_new_from_data(rgb_data, GDK_COLORSPACE_RGB, FALSE, 8, |
648 width, height, 3 * width, free_rgb_buffer, NULL); | |
649 | |
650 if (normalize_thumb(&width, &height, max_w, max_h)) | |
651 { | |
652 /* scale */ | |
653 GdkPixbuf *tmp; | |
654 | |
655 tmp = pixbuf; | |
656 pixbuf = gdk_pixbuf_scale_simple(tmp, width, height, GDK_INTERP_NEAREST); | |
657 gdk_pixbuf_unref(tmp); | |
658 } | |
442 | 659 |
9 | 660 return pixbuf; |
1 | 661 } |
662 | |
9 | 663 return NULL; |
1 | 664 } |