diff --git a/src/base/fm-config.c b/src/base/fm-config.c index a6338579..d51a7e2a 100644 --- a/src/base/fm-config.c +++ b/src/base/fm-config.c @@ -145,6 +145,8 @@ static void fm_config_init(FmConfig *self) self->thumbnail_size = FM_CONFIG_DEFAULT_THUMBNAIL_SIZE; self->show_thumbnail = FM_CONFIG_DEFAULT_SHOW_THUMBNAIL; self->thumbnail_local = FM_CONFIG_DEFAULT_THUMBNAIL_LOCAL; + self->thumbnail_threshold = FM_CONFIG_DEFAULT_THUMBNAIL_THRESHOLD; + self->thumbnail_overlay = FM_CONFIG_DEFAULT_THUMBNAIL_OVERLAY; self->thumbnail_max = FM_CONFIG_DEFAULT_THUMBNAIL_MAX; /* show_internal_volumes defaulted to FALSE */ /* si_unit defaulted to FALSE */ @@ -303,6 +305,8 @@ void fm_config_load_from_key_file(FmConfig* cfg, GKeyFile* kf) fm_key_file_get_int(kf, "ui", "small_icon_size", &cfg->small_icon_size); fm_key_file_get_int(kf, "ui", "pane_icon_size", &cfg->pane_icon_size); fm_key_file_get_int(kf, "ui", "thumbnail_size", &cfg->thumbnail_size); + fm_key_file_get_int(kf, "ui", "thumbnail_threshold", &cfg->thumbnail_threshold); + fm_key_file_get_bool(kf, "ui", "thumbnail_overlay", &cfg->thumbnail_overlay); fm_key_file_get_bool(kf, "ui", "show_thumbnail", &cfg->show_thumbnail); fm_key_file_get_bool(kf, "ui", "shadow_hidden", &cfg->shadow_hidden); g_free(cfg->list_view_size_units); @@ -512,6 +516,8 @@ void fm_config_save(FmConfig* cfg, const char* name) _save_config_int(str, cfg, small_icon_size); _save_config_int(str, cfg, pane_icon_size); _save_config_int(str, cfg, thumbnail_size); + _save_config_int(str, cfg, thumbnail_threshold); + _save_config_bool(str, cfg, thumbnail_overlay); _save_config_bool(str, cfg, show_thumbnail); _save_config_bool(str, cfg, shadow_hidden); if (cfg->list_view_size_units && cfg->list_view_size_units[0]) diff --git a/src/base/fm-config.h b/src/base/fm-config.h index 10d5bbee..671def5e 100644 --- a/src/base/fm-config.h +++ b/src/base/fm-config.h @@ -57,6 +57,8 @@ typedef struct _FmConfigClass FmConfigClass; #define FM_CONFIG_DEFAULT_SHOW_THUMBNAIL TRUE #define FM_CONFIG_DEFAULT_THUMBNAIL_LOCAL TRUE +#define FM_CONFIG_DEFAULT_THUMBNAIL_THRESHOLD 48 +#define FM_CONFIG_DEFAULT_THUMBNAIL_OVERLAY TRUE #define FM_CONFIG_DEFAULT_THUMBNAIL_MAX 2048 #define FM_CONFIG_DEFAULT_FORCE_S_NOTIFY TRUE @@ -208,9 +210,10 @@ struct _FmConfig gboolean smart_desktop_autodrop; gchar *saved_search; /*< private >*/ - gpointer _reserved1; /* reserved space for updates until next ABI */ - gpointer _reserved2; - gpointer _reserved3; + + gint thumbnail_threshold; + gboolean thumbnail_overlay; + gpointer _reserved3; /* reserved space for updates until next ABI */ gpointer _reserved4; gpointer _reserved5; gpointer _reserved6; diff --git a/src/base/fm-file-info.c b/src/base/fm-file-info.c index a2775d5a..dcc61620 100644 --- a/src/base/fm-file-info.c +++ b/src/base/fm-file-info.c @@ -443,7 +443,32 @@ gboolean _fm_file_info_set_from_native_file(FmFileInfo* fi, const char* path, } } if(!fi->icon) - fi->icon = g_object_ref(fm_mime_type_get_icon(fi->mime_type)); + { + if(S_ISDIR(st.st_mode)) + { + /* If there is a custom folder icon, use it! */ + char* path_name = fm_path_to_str(fi->path); + gchar *dot_dir = g_build_filename (path_name, G_DIR_SEPARATOR_S, ".directory", NULL); + g_free(path_name); + if(g_file_test(dot_dir, G_FILE_TEST_IS_REGULAR)) + { + GKeyFile *key = g_key_file_new(); + if(g_key_file_load_from_file(key, dot_dir, G_KEY_FILE_NONE, NULL)) + { + gchar *icn = g_key_file_get_string(key, "Desktop Entry", "Icon", NULL); + if(icn) + { + fi->icon = fm_icon_from_name(icn); + g_free(icn); + } + } + g_key_file_free(key); + } + g_free(dot_dir); + } + if(!fi->icon) + fi->icon = g_object_ref(fm_mime_type_get_icon(fi->mime_type)); + } gfile = g_file_new_for_path(path); @@ -1469,8 +1494,8 @@ gboolean fm_file_info_is_backup(FmFileInfo* fi) gboolean fm_file_info_can_thumbnail(FmFileInfo* fi) { /* We cannot use S_ISREG here as this exclude all symlinks */ - if( fi->size == 0 || /* don't generate thumbnails for empty files */ - !(fi->mode & S_IFREG) || + if( (fi->size == 0 && !(fi->mode & S_IFDIR)) || /* don't generate thumbnails for empty files */ + !(fi->mode & (S_IFREG | S_IFDIR)) || fm_file_info_is_desktop_entry(fi) || fm_file_info_is_unknown_type(fi)) return FALSE; diff --git a/src/base/fm-thumbnail-loader.c b/src/base/fm-thumbnail-loader.c index d6e7725f..fffa08f2 100644 --- a/src/base/fm-thumbnail-loader.c +++ b/src/base/fm-thumbnail-loader.c @@ -845,23 +845,44 @@ static GObject* scale_pix(GObject* ori_pix, int size) new_width = new_height = size; } - if((new_width == width && new_height == height) || - (size > width && size > height )) /* don't scale up */ + /* if original size is smaller, use original size. */ + if (size > width && size > height ) { - /* if size is not changed or original size is smaller, use original size. */ - scaled_pix = (GObject*)g_object_ref(ori_pix); - } - else - { - /* avoid width or height of 0 pixel. - * FIXME: or we should just fail creating the thumbnail for the image? */ - if(new_width == 0) - new_width = 1; - if(new_height == 0) - new_height = 1; - scaled_pix = backend.scale_image(ori_pix, new_width, new_height); + new_width = width; + new_height = height; } + /* avoid width or height of 0 pixel. + * FIXME: or we should just fail creating the thumbnail for the image? */ + if(new_width == 0) + new_width = 1; + if(new_height == 0) + new_height = 1; + + double scale_factor = (double)new_width/width; + + /* create new size*size image*/ + scaled_pix = backend.new_image ( + backend.get_colorspace(ori_pix), + TRUE, + backend.get_bits_per_sample(ori_pix), + size, + size); + + /* fill the image with transparent pixels */ + backend.fill_image(scaled_pix, 0x00000000); + + /* scale original image composite it into the new image*/ + backend.composite( + ori_pix, scaled_pix, /* src, dst */ + (size-new_width)/2, /* dst_x */ + (size-new_height)/2, /* dst_y */ + new_width, /* dst_width */ + new_height, /* dst_height */ + (size-new_width)/2, /* offset_x */ + (size-new_height)/2, /* offset_y */ + scale_factor, scale_factor, /* scale_x, scale_y */ + 255); /* overall_alpha */ return scaled_pix; } diff --git a/src/base/fm-thumbnail-loader.h b/src/base/fm-thumbnail-loader.h index 814228c1..a53feaeb 100644 --- a/src/base/fm-thumbnail-loader.h +++ b/src/base/fm-thumbnail-loader.h @@ -77,11 +77,16 @@ typedef struct _FmThumbnailLoaderBackend FmThumbnailLoaderBackend; * @read_image_from_stream: callback to read image by opened #GInputStream * @write_image: callback to write thumbnail file from image * @scale_image: callback to change image sizes + * @new_image: callback to create new image + * @fill_image: callback to fill an image with a uniform color * @rotate_image: callback to change image orientation + * @get_colorspace: callback to retrieve the image colorspace * @get_image_width: callback to retrieve width from image * @get_image_height: callback to retrieve height from image + * @get_bits_per_sample: callback to retrieve the image bit depth * @get_image_text: callback to retrieve custom attributes text from image * @set_image_text: callback to set custom attributes text into image + * @composite: callback to merge two images into one * * Abstract backend callbacks list. */ @@ -90,11 +95,17 @@ struct _FmThumbnailLoaderBackend { GObject* (*read_image_from_stream)(GInputStream* stream, guint64 len, GCancellable* cancellable); gboolean (*write_image)(GObject* image, const char* filename); GObject* (*scale_image)(GObject* ori_pix, int new_width, int new_height); + GObject* (*new_image)(int colorspace, gboolean has_alpha, int bits_per_sample, int width, int height); + void (*fill_image)(GObject* image, guint32 color); GObject* (*rotate_image)(GObject* image, int degree); + int (*get_colorspace)(GObject* image); int (*get_image_width)(GObject* image); int (*get_image_height)(GObject* image); + int (*get_bits_per_sample)(GObject* image); char* (*get_image_text)(GObject* image, const char* key); gboolean (*set_image_text)(GObject* image, const char* key, const char* val); + void (*composite)(GObject* src, GObject* dst, int dst_x, int dst_y, int dst_width, int dst_height, + double offset_x, double offset_y, double scale_x, double scale_y, int overall_alpha); // const char* (*get_image_orientation)(GObject* image); // GObject* (*apply_orientation)(GObject* image); }; diff --git a/src/gtk/fm-folder-model.c b/src/gtk/fm-folder-model.c index bfa2ff5d..1fb51b9e 100644 --- a/src/gtk/fm-folder-model.c +++ b/src/gtk/fm-folder-model.c @@ -741,7 +741,9 @@ static void fm_folder_model_get_value(GtkTreeModel *tree_model, /* if we want to show a thumbnail */ /* if we're on local filesystem or thumbnailing for remote files is allowed */ - if(fm_config->show_thumbnail && (fm_path_is_native_or_trash(fm_file_info_get_path(info)) || !fm_config->thumbnail_local)) + /* if current icon size is at least equal to the threshold size */ + if(fm_config->show_thumbnail && (fm_path_is_native_or_trash(fm_file_info_get_path(info)) || !fm_config->thumbnail_local) + && (model->icon_size >= fm_config->thumbnail_threshold) ) { if(!item->is_thumbnail && !item->thumbnail_failed && !item->thumbnail_loading) { @@ -1600,7 +1602,30 @@ static void on_thumbnail_loaded(FmThumbnailRequest* req, gpointer user_data) GDK_THREADS_ENTER(); tp = fm_folder_model_get_path(GTK_TREE_MODEL(model), &it); if(item->icon) + { + if (fm_config->thumbnail_overlay) + { + float overlay_relative_size = 0.5; + int thumbnail_width = gdk_pixbuf_get_width(pix); + int thumbnail_height = gdk_pixbuf_get_height(pix); + int icon_width = gdk_pixbuf_get_width(item->icon); + int icon_height = gdk_pixbuf_get_height(item->icon); + int overlay_width = thumbnail_width * overlay_relative_size; + int overlay_height = thumbnail_height * overlay_relative_size; + gdk_pixbuf_composite( + item->icon, pix, /* src, dst */ + thumbnail_width - overlay_width, /* dst_x */ + thumbnail_height - overlay_height, /* dst_y */ + overlay_width, /* dst_width */ + overlay_height, /* dst_height */ + thumbnail_width - overlay_width, /* offset_x */ + thumbnail_height - overlay_height, /* offset_y */ + overlay_relative_size, overlay_relative_size, /* scale_x, scale_y */ + GDK_INTERP_BILINEAR, 255 /* interp_type, overall_alpha */ + ); + } g_object_unref(item->icon); + } item->icon = g_object_ref(pix); item->is_thumbnail = TRUE; gtk_tree_model_row_changed(GTK_TREE_MODEL(model), tp, &it); @@ -1630,6 +1655,10 @@ void fm_folder_model_set_icon_size(FmFolderModel* model, guint icon_size) { if(model->icon_size == icon_size) return; + if(model->icon_size >= fm_config->thumbnail_threshold && icon_size < fm_config->thumbnail_threshold) + { + g_list_foreach(model->thumbnail_requests, (GFunc)fm_thumbnail_request_cancel, NULL); + } model->icon_size = icon_size; reload_icons(model, RELOAD_BOTH); } diff --git a/src/gtk/fm-thumbnail.c b/src/gtk/fm-thumbnail.c index de0829a1..9fdc9e5d 100644 --- a/src/gtk/fm-thumbnail.c +++ b/src/gtk/fm-thumbnail.c @@ -193,6 +193,26 @@ static GObject* scale_image(GObject* ori_pix, int new_width, int new_height) return (GObject*)gdk_pixbuf_scale_simple(GDK_PIXBUF(ori_pix), new_width, new_height, GDK_INTERP_BILINEAR); } +static GObject* new_image(int colorspace, gboolean has_alpha, int bits_per_sample, int width, int height) +{ + return (GObject*)gdk_pixbuf_new(colorspace, has_alpha, bits_per_sample, width, height); +} + +static void fill_image(GObject* image, guint32 color) +{ + gdk_pixbuf_fill(GDK_PIXBUF(image), color); +} + +static int get_colorspace(GObject* image) +{ + return (int)gdk_pixbuf_get_colorspace(GDK_PIXBUF(image)); +} + +static int get_bits_per_sample(GObject* image) +{ + return gdk_pixbuf_get_bits_per_sample(GDK_PIXBUF(image)); +} + static int get_image_width(GObject* image) { return gdk_pixbuf_get_width(GDK_PIXBUF(image)); @@ -213,16 +233,28 @@ static GObject* rotate_image(GObject* image, int degree) return (GObject*)gdk_pixbuf_rotate_simple(GDK_PIXBUF(image), (GdkPixbufRotation)degree); } +static void composite(GObject* src, GObject* dst, int dst_x, int dst_y, int dst_width, int dst_height, + double offset_x, double offset_y, double scale_x, double scale_y, int overall_alpha) +{ + gdk_pixbuf_composite(GDK_PIXBUF(src), GDK_PIXBUF(dst), dst_x, dst_y, dst_width, dst_height, + offset_x, offset_y, scale_x, scale_y, GDK_INTERP_BILINEAR, overall_alpha); +} + static FmThumbnailLoaderBackend gtk_backend = { read_image_from_file, read_image_from_stream, write_image, scale_image, + new_image, + fill_image, rotate_image, + get_colorspace, get_image_width, get_image_height, + get_bits_per_sample, get_image_text, - set_image_text + set_image_text, + composite }; /* in main loop */