From a28322e65d0f80cbbab4b0f8d1e0e410f4f01cae Mon Sep 17 00:00:00 2001 From: Christian Neumair Date: Mon, 30 Jun 2008 13:28:52 +0000 Subject: Scale down huge pixbufs when loading. Massively speed up loading of 2008-06-30 Christian Neumair * libnautilus-private/nautilus-directory-async.c (thumbnail_done), (thumbnail_loader_size_prepared), (get_pixbuf_for_content), (thumbnail_start): * libnautilus-private/nautilus-file.h: Scale down huge pixbufs when loading. Massively speed up loading of directories where the original image is used as thumbnail. Also try to write the whole file at once rather than writing in chunks. * libnautilus-private/nautilus-file-private.h: * libnautilus-private/nautilus-file.c (nautilus_file_get_icon): Rewrite some flaky parts of thumbnailing. Never scale any thumbnails up. Always compose thumbnail on the fly instead storing it once and scaling it up and down. Add flag to force that thumbnail size matches the output icon size for tree view. * src/file-manager/fm-list-model.c (fm_list_model_get_value): Use this flag. svn path=/branches/gnome-2-22/; revision=14310 --- ChangeLog | 23 ++++++++ libnautilus-private/nautilus-directory-async.c | 37 ++++++++++++- libnautilus-private/nautilus-file-private.h | 2 +- libnautilus-private/nautilus-file.c | 77 +++++++++++++------------- libnautilus-private/nautilus-file.h | 4 +- src/file-manager/fm-list-model.c | 3 +- 6 files changed, 102 insertions(+), 44 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4beee9f13..4f0ff351d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2008-06-30 Christian Neumair + + * libnautilus-private/nautilus-directory-async.c (thumbnail_done), + (thumbnail_loader_size_prepared), (get_pixbuf_for_content), + (thumbnail_start): + * libnautilus-private/nautilus-file.h: + Scale down huge pixbufs when loading. Massively speed up loading of + directories where the original image is used as thumbnail. + Also try to write the whole file at once rather than writing in chunks. + + * libnautilus-private/nautilus-file-private.h: + * libnautilus-private/nautilus-file.c (nautilus_file_get_icon): + Rewrite some flaky parts of thumbnailing. + + Never scale any thumbnails up. + Always compose thumbnail on the fly instead storing it once and + scaling it up and down. + Add flag to force that thumbnail size matches + the output icon size for tree view. + + * src/file-manager/fm-list-model.c (fm_list_model_get_value): + Use this flag. + 2008-06-29 Cosimo Cecchi * src/nautilus-window.c: (nautilus_window_set_status): diff --git a/libnautilus-private/nautilus-directory-async.c b/libnautilus-private/nautilus-directory-async.c index 63a74f6dd..267925629 100644 --- a/libnautilus-private/nautilus-directory-async.c +++ b/libnautilus-private/nautilus-directory-async.c @@ -3768,7 +3768,6 @@ thumbnail_done (NautilusDirectory *directory, g_object_unref (file->details->thumbnail); file->details->thumbnail = NULL; } - file->details->thumbnail_size = 0; if (pixbuf) { thumb_mtime_str = gdk_pixbuf_get_option (pixbuf, "tEXt::Thumb::MTime"); if (thumb_mtime_str) { @@ -3838,6 +3837,35 @@ thumbnail_state_free (ThumbnailState *state) g_free (state); } +extern int cached_thumbnail_size; + +/* scale very large images down to the max. size we need */ +static void +thumbnail_loader_size_prepared (GdkPixbufLoader *loader, + int width, + int height, + gpointer user_data) +{ + int max_thumbnail_size; + double aspect_ratio; + + aspect_ratio = ((double) width) / height; + + /* cf. nautilus_file_get_icon() */ + max_thumbnail_size = NAUTILUS_ICON_SIZE_LARGEST * cached_thumbnail_size / NAUTILUS_ICON_SIZE_STANDARD; + if (MAX (width, height) > max_thumbnail_size) { + if (width > height) { + width = max_thumbnail_size; + height = width / aspect_ratio; + } else { + height = max_thumbnail_size; + width = height * aspect_ratio; + } + + gdk_pixbuf_loader_set_size (loader, width, height); + } +} + static GdkPixbuf * get_pixbuf_for_content (goffset file_len, char *file_contents) @@ -3849,11 +3877,14 @@ get_pixbuf_for_content (goffset file_len, pixbuf = NULL; loader = gdk_pixbuf_loader_new (); + g_signal_connect (loader, "size-prepared", + G_CALLBACK (thumbnail_loader_size_prepared), + NULL); /* For some reason we have to write in chunks, or gdk-pixbuf fails */ res = TRUE; while (res && file_len > 0) { - chunk_len = MIN (32*1024, file_len); + chunk_len = file_len; res = gdk_pixbuf_loader_write (loader, file_contents, chunk_len, NULL); file_contents += chunk_len; file_len -= chunk_len; @@ -3959,7 +3990,7 @@ thumbnail_start (NautilusDirectory *directory, state->file = file; state->cancellable = g_cancellable_new (); - if (file->details->thumbnail_size > 128) { + if (file->details->thumbnail_wants_original) { state->tried_original = TRUE; state->trying_original = TRUE; location = nautilus_file_get_location (file); diff --git a/libnautilus-private/nautilus-file-private.h b/libnautilus-private/nautilus-file-private.h index 8420a7ef5..ee63b70b5 100644 --- a/libnautilus-private/nautilus-file-private.h +++ b/libnautilus-private/nautilus-file-private.h @@ -106,7 +106,6 @@ struct NautilusFileDetails char *thumbnail_path; GdkPixbuf *thumbnail; time_t thumbnail_mtime; - int thumbnail_size; /* 0 means original unframed thumbnail */ GList *mime_list; /* If this is a directory, the list of MIME types in it. */ char *top_left_text; @@ -181,6 +180,7 @@ struct NautilusFileDetails eel_boolean_bit got_custom_activation_location : 1; eel_boolean_bit thumbnail_is_up_to_date : 1; + eel_boolean_bit thumbnail_wants_original : 1; eel_boolean_bit thumbnail_tried_original : 1; eel_boolean_bit thumbnailing_failed : 1; diff --git a/libnautilus-private/nautilus-file.c b/libnautilus-private/nautilus-file.c index 222b524ff..b9bdc5853 100644 --- a/libnautilus-private/nautilus-file.c +++ b/libnautilus-private/nautilus-file.c @@ -3190,7 +3190,7 @@ get_custom_icon (NautilusFile *file) static int cached_thumbnail_limit; -static int cached_thumbnail_size; +int cached_thumbnail_size; static int show_image_thumbs; GFilesystemPreviewType @@ -3349,51 +3349,52 @@ nautilus_file_get_icon (NautilusFile *file, return icon; } - modified_size = size * cached_thumbnail_size / NAUTILUS_ICON_SIZE_STANDARD; + if (flags & NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE) { + modified_size = size; + } else { + modified_size = size * cached_thumbnail_size / NAUTILUS_ICON_SIZE_STANDARD; + } if (flags & NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS && nautilus_file_should_show_thumbnail (file)) { if (file->details->thumbnail) { - if (file->details->thumbnail_size == modified_size) { - scaled_pixbuf = g_object_ref (file->details->thumbnail); - } else { - int w, h, s; - double scale; - - if (file->details->thumbnail_size == 0) { - raw_pixbuf = g_object_ref (file->details->thumbnail); - } else { - raw_pixbuf = nautilus_thumbnail_unframe_image (file->details->thumbnail); - } + int w, h, s; + double scale; - w = gdk_pixbuf_get_width (raw_pixbuf); - h = gdk_pixbuf_get_height (raw_pixbuf); - - s = MAX (w, h); - - scale = (double)modified_size / s; + raw_pixbuf = g_object_ref (file->details->thumbnail); - /* These compensate for the size of the frame which will be added around the raw image */ + w = gdk_pixbuf_get_width (raw_pixbuf); + h = gdk_pixbuf_get_height (raw_pixbuf); + + s = MAX (w, h); + scale = (double)modified_size / s; + + + if (scale > 0.99) { + /* never scale any thumbnails up */ + scaled_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + gdk_pixbuf_get_has_alpha (raw_pixbuf), + gdk_pixbuf_get_bits_per_sample (raw_pixbuf), + w * scale, h * scale); + gdk_pixbuf_fill (scaled_pixbuf, 0xffffff00); + gdk_pixbuf_copy_area (raw_pixbuf, + 0, 0, w, h, + scaled_pixbuf, + (gdk_pixbuf_get_width (scaled_pixbuf) - w) / 2, + (gdk_pixbuf_get_height (scaled_pixbuf) - h) / 2); + } else { scaled_pixbuf = gdk_pixbuf_scale_simple (raw_pixbuf, - NAUTILUS_THUMBNAIL_FRAME_LEFT + w * scale + NAUTILUS_THUMBNAIL_FRAME_RIGHT, - NAUTILUS_THUMBNAIL_FRAME_TOP + h * scale + NAUTILUS_THUMBNAIL_FRAME_BOTTOM, + w * scale, h * scale, GDK_INTERP_BILINEAR); - nautilus_thumbnail_frame_image (&scaled_pixbuf); - - g_object_unref (raw_pixbuf); - - if (modified_size > file->details->thumbnail_size) { - /* Invalidate if we resize upward (and the - loaded was not the original raw version, w/ size 0). - */ - if (file->details->thumbnail_size != 0 || - (modified_size > 128 && !file->details->thumbnail_tried_original)) { - nautilus_file_invalidate_attributes (file, NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL); - } - file->details->thumbnail_size = modified_size; - g_object_unref (file->details->thumbnail); - file->details->thumbnail = g_object_ref (scaled_pixbuf); - } + } + nautilus_thumbnail_frame_image (&scaled_pixbuf); + g_object_unref (raw_pixbuf); + + if (modified_size > 128 && + !file->details->thumbnail_wants_original) { + /* Invalidate if we resize upward */ + file->details->thumbnail_wants_original = TRUE; + nautilus_file_invalidate_attributes (file, NAUTILUS_FILE_ATTRIBUTE_THUMBNAIL); } icon = nautilus_icon_info_new_for_pixbuf (scaled_pixbuf); diff --git a/libnautilus-private/nautilus-file.h b/libnautilus-private/nautilus-file.h index 99e0aeb98..2ab12df4a 100644 --- a/libnautilus-private/nautilus-file.h +++ b/libnautilus-private/nautilus-file.h @@ -77,7 +77,9 @@ typedef enum { NAUTILUS_FILE_ICON_FLAGS_IGNORE_VISITING = (1<<1), NAUTILUS_FILE_ICON_FLAGS_EMBEDDING_TEXT = (1<<2), NAUTILUS_FILE_ICON_FLAGS_FOR_DRAG_ACCEPT = (1<<3), - NAUTILUS_FILE_ICON_FLAGS_FOR_OPEN_FOLDER = (1<<4) + NAUTILUS_FILE_ICON_FLAGS_FOR_OPEN_FOLDER = (1<<4), + /* whether the thumbnail size must match the display icon size */ + NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE = (1<<5) } NautilusFileIconFlags; /* Emblems sometimes displayed for NautilusFiles. Do not localize. */ diff --git a/src/file-manager/fm-list-model.c b/src/file-manager/fm-list-model.c index 324829f4d..aa37e9849 100644 --- a/src/file-manager/fm-list-model.c +++ b/src/file-manager/fm-list-model.c @@ -292,7 +292,8 @@ fm_list_model_get_value (GtkTreeModel *tree_model, GtkTreeIter *iter, int column zoom_level = fm_list_model_get_zoom_level_from_column_id (column); icon_size = nautilus_get_icon_size_for_zoom_level (zoom_level); - flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS; + flags = NAUTILUS_FILE_ICON_FLAGS_USE_THUMBNAILS | + NAUTILUS_FILE_ICON_FLAGS_FORCE_THUMBNAIL_SIZE; if (model->details->drag_view != NULL) { GtkTreePath *path_a, *path_b; -- cgit v1.2.1