diff options
Diffstat (limited to 'src/eom-thumbnail.c')
-rw-r--r-- | src/eom-thumbnail.c | 509 |
1 files changed, 509 insertions, 0 deletions
diff --git a/src/eom-thumbnail.c b/src/eom-thumbnail.c new file mode 100644 index 0000000..d84cb24 --- /dev/null +++ b/src/eom-thumbnail.c @@ -0,0 +1,509 @@ +/* Eye Of Mate - Thumbnailing functions + * + * Copyright (C) 2000-2008 The Free Software Foundation + * + * Author: Lucas Rocha <[email protected]> + * + * Based on eel code (eel/eel-graphic-effects.c) by: + * - Andy Hertzfeld <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +/* We must define MATE_DESKTOP_USE_UNSTABLE_API to be able + to use MateDesktopThumbnail */ +#ifndef MATE_DESKTOP_USE_UNSTABLE_API +#define MATE_DESKTOP_USE_UNSTABLE_API +#endif +#include <libmateui/mate-desktop-thumbnail.h> + +#include "eom-thumbnail.h" +#include "eom-list-store.h" +#include "eom-debug.h" + +#define EOM_THUMB_ERROR eom_thumb_error_quark () + +static MateDesktopThumbnailFactory *factory = NULL; +static GdkPixbuf *frame = NULL; + +typedef enum { + EOM_THUMB_ERROR_VFS, + EOM_THUMB_ERROR_GENERIC, + EOM_THUMB_ERROR_UNKNOWN +} EomThumbError; + +typedef struct { + char *uri_str; + char *thumb_path; + time_t mtime; + char *mime_type; + gboolean thumb_exists; + gboolean failed_thumb_exists; + gboolean can_read; +} EomThumbData; + +static GQuark +eom_thumb_error_quark (void) +{ + static GQuark q = 0; + if (q == 0) + q = g_quark_from_static_string ("eom-thumb-error-quark"); + + return q; +} + +static void +set_vfs_error (GError **error, GError *ioerror) +{ + g_set_error (error, + EOM_THUMB_ERROR, + EOM_THUMB_ERROR_VFS, + "%s", ioerror ? ioerror->message : "VFS error making a thumbnail"); +} + +static void +set_thumb_error (GError **error, int error_id, const char *string) +{ + g_set_error (error, + EOM_THUMB_ERROR, + error_id, + "%s", string); +} + +static GdkPixbuf* +get_valid_thumbnail (EomThumbData *data, GError **error) +{ + GdkPixbuf *thumb = NULL; + + g_return_val_if_fail (data != NULL, NULL); + + /* does a thumbnail under the path exists? */ + if (data->thumb_exists) { + thumb = gdk_pixbuf_new_from_file (data->thumb_path, error); + + /* is this thumbnail file up to date? */ + if (thumb != NULL && !mate_desktop_thumbnail_is_valid (thumb, data->uri_str, data->mtime)) { + g_object_unref (thumb); + thumb = NULL; + } + } + + return thumb; +} + +static GdkPixbuf * +create_thumbnail_from_pixbuf (EomThumbData *data, + GdkPixbuf *pixbuf, + GError **error) +{ + GdkPixbuf *thumb; + gint width, height; + gfloat perc; + + g_assert (factory != NULL); + + width = gdk_pixbuf_get_width (pixbuf); + height = gdk_pixbuf_get_height (pixbuf); + + perc = CLAMP (128.0/(MAX (width, height)), 0, 1); + + thumb = mate_desktop_thumbnail_scale_down_pixbuf (pixbuf, + width*perc, + height*perc); + + return thumb; +} + +static void +eom_thumb_data_free (EomThumbData *data) +{ + if (data == NULL) + return; + + g_free (data->thumb_path); + g_free (data->mime_type); + g_free (data->uri_str); + + g_slice_free (EomThumbData, data); +} + +static EomThumbData* +eom_thumb_data_new (GFile *file, GError **error) +{ + EomThumbData *data; + GFileInfo *file_info; + GError *ioerror = NULL; + + g_return_val_if_fail (file != NULL, NULL); + g_return_val_if_fail (error != NULL && *error == NULL, NULL); + + data = g_slice_new0 (EomThumbData); + + data->uri_str = g_file_get_uri (file); + data->thumb_path = mate_desktop_thumbnail_path_for_uri (data->uri_str, MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + + file_info = g_file_query_info (file, + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," + G_FILE_ATTRIBUTE_TIME_MODIFIED "," + G_FILE_ATTRIBUTE_THUMBNAIL_PATH "," + G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "," + G_FILE_ATTRIBUTE_ACCESS_CAN_READ, + 0, NULL, &ioerror); + if (file_info == NULL) + { + set_vfs_error (error, ioerror); + g_clear_error (&ioerror); + } + + if (*error == NULL) { + /* if available, copy data */ + data->mtime = g_file_info_get_attribute_uint64 (file_info, + G_FILE_ATTRIBUTE_TIME_MODIFIED); + data->mime_type = g_strdup (g_file_info_get_content_type (file_info)); + + data->thumb_exists = (g_file_info_get_attribute_byte_string (file_info, + G_FILE_ATTRIBUTE_THUMBNAIL_PATH) != NULL); + data->failed_thumb_exists = g_file_info_get_attribute_boolean (file_info, + G_FILE_ATTRIBUTE_THUMBNAILING_FAILED); + data->can_read = TRUE; + if (g_file_info_has_attribute (file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) { + data->can_read = g_file_info_get_attribute_boolean (file_info, + G_FILE_ATTRIBUTE_ACCESS_CAN_READ); + } + } + else { + eom_thumb_data_free (data); + data = NULL; + g_clear_error (&ioerror); + } + + g_object_unref (file_info); + + return data; +} + +static void +draw_frame_row (GdkPixbuf *frame_image, + gint target_width, + gint source_width, + gint source_v_position, + gint dest_v_position, + GdkPixbuf *result_pixbuf, + gint left_offset, + gint height) +{ + gint remaining_width, h_offset, slab_width; + + remaining_width = target_width; + h_offset = 0; + + while (remaining_width > 0) { + slab_width = remaining_width > source_width ? + source_width : remaining_width; + + gdk_pixbuf_copy_area (frame_image, + left_offset, + source_v_position, + slab_width, + height, + result_pixbuf, + left_offset + h_offset, + dest_v_position); + + remaining_width -= slab_width; + h_offset += slab_width; + } +} + +static void +draw_frame_column (GdkPixbuf *frame_image, + gint target_height, + gint source_height, + gint source_h_position, + gint dest_h_position, + GdkPixbuf *result_pixbuf, + gint top_offset, + gint width) +{ + gint remaining_height, v_offset, slab_height; + + remaining_height = target_height; + v_offset = 0; + + while (remaining_height > 0) { + slab_height = remaining_height > source_height ? + source_height : remaining_height; + + gdk_pixbuf_copy_area (frame_image, + source_h_position, + top_offset, + width, + slab_height, + result_pixbuf, + dest_h_position, + top_offset + v_offset); + + remaining_height -= slab_height; + v_offset += slab_height; + } +} + +static GdkPixbuf * +eom_thumbnail_stretch_frame_image (GdkPixbuf *frame_image, + gint left_offset, + gint top_offset, + gint right_offset, + gint bottom_offset, + gint dest_width, + gint dest_height, + gboolean fill_flag) +{ + GdkPixbuf *result_pixbuf; + gint frame_width, frame_height; + gint target_width, target_frame_width; + gint target_height, target_frame_height; + + frame_width = gdk_pixbuf_get_width (frame_image); + frame_height = gdk_pixbuf_get_height (frame_image); + + if (fill_flag) { + result_pixbuf = gdk_pixbuf_scale_simple (frame_image, + dest_width, + dest_height, + GDK_INTERP_NEAREST); + } else { + result_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, + dest_width, + dest_height); + + /* Clear pixbuf to fully opaque white */ + gdk_pixbuf_fill (result_pixbuf, 0xffffffff); + } + + target_width = dest_width - left_offset - right_offset; + target_frame_width = frame_width - left_offset - right_offset; + + target_height = dest_height - top_offset - bottom_offset; + target_frame_height = frame_height - top_offset - bottom_offset; + + /* Draw the left top corner and top row */ + gdk_pixbuf_copy_area (frame_image, + 0, 0, + left_offset, + top_offset, + result_pixbuf, + 0, 0); + + draw_frame_row (frame_image, + target_width, + target_frame_width, + 0, 0, + result_pixbuf, + left_offset, + top_offset); + + /* Draw the right top corner and left column */ + gdk_pixbuf_copy_area (frame_image, + frame_width - right_offset, + 0, + right_offset, + top_offset, + result_pixbuf, + dest_width - right_offset, + 0); + + draw_frame_column (frame_image, + target_height, + target_frame_height, + 0, 0, + result_pixbuf, + top_offset, + left_offset); + + /* Draw the bottom right corner and bottom row */ + gdk_pixbuf_copy_area (frame_image, + frame_width - right_offset, + frame_height - bottom_offset, + right_offset, + bottom_offset, + result_pixbuf, + dest_width - right_offset, + dest_height - bottom_offset); + + draw_frame_row (frame_image, + target_width, + target_frame_width, + frame_height - bottom_offset, + dest_height - bottom_offset, + result_pixbuf, + left_offset, bottom_offset); + + /* Draw the bottom left corner and the right column */ + gdk_pixbuf_copy_area (frame_image, + 0, + frame_height - bottom_offset, + left_offset, + bottom_offset, + result_pixbuf, + 0, + dest_height - bottom_offset); + + draw_frame_column (frame_image, + target_height, + target_frame_height, + frame_width - right_offset, + dest_width - right_offset, + result_pixbuf, top_offset, + right_offset); + + return result_pixbuf; +} + +GdkPixbuf * +eom_thumbnail_add_frame (GdkPixbuf *thumbnail) +{ + GdkPixbuf *result_pixbuf; + gint source_width, source_height; + gint dest_width, dest_height; + + source_width = gdk_pixbuf_get_width (thumbnail); + source_height = gdk_pixbuf_get_height (thumbnail); + + dest_width = source_width + 9; + dest_height = source_height + 9; + + result_pixbuf = eom_thumbnail_stretch_frame_image (frame, + 3, 3, 6, 6, + dest_width, + dest_height, + FALSE); + + gdk_pixbuf_copy_area (thumbnail, + 0, 0, + source_width, + source_height, + result_pixbuf, + 3, 3); + + return result_pixbuf; +} + +GdkPixbuf * +eom_thumbnail_fit_to_size (GdkPixbuf *thumbnail, gint dimension) +{ + gint width, height; + + width = gdk_pixbuf_get_width (thumbnail); + height = gdk_pixbuf_get_height (thumbnail); + + if (width > dimension || height > dimension) { + GdkPixbuf *result_pixbuf; + gfloat factor; + + if (width > height) { + factor = (gfloat) dimension / (gfloat) width; + } else { + factor = (gfloat) dimension / (gfloat) height; + } + + width = MAX (width * factor, 1); + height = MAX (height * factor, 1); + + result_pixbuf = mate_desktop_thumbnail_scale_down_pixbuf (thumbnail, width, height); + + return result_pixbuf; + } + return gdk_pixbuf_copy (thumbnail); +} + +GdkPixbuf* +eom_thumbnail_load (EomImage *image, GError **error) +{ + GdkPixbuf *thumb = NULL; + GFile *file; + EomThumbData *data; + GdkPixbuf *pixbuf; + + g_return_val_if_fail (image != NULL, NULL); + g_return_val_if_fail (error != NULL && *error == NULL, NULL); + + file = eom_image_get_file (image); + data = eom_thumb_data_new (file, error); + g_object_unref (file); + + if (data == NULL) + return NULL; + + if (!data->can_read || + (data->failed_thumb_exists && mate_desktop_thumbnail_factory_has_valid_failed_thumbnail (factory, data->uri_str, data->mtime))) { + eom_debug_message (DEBUG_THUMBNAIL, "%s: bad permissions or valid failed thumbnail present",data->uri_str); + set_thumb_error (error, EOM_THUMB_ERROR_GENERIC, "Thumbnail creation failed"); + return NULL; + } + + /* check if there is already a valid cached thumbnail */ + thumb = get_valid_thumbnail (data, error); + + if (thumb != NULL) { + eom_debug_message (DEBUG_THUMBNAIL, "%s: loaded from cache",data->uri_str); + } else if (mate_desktop_thumbnail_factory_can_thumbnail (factory, data->uri_str, data->mime_type, data->mtime)) { + pixbuf = eom_image_get_pixbuf (image); + + if (pixbuf != NULL) { + /* generate a thumbnail from the in-memory image, + if we have already loaded the image */ + eom_debug_message (DEBUG_THUMBNAIL, "%s: creating from pixbuf",data->uri_str); + thumb = create_thumbnail_from_pixbuf (data, pixbuf, error); + g_object_unref (pixbuf); + } else { + /* generate a thumbnail from the file */ + eom_debug_message (DEBUG_THUMBNAIL, "%s: creating from file",data->uri_str); + thumb = mate_desktop_thumbnail_factory_generate_thumbnail (factory, data->uri_str, data->mime_type); + } + + if (thumb != NULL) { + /* Save the new thumbnail */ + mate_desktop_thumbnail_factory_save_thumbnail (factory, thumb, data->uri_str, data->mtime); + eom_debug_message (DEBUG_THUMBNAIL, "%s: normal thumbnail saved",data->uri_str); + } else { + /* Save a failed thumbnail, to stop further thumbnail attempts */ + mate_desktop_thumbnail_factory_create_failed_thumbnail (factory, data->uri_str, data->mtime); + eom_debug_message (DEBUG_THUMBNAIL, "%s: failed thumbnail saved",data->uri_str); + set_thumb_error (error, EOM_THUMB_ERROR_GENERIC, "Thumbnail creation failed"); + } + } + + eom_thumb_data_free (data); + + return thumb; +} + +void +eom_thumbnail_init (void) +{ + if (factory == NULL) { + factory = mate_desktop_thumbnail_factory_new (MATE_DESKTOP_THUMBNAIL_SIZE_NORMAL); + } + + if (frame == NULL) { + frame = gdk_pixbuf_new_from_file (EOM_DATA_DIR "/pixmaps/thumbnail-frame.png", NULL); + } +} |