4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* pixmaps.c - code for handling pixmaps */
27 /* Remove pixmaps from the cache when they haven't been accessed for
28 * this period of time (seconds).
31 #define PIXMAP_PURGE_TIME 1200
37 #include <sys/types.h>
41 #include <gdk-pixbuf/gdk-pixbuf.h>
42 #include <gdk-pixbuf/gdk-pixbuf-loader.h>
44 #ifdef THUMBS_USE_LIBPNG
48 # define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf)
56 #include "gui_support.h"
62 GFSCache
*pixmap_cache
= NULL
;
64 static const char * bad_xpm
[] = {
82 MaskedPixmap
*im_error
;
83 MaskedPixmap
*im_unknown
;
84 MaskedPixmap
*im_symlink
;
86 MaskedPixmap
*im_unmounted
;
87 MaskedPixmap
*im_mounted
;
88 MaskedPixmap
*im_multiple
;
89 MaskedPixmap
*im_appdir
;
91 MaskedPixmap
*im_help
;
92 MaskedPixmap
*im_dirs
;
94 /* Static prototypes */
96 static void load_default_pixmaps(void);
97 static MaskedPixmap
*load(char *pathname
, gpointer data
);
98 static void ref(MaskedPixmap
*mp
, gpointer data
);
99 static void unref(MaskedPixmap
*mp
, gpointer data
);
100 static int getref(MaskedPixmap
*mp
);
101 static gint
purge(gpointer data
);
102 static MaskedPixmap
*image_from_file(char *path
);
103 static MaskedPixmap
*get_bad_image(void);
104 static GdkPixbuf
*scale_pixbuf(GdkPixbuf
*src
, int max_w
, int max_h
);
105 static GdkPixbuf
*scale_pixbuf_up(GdkPixbuf
*src
, int max_w
, int max_h
);
106 static void got_thumb_data(GdkPixbufLoader
*loader
,
107 gint fd
, GdkInputCondition cond
);
108 static GdkPixbuf
*get_thumbnail_for(char *path
);
111 /****************************************************************
112 * EXTERNAL INTERFACE *
113 ****************************************************************/
115 void pixmaps_init(void)
119 gtk_widget_push_visual(gdk_rgb_get_visual());
121 gtk_widget_push_colormap(gdk_rgb_get_cmap());
123 pixmap_cache
= g_fscache_new((GFSLoadFunc
) load
,
126 (GFSGetRefFunc
) getref
,
127 NULL
, /* Update func */
130 gtk_timeout_add(10000, purge
, NULL
);
132 load_default_pixmaps();
135 /* 'name' is relative to app_dir. Always returns with a valid image. */
136 MaskedPixmap
*load_pixmap(char *name
)
138 MaskedPixmap
*retval
;
140 retval
= image_from_file(make_path(app_dir
, name
)->str
);
142 retval
= get_bad_image();
146 /* Load all the standard pixmaps */
147 static void load_default_pixmaps(void)
149 im_error
= load_pixmap("pixmaps/error.xpm");
150 im_unknown
= load_pixmap("pixmaps/unknown.xpm");
151 im_symlink
= load_pixmap("pixmaps/symlink.xpm");
153 im_unmounted
= load_pixmap("pixmaps/mount.xpm");
154 im_mounted
= load_pixmap("pixmaps/mounted.xpm");
155 im_multiple
= load_pixmap("pixmaps/multiple.xpm");
156 im_appdir
= load_pixmap("pixmaps/application.xpm");
158 im_help
= load_pixmap("pixmaps/help.xpm");
159 im_dirs
= load_pixmap("pixmaps/dirs.xpm");
162 void pixmap_ref(MaskedPixmap
*mp
)
167 void pixmap_unref(MaskedPixmap
*mp
)
172 void pixmap_make_huge(MaskedPixmap
*mp
)
179 g_return_if_fail(mp
->huge_pixbuf
!= NULL
);
181 hg
= scale_pixbuf_up(mp
->huge_pixbuf
,
187 gdk_pixbuf_render_pixmap_and_mask(hg
,
191 mp
->huge_width
= gdk_pixbuf_get_width(hg
);
192 mp
->huge_height
= gdk_pixbuf_get_height(hg
);
193 gdk_pixbuf_unref(hg
);
196 if (!mp
->huge_pixmap
)
198 gdk_pixmap_ref(mp
->pixmap
);
200 gdk_bitmap_ref(mp
->mask
);
201 mp
->huge_pixmap
= mp
->pixmap
;
202 mp
->huge_mask
= mp
->mask
;
203 mp
->huge_width
= mp
->width
;
204 mp
->huge_height
= mp
->height
;
208 void pixmap_make_small(MaskedPixmap
*mp
)
215 g_return_if_fail(mp
->huge_pixbuf
!= NULL
);
217 sm
= scale_pixbuf(mp
->huge_pixbuf
, SMALL_WIDTH
, SMALL_HEIGHT
);
221 gdk_pixbuf_render_pixmap_and_mask(sm
,
225 mp
->sm_width
= gdk_pixbuf_get_width(sm
);
226 mp
->sm_height
= gdk_pixbuf_get_height(sm
);
227 gdk_pixbuf_unref(sm
);
233 gdk_pixmap_ref(mp
->pixmap
);
235 gdk_bitmap_ref(mp
->mask
);
236 mp
->sm_pixmap
= mp
->pixmap
;
237 mp
->sm_mask
= mp
->mask
;
238 mp
->sm_width
= mp
->width
;
239 mp
->sm_height
= mp
->height
;
243 # define GET_LOADER(key) (g_object_get_data(G_OBJECT(loader), key))
244 # define SET_LOADER(key, data) (g_object_set_data(G_OBJECT(loader), key, data))
245 # define SET_LOADER_FULL(key, data, free) \
246 (g_object_set_data_full(G_OBJECT(loader), key, data, free))
248 # define GET_LOADER(key) (gtk_object_get_data(GTK_OBJECT(loader), key))
249 # define SET_LOADER(key, data) \
250 (gtk_object_set_data(GTK_OBJECT(loader), key, data))
251 # define SET_LOADER_FULL(key, data, free) \
252 (gtk_object_set_data_full(GTK_OBJECT(loader), key, data, free))
255 /* Load image 'path' in the background and insert into pixmap_cache.
256 * Call callback(data, path) when done (path is NULL => error).
257 * If the image is already uptodate, or being created already, calls the
258 * callback right away.
260 void pixmap_background_thumb(gchar
*path
, GFunc callback
, gpointer data
)
262 GdkPixbufLoader
*loader
;
268 image
= g_fscache_lookup_full(pixmap_cache
, path
,
269 FSCACHE_LOOKUP_ONLY_NEW
, &found
);
273 /* Thumbnail is known, or being created */
274 callback(data
, NULL
);
278 pixbuf
= get_thumbnail_for(path
);
283 image
= image_from_pixbuf(pixbuf
);
284 gdk_pixbuf_unref(pixbuf
);
285 g_fscache_insert(pixmap_cache
, path
, image
);
286 callback(data
, path
);
290 /* Add an entry, set to NULL, so no-one else tries to load this
293 g_fscache_insert(pixmap_cache
, path
, NULL
);
295 fd
= mc_open(path
, O_RDONLY
| O_NONBLOCK
);
298 callback(data
, NULL
);
302 loader
= gdk_pixbuf_loader_new();
304 SET_LOADER_FULL("thumb-path", g_strdup(path
), g_free
);
305 SET_LOADER("thumb-callback", callback
);
306 SET_LOADER("thumb-callback-data", data
);
308 tag
= gdk_input_add(fd
, GDK_INPUT_READ
,
309 (GdkInputFunction
) got_thumb_data
, loader
);
311 SET_LOADER("thumb-input-tag", GINT_TO_POINTER(tag
));
314 /****************************************************************
315 * INTERNAL FUNCTIONS *
316 ****************************************************************/
318 #ifdef THUMBS_USE_LIBPNG
319 /* Do this in a separate function to avoid problems with longjmp and
320 * non-volatile variables.
322 static gboolean
png_save(png_structp png_ptr
, png_infop info_ptr
,
323 FILE *file
, GdkPixbuf
*pixbuf
, GPtrArray
*textarr
)
332 /* An error during saving will cause a jump back to here */
333 if (setjmp(png_jmpbuf(png_ptr
)))
336 png_init_io(png_ptr
, file
);
338 pixels
= gdk_pixbuf_get_pixels(pixbuf
);
339 width
= gdk_pixbuf_get_width(pixbuf
);
340 height
= gdk_pixbuf_get_height(pixbuf
);
341 has_alpha
= gdk_pixbuf_get_has_alpha(pixbuf
);
342 bit_depth
= gdk_pixbuf_get_bits_per_sample(pixbuf
);
343 rowstride
= gdk_pixbuf_get_rowstride(pixbuf
);
345 png_set_IHDR(png_ptr
, info_ptr
, width
, height
, bit_depth
,
346 PNG_COLOR_TYPE_RGB_ALPHA
,
348 PNG_COMPRESSION_TYPE_DEFAULT
,
349 PNG_FILTER_TYPE_DEFAULT
);
352 /* Write image info */
353 png_write_info(png_ptr
, info_ptr
);
355 /* Write the image data */
360 for (row
= 0; row
< height
; row
++, pixels
+= rowstride
)
361 png_write_row(png_ptr
, (png_bytep
) pixels
);
363 else /* convert RGB to RGBA */
369 rowbuf
= g_malloc(width
* 4);
370 for (row
= 0; row
< height
; row
++, pixels
+= rowstride
)
372 guchar
*src
= pixels
;
373 guchar
*dst
= rowbuf
;
374 for (col
= 0; col
< width
; col
++)
381 png_write_row(png_ptr
, (png_bytep
) rowbuf
);
386 png_write_end(png_ptr
, info_ptr
);
391 /* Saves pixbuf to a PNG file (non-interlaced RGBA).
392 * The variable arguments are key-text pairs and must be NULL-terminated.
393 * NOTE: unlike with gdk_pixbuf_save, keys do NOT have to be prefixed
395 * Returns TRUE if the image was saved, FALSE otherwise.
397 static gboolean
pixbuf_save_png(GdkPixbuf
*pixbuf
, char *filename
, ...)
400 png_structp png_ptr
= NULL
;
401 png_infop info_ptr
= NULL
;
402 GPtrArray
*textarr
= NULL
;
403 gboolean retval
= FALSE
;
407 g_return_val_if_fail(pixbuf
!= NULL
, FALSE
);
408 g_return_val_if_fail(filename
!= NULL
, FALSE
);
409 g_return_val_if_fail(*filename
!= '\0', FALSE
);
411 file
= fopen(filename
, "wb");
415 png_ptr
= png_create_write_struct(PNG_LIBPNG_VER_STRING
,
420 info_ptr
= png_create_info_struct(png_ptr
);
424 /* Get optional key-text pairs. */
425 textarr
= g_ptr_array_new();
426 va_start(ap
, filename
);
427 string
= va_arg(ap
, char *);
428 while (string
!= NULL
)
432 pngtext
= g_new(png_text
, 1);
433 pngtext
->key
= string
;
434 pngtext
->text
= va_arg(ap
, char *);
435 pngtext
->compression
= PNG_TEXT_COMPRESSION_NONE
;
436 /* (lang and translated_keyword not needed for COMP_NONE) */
437 png_set_text(png_ptr
, info_ptr
, pngtext
, 1);
438 /* png_set_text() does NOT copy data, so we can't free
439 * it now -- store the pointers, they'll be freed later.
441 g_ptr_array_add(textarr
, pngtext
);
443 string
= va_arg(ap
, char *);
447 retval
= png_save(png_ptr
, info_ptr
, file
, pixbuf
, textarr
);
454 png_destroy_write_struct(&png_ptr
, &info_ptr
);
456 png_destroy_write_struct(&png_ptr
, (png_infopp
) NULL
);
459 g_ptr_array_free(textarr
, TRUE
);
462 g_warning("Error saving thumbnail '%s'", filename
);
467 static void destroy_key_value_fn(gpointer key
, gpointer value
, gpointer data
)
473 /* Frees the pixel array of a pixbuf and the hash table associated with it. */
474 static void pixbuf_destroy_fn(guchar
*pixels
, gpointer data
)
478 g_hash_table_foreach((GHashTable
*) data
,
479 destroy_key_value_fn
, NULL
);
480 g_hash_table_destroy((GHashTable
*) data
);
486 static GdkPixbuf
*png_load(png_structp png_ptr
, png_infop info_ptr
,
487 FILE *file
, GHashTable
*text_hash
)
489 png_uint_32 width
, height
, rowbytes
, row
;
490 int bit_depth
, color_type
, interlace_type
;
494 /* An error during loading will cause a jump back to here */
495 if (setjmp(png_jmpbuf(png_ptr
)))
498 png_init_io(png_ptr
, file
);
500 /* We've already read 8 bytes from the file */
501 png_set_sig_bytes(png_ptr
, 8);
503 png_read_info(png_ptr
, info_ptr
);
505 png_get_IHDR(png_ptr
, info_ptr
, &width
, &height
, &bit_depth
,
506 &color_type
, &interlace_type
, NULL
, NULL
);
508 if (interlace_type
!= PNG_INTERLACE_NONE
|| width
> 256 || height
> 256)
511 /* Change paletted images to RGB */
512 if (color_type
== PNG_COLOR_TYPE_PALETTE
)
513 png_set_palette_to_rgb(png_ptr
);
515 /* Transform grayscale images of less than 8 to 8 bits */
516 if (color_type
== PNG_COLOR_TYPE_GRAY
&& bit_depth
< 8)
517 png_set_gray_1_2_4_to_8(png_ptr
);
519 /* Add alpha channel if there's transparency info in the file */
520 if (png_get_valid(png_ptr
, info_ptr
, PNG_INFO_tRNS
))
521 png_set_tRNS_to_alpha(png_ptr
);
523 /* 16-bit RGB to 8-bit RGB */
525 png_set_strip_16(png_ptr
);
527 /* Convert RGB to RGBA with an opacity value of 255 */
528 if (bit_depth
== 8 && color_type
== PNG_COLOR_TYPE_RGB
)
529 png_set_filler(png_ptr
, 255, PNG_FILLER_AFTER
);
531 /* Expand bit depths of 1, 2 and 4 to 8 */
533 png_set_packing(png_ptr
);
535 /* Grayscale to RGB */
536 if (color_type
== PNG_COLOR_TYPE_GRAY
||
537 color_type
== PNG_COLOR_TYPE_GRAY_ALPHA
)
538 png_set_gray_to_rgb(png_ptr
);
540 png_read_update_info(png_ptr
, info_ptr
);
541 rowbytes
= png_get_rowbytes(png_ptr
, info_ptr
);
544 pixels
= g_malloc(rowbytes
* height
);
545 for (row
= 0, rowp
= pixels
; row
< height
; row
++, rowp
+= rowbytes
)
546 png_read_row(png_ptr
, rowp
, NULL
);
548 png_read_end(png_ptr
, NULL
);
550 /* Get key-text pairs and store them for later use */
556 png_get_text(png_ptr
, info_ptr
, &text_ptr
, &num_text
);
557 while (--num_text
>= 0)
559 g_hash_table_insert(text_hash
,
560 g_strdup(text_ptr
[num_text
].key
),
561 g_strdup(text_ptr
[num_text
].text
));
565 return gdk_pixbuf_new_from_data(pixels
,
566 GDK_COLORSPACE_RGB
, TRUE
, 8,
567 (int) width
, (int) height
, (int) rowbytes
,
568 pixbuf_destroy_fn
, text_hash
);
571 /* Creates a pixbuf from a PNG file and returns it. If 'text_hash' is not
572 * NULL, it will be set to point to a hash table storing the image comments
574 * This is a support function for the thumbnail standard, so it will refuse
575 * to load interlaced images or images larger than 256x256 pixels.
576 * The hash table will be automatically destroyed when you destroy the pixbuf.
578 static GdkPixbuf
*pixbuf_new_from_png(char *filename
, GHashTable
**text_hash
)
581 png_structp png_ptr
= NULL
;
582 png_infop info_ptr
= NULL
;
583 GdkPixbuf
*retval
= NULL
;
586 g_return_val_if_fail(filename
!= NULL
, NULL
);
587 g_return_val_if_fail(*filename
!= '\0', NULL
);
589 file
= fopen(filename
, "rb");
593 /* Is it a PNG file? */
594 if (fread(header
, 1, 8, file
) != 8 || png_sig_cmp(header
, 0, 8))
597 png_ptr
= png_create_read_struct(PNG_LIBPNG_VER_STRING
,
602 info_ptr
= png_create_info_struct(png_ptr
);
606 *text_hash
= g_hash_table_new(g_str_hash
, g_str_equal
);
608 retval
= png_load(png_ptr
, info_ptr
, file
, *text_hash
);
616 png_destroy_read_struct(&png_ptr
, &info_ptr
, NULL
);
618 png_destroy_read_struct(&png_ptr
, NULL
, NULL
);
622 g_warning("Error loading thumbnail '%s'", filename
);
626 #endif /* THUMBS_USE_LIBPNG */
628 #if defined(GTK2) || defined(THUMBS_USE_LIBPNG)
629 /* Create a thumbnail file for this image.
630 * XXX: Thumbnails should be deleted somewhere!
632 static void save_thumbnail(char *path
, GdkPixbuf
*full
, MaskedPixmap
*image
)
635 int original_width
, original_height
;
637 char *md5
, *swidth
, *sheight
, *ssize
, *smtime
, *uri
;
641 /* If the source image was very small, don't bother saving */
642 if (gdk_pixbuf_get_width(full
) * gdk_pixbuf_get_height(full
) <
643 (HUGE_WIDTH
* HUGE_HEIGHT
* 3))
646 original_width
= gdk_pixbuf_get_width(full
);
647 original_height
= gdk_pixbuf_get_height(full
);
649 if (mc_stat(path
, &info
) != 0)
652 swidth
= g_strdup_printf("%d", original_width
);
653 sheight
= g_strdup_printf("%d", original_height
);
654 ssize
= g_strdup_printf("%" SIZE_FMT
, info
.st_size
);
655 smtime
= g_strdup_printf("%ld", info
.st_mtime
);
657 path
= pathdup(path
);
658 uri
= g_strconcat("file://", path
, NULL
);
662 to
= g_string_new(home_dir
);
663 g_string_append(to
, "/.thumbnails");
664 mkdir(to
->str
, 0700);
665 g_string_append(to
, "/96x96/");
666 mkdir(to
->str
, 0700);
667 g_string_append(to
, md5
);
668 name_len
= to
->len
+ 4; /* Truncate to this length when renaming */
669 g_string_sprintfa(to
, ".png.ROX-Filer-%ld", (long) getpid());
673 old_mask
= umask(0077);
675 gdk_pixbuf_save(image
->huge_pixbuf
,
679 "tEXt::Thumb::Image::Width", swidth
,
680 "tEXt::Thumb::Image::Height", sheight
,
681 "tEXt::Thumb::Size", ssize
,
682 "tEXt::Thumb::MTime", smtime
,
683 "tEXt::Thumb::URI", uri
,
684 "tEXt::Software", PROJECT
,
687 pixbuf_save_png(image
->huge_pixbuf
,
689 "Thumb::Image::Width", swidth
,
690 "Thumb::Image::Height", sheight
,
691 "Thumb::Size", ssize
,
692 "Thumb::MTime", smtime
,
699 /* We create the file ###.png.ROX-Filer-PID and rename it to avoid
700 * a race condition if two program create the same thumb at
706 final
= g_strndup(to
->str
, name_len
);
707 if (rename(to
->str
, final
))
708 g_warning("Failed to rename '%s' to '%s': %s",
709 to
->str
, final
, g_strerror(errno
));
713 g_string_free(to
, TRUE
);
720 #endif /* GTK2 or THUMBS_USE_LIBPNG */
722 /* Check if we have an up-to-date thumbnail for this image.
723 * If so, return it. Otherwise, returns NULL.
725 static GdkPixbuf
*get_thumbnail_for(char *path
)
727 GdkPixbuf
*thumb
= NULL
;
728 #if defined(GTK2) || defined(THUMBS_USE_LIBPNG)
729 char *thumb_path
, *md5
, *uri
;
730 char *ssize
, *smtime
;
733 GHashTable
*text_hash
;
736 path
= pathdup(path
);
737 uri
= g_strconcat("file://", path
, NULL
);
741 thumb_path
= g_strdup_printf("%s/.thumbnails/96x96/%s.png",
746 thumb
= gdk_pixbuf_new_from_file(thumb_path
, NULL
);
748 thumb
= pixbuf_new_from_png(thumb_path
, &text_hash
);
753 /* Note that these don't need freeing... */
755 ssize
= gdk_pixbuf_get_option(thumb
, "tEXt::Thumb::Size");
757 ssize
= g_hash_table_lookup(text_hash
, "Thumb::Size");
763 smtime
= gdk_pixbuf_get_option(thumb
, "tEXt::Thumb::MTime");
765 smtime
= g_hash_table_lookup(text_hash
, "Thumb::MTime");
770 if (mc_stat(path
, &info
) != 0)
773 if (info
.st_mtime
!= atol(smtime
) || info
.st_size
!= atol(ssize
))
779 gdk_pixbuf_unref(thumb
);
784 #endif /* GTK2 or THUMBS_USE_LIBPNG */
788 /* Load the image 'path' and return a pointer to the resulting
789 * MaskedPixmap. NULL on failure.
791 static MaskedPixmap
*image_from_file(char *path
)
796 GError
*error
= NULL
;
798 pixbuf
= get_thumbnail_for(path
);
800 pixbuf
= gdk_pixbuf_new_from_file(path
, &error
);
803 g_print("%s\n", error
? error
->message
: _("Unknown error"));
806 #elif defined(THUMBS_USE_LIBPNG)
807 pixbuf
= get_thumbnail_for(path
);
809 pixbuf
= gdk_pixbuf_new_from_file(path
);
811 pixbuf
= gdk_pixbuf_new_from_file(path
);
816 image
= image_from_pixbuf(pixbuf
);
818 gdk_pixbuf_unref(pixbuf
);
823 /* Scale src down to fit in max_w, max_h and return the new pixbuf.
824 * If src is small enough, then ref it and return that.
826 static GdkPixbuf
*scale_pixbuf(GdkPixbuf
*src
, int max_w
, int max_h
)
830 w
= gdk_pixbuf_get_width(src
);
831 h
= gdk_pixbuf_get_height(src
);
833 if (w
<= max_w
&& h
<= max_h
)
840 float scale_x
= ((float) w
) / max_w
;
841 float scale_y
= ((float) h
) / max_h
;
842 float scale
= MAX(scale_x
, scale_y
);
843 int dest_w
= w
/ scale
;
844 int dest_h
= h
/ scale
;
846 return gdk_pixbuf_scale_simple(src
,
849 GDK_INTERP_BILINEAR
);
853 /* Scale src up to fit in max_w, max_h and return the new pixbuf.
854 * If src is that size or bigger, then ref it and return that.
856 static GdkPixbuf
*scale_pixbuf_up(GdkPixbuf
*src
, int max_w
, int max_h
)
860 w
= gdk_pixbuf_get_width(src
);
861 h
= gdk_pixbuf_get_height(src
);
863 if (w
== 0 || h
== 0 || (w
>= max_w
&& h
>= max_h
))
870 float scale_x
= max_w
/ ((float) w
);
871 float scale_y
= max_h
/ ((float) h
);
872 float scale
= MIN(scale_x
, scale_y
);
874 return gdk_pixbuf_scale_simple(src
,
877 GDK_INTERP_BILINEAR
);
881 /* Turn a full-size pixbuf into a MaskedPixmap */
882 MaskedPixmap
*image_from_pixbuf(GdkPixbuf
*full_size
)
885 GdkPixbuf
*huge_pixbuf
, *normal_pixbuf
;
889 g_return_val_if_fail(full_size
!= NULL
, NULL
);
891 huge_pixbuf
= scale_pixbuf(full_size
, HUGE_WIDTH
, HUGE_HEIGHT
);
892 g_return_val_if_fail(huge_pixbuf
!= NULL
, NULL
);
894 normal_pixbuf
= scale_pixbuf(huge_pixbuf
, ICON_WIDTH
, ICON_HEIGHT
);
895 g_return_val_if_fail(normal_pixbuf
!= NULL
, NULL
);
897 gdk_pixbuf_render_pixmap_and_mask(normal_pixbuf
, &pixmap
, &mask
, 128);
901 gdk_pixbuf_unref(huge_pixbuf
);
902 gdk_pixbuf_unref(normal_pixbuf
);
906 mp
= g_new(MaskedPixmap
, 1);
907 mp
->huge_pixbuf
= huge_pixbuf
;
912 mp
->width
= gdk_pixbuf_get_width(normal_pixbuf
);
913 mp
->height
= gdk_pixbuf_get_height(normal_pixbuf
);
915 gdk_pixbuf_unref(normal_pixbuf
);
917 mp
->huge_pixmap
= NULL
;
918 mp
->huge_mask
= NULL
;
920 mp
->huge_height
= -1;
922 mp
->sm_pixmap
= NULL
;
930 /* Return a pointer to the (static) bad image. The ref counter will ensure
931 * that the image is never freed.
933 static MaskedPixmap
*get_bad_image(void)
938 bad
= gdk_pixbuf_new_from_xpm_data(bad_xpm
);
939 mp
= image_from_pixbuf(bad
);
940 gdk_pixbuf_unref(bad
);
945 static MaskedPixmap
*load(char *pathname
, gpointer user_data
)
947 return image_from_file(pathname
);
950 static void ref(MaskedPixmap
*mp
, gpointer data
)
952 /* printf("[ ref %p %d->%d ]\n", mp, mp->ref, mp->ref + 1); */
958 static void unref(MaskedPixmap
*mp
, gpointer data
)
960 /* printf("[ unref %p %d->%d ]\n", mp, mp->ref, mp->ref - 1); */
962 if (mp
&& --mp
->ref
== 0)
966 gdk_pixbuf_unref(mp
->huge_pixbuf
);
970 gdk_pixmap_unref(mp
->huge_pixmap
);
972 gdk_bitmap_unref(mp
->huge_mask
);
975 gdk_pixmap_unref(mp
->pixmap
);
977 gdk_bitmap_unref(mp
->mask
);
980 gdk_pixmap_unref(mp
->sm_pixmap
);
982 gdk_bitmap_unref(mp
->sm_mask
);
988 static int getref(MaskedPixmap
*mp
)
990 return mp
? mp
->ref
: 0;
993 /* Called now and then to clear out old pixmaps */
994 static gint
purge(gpointer data
)
996 g_fscache_purge(pixmap_cache
, PIXMAP_PURGE_TIME
);
1001 static void got_thumb_data(GdkPixbufLoader
*loader
,
1002 gint fd
, GdkInputCondition cond
)
1010 got
= read(fd
, data
, sizeof(data
));
1013 if (got
> 0 && gdk_pixbuf_loader_write(loader
, data
, got
, NULL
))
1016 if (got
> 0 && gdk_pixbuf_loader_write(loader
, data
, got
))
1020 /* Some kind of error, or end-of-file */
1022 path
= GET_LOADER("thumb-path");
1024 tag
= GPOINTER_TO_INT(GET_LOADER("thumb-input-tag"));
1025 gdk_input_remove(tag
);
1028 gdk_pixbuf_loader_close(loader
, NULL
);
1030 gdk_pixbuf_loader_close(loader
);
1035 MaskedPixmap
*image
;
1038 pixbuf
= gdk_pixbuf_loader_get_pixbuf(loader
);
1041 gdk_pixbuf_ref(pixbuf
);
1042 image
= image_from_pixbuf(pixbuf
);
1044 g_fscache_insert(pixmap_cache
, path
, image
);
1045 #if defined(GTK2) || defined(THUMBS_USE_LIBPNG)
1046 save_thumbnail(path
, pixbuf
, image
);
1048 gdk_pixbuf_unref(pixbuf
);
1049 pixmap_unref(image
);
1057 callback
= GET_LOADER("thumb-callback");
1058 cbdata
= GET_LOADER("thumb-callback-data");
1060 callback(cbdata
, got
!= 0 ? NULL
: path
);
1063 g_object_unref(G_OBJECT(loader
));
1065 gtk_object_unref(GTK_OBJECT(loader
));