From 39b2f4bc368edb65fa2119382256eecc8cf14042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= <41945903+qarmin@users.noreply.github.com> Date: Tue, 5 Apr 2022 16:40:41 +0200 Subject: [PATCH] Simplify and speedup preview creating (#660) * Simplify and speedup preview creating * Speedup also comparing images --- czkawka_gui/i18n/en/czkawka_gui.ftl | 7 +- .../connect_things/connect_button_compare.rs | 68 ++++---- czkawka_gui/src/help_functions.rs | 23 +-- czkawka_gui/src/initialize_gui.rs | 163 +++++------------- 4 files changed, 83 insertions(+), 178 deletions(-) diff --git a/czkawka_gui/i18n/en/czkawka_gui.ftl b/czkawka_gui/i18n/en/czkawka_gui.ftl index a259f08..b2b98ea 100644 --- a/czkawka_gui/i18n/en/czkawka_gui.ftl +++ b/czkawka_gui/i18n/en/czkawka_gui.ftl @@ -473,11 +473,8 @@ cache_clear_message_label_3 = This may slightly speedup loading/saving to cache. cache_clear_message_label_4 = WARNING: Operation will remove all cached data from unplugged external drives. So each hash will need to be regenerated. # Show preview -preview_temporary_file = Failed to open temporary image file {$name}, reason {$reason}. -preview_0_size = Cannot create preview of image {$name}, with 0 width or height. -preview_temporary_image_save = Failed to save temporary image file to {$name}, reason {$reason}. -preview_temporary_image_remove = Failed to delete temporary image file {$name}, reason {$reason}. -preview_failed_to_create_cache_dir = Failed to create dir {$name} needed by image preview, reason {$reason}. +preview_image_resize_failure = Failed to resize image {$name}. +preview_image_opening_failure = Failed to open image {$name}, reason {$reason} # Compare images (L is short Left, R is short Right - they can't take too much space) compare_groups_number = Group { $current_group }/{ $all_groups } ({ $images_in_group } images) diff --git a/czkawka_gui/src/connect_things/connect_button_compare.rs b/czkawka_gui/src/connect_things/connect_button_compare.rs index 8e0cd9d..86c802b 100644 --- a/czkawka_gui/src/connect_things/connect_button_compare.rs +++ b/czkawka_gui/src/connect_things/connect_button_compare.rs @@ -1,22 +1,16 @@ use crate::flg; -use czkawka_core::common::get_dynamic_image_from_raw_image; -use czkawka_core::similar_images::RAW_IMAGE_EXTENSIONS; +use gdk::gdk_pixbuf::{InterpType, Pixbuf}; use gtk::prelude::*; use gtk::{CheckButton, Image, ListStore, Orientation, ScrolledWindow, TreeIter, TreeModel, TreePath, TreeSelection}; -use image::imageops::FilterType; -use image::DynamicImage; use std::cell::RefCell; use std::rc::Rc; use crate::gui_structs::gui_data::GuiData; -use crate::help_functions::{ - count_number_of_groups, get_full_name_from_path_name, get_image_path_temporary, get_max_file_name, resize_dynamic_image_dimension, NotebookObject, HEADER_ROW_COLOR, - NOTEBOOKS_INFOS, -}; +use crate::help_functions::{count_number_of_groups, get_full_name_from_path_name, get_max_file_name, resize_pixbuf_dimension, NotebookObject, HEADER_ROW_COLOR, NOTEBOOKS_INFOS}; use crate::localizer_core::generate_translation_hashmap; -const BIG_PREVIEW_SIZE: u32 = 600; -const SMALL_PREVIEW_SIZE: u32 = 100; +const BIG_PREVIEW_SIZE: i32 = 600; +const SMALL_PREVIEW_SIZE: i32 = 100; pub fn connect_button_compare(gui_data: &GuiData) { let button_compare = gui_data.bottom_buttons.buttons_compare.clone(); @@ -345,37 +339,39 @@ fn generate_cache_for_results(vector_with_path: Vec<(String, String, gtk::TreePa // For now threads cannot be used because Image and TreeIter cannot be used in threads let mut cache_all_images = Vec::new(); for (full_path, name, tree_path) in vector_with_path { - let name_lowercase = name.to_lowercase(); - let dynamic_image = if RAW_IMAGE_EXTENSIONS.iter().any(|f| name_lowercase.ends_with(f)) { - match get_dynamic_image_from_raw_image(&full_path) { - Some(t) => t, - None => { - println!("Failed to convert rawimage {}", full_path); - DynamicImage::new_rgb8(1, 1) + let small_img = gtk::Image::new(); + let big_img = gtk::Image::new(); + + match Pixbuf::from_file(&full_path) { + Ok(pixbuf) => + { + #[allow(clippy::never_loop)] + loop { + let pixbuf_big = match resize_pixbuf_dimension(pixbuf, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), InterpType::Nearest) { + None => { + println!("Failed to resize image {}.", full_path); + break; + } + Some(pixbuf) => pixbuf, + }; + let pixbuf_small = match resize_pixbuf_dimension(pixbuf_big.clone(), (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), InterpType::Nearest) { + None => { + println!("Failed to resize image {}.", full_path); + break; + } + Some(pixbuf) => pixbuf, + }; + + big_img.set_pixbuf(Some(&pixbuf_big)); + small_img.set_pixbuf(Some(&pixbuf_small)); + break; } } - } else { - match image::open(&full_path) { - Ok(t) => t, - Err(_) => { - println!("Failed to open image {}", full_path); - DynamicImage::new_rgb8(1, 1) - } + Err(e) => { + println!("Failed to open image {}, reason {}", full_path, e); } }; - let big_thumbnail = resize_dynamic_image_dimension(dynamic_image, (BIG_PREVIEW_SIZE, BIG_PREVIEW_SIZE), &FilterType::Triangle); - let big_path = get_image_path_temporary("roman", 1, "jpg"); - let _ = big_thumbnail.save(&big_path); - let big_img = gtk::Image::new(); - big_img.set_from_file(Some(big_path)); - - let small_thumbnail = resize_dynamic_image_dimension(big_thumbnail, (SMALL_PREVIEW_SIZE, SMALL_PREVIEW_SIZE), &FilterType::Triangle); - let small_path = get_image_path_temporary("roman", 1, "jpg"); - let _ = small_thumbnail.save(&small_path); - let small_img = gtk::Image::new(); - small_img.set_from_file(Some(small_path)); - cache_all_images.push((full_path, name, big_img, small_img, tree_path)); } cache_all_images diff --git a/czkawka_gui/src/help_functions.rs b/czkawka_gui/src/help_functions.rs index e4af417..5d7198b 100644 --- a/czkawka_gui/src/help_functions.rs +++ b/czkawka_gui/src/help_functions.rs @@ -1,4 +1,3 @@ -use directories_next::ProjectDirs; use gdk::gdk_pixbuf::{InterpType, Pixbuf}; use std::cmp::Ordering; use std::collections::HashMap; @@ -6,8 +5,6 @@ use std::path::{Path, PathBuf}; use gtk::prelude::*; use gtk::{Bin, ListStore, TextView, TreeView, Widget}; -use image::imageops::FilterType; -use image::DynamicImage; use crate::flg; use czkawka_core::big_file::BigFile; @@ -723,16 +720,16 @@ pub fn count_number_of_groups(tree_view: &TreeView, column_color: i32) -> u32 { number_of_selected_groups } -pub fn resize_dynamic_image_dimension(img: DynamicImage, requested_size: (u32, u32), filter_type: &FilterType) -> DynamicImage { - let current_ratio = img.width() as f32 / img.height() as f32; +pub fn resize_pixbuf_dimension(pixbuf: Pixbuf, requested_size: (i32, i32), interp_type: InterpType) -> Option { + let current_ratio = pixbuf.width() as f32 / pixbuf.height() as f32; let mut new_size; match current_ratio.partial_cmp(&(requested_size.0 as f32 / requested_size.1 as f32)).unwrap() { Ordering::Greater => { - new_size = (requested_size.0, (img.height() * requested_size.0) / img.width()); + new_size = (requested_size.0, (pixbuf.height() * requested_size.0) / pixbuf.width()); new_size = (std::cmp::max(new_size.0, 1), std::cmp::max(new_size.1, 1)); } Ordering::Less => { - new_size = ((img.width() * requested_size.1) / img.height(), requested_size.1); + new_size = ((pixbuf.width() * requested_size.1) / pixbuf.height(), requested_size.1); new_size = (std::cmp::max(new_size.0, 1), std::cmp::max(new_size.1, 1)); } Ordering::Equal => { @@ -740,17 +737,7 @@ pub fn resize_dynamic_image_dimension(img: DynamicImage, requested_size: (u32, u new_size = (std::cmp::max(new_size.0, 1), std::cmp::max(new_size.1, 1)); } } - img.resize(new_size.0, new_size.1, *filter_type) -} - -pub fn get_image_path_temporary(file_name: &str, number: u32, extension: &str) -> PathBuf { - let path_buf; - if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") { - path_buf = PathBuf::from(proj_dirs.cache_dir()); - } else { - path_buf = PathBuf::new().join("/var"); - } - path_buf.join(format!("{}{}.{}", file_name, number, extension)) + pixbuf.scale_simple(new_size.0, new_size.1, interp_type) } pub fn get_max_file_name(file_name: &str, max_length: usize) -> String { diff --git a/czkawka_gui/src/initialize_gui.rs b/czkawka_gui/src/initialize_gui.rs index 51cc1a8..6d445e5 100644 --- a/czkawka_gui/src/initialize_gui.rs +++ b/czkawka_gui/src/initialize_gui.rs @@ -1,17 +1,14 @@ use std::cell::RefCell; -use std::fs; use std::ops::Deref; -use std::path::Path; use std::rc::Rc; -use czkawka_core::common::get_dynamic_image_from_raw_image; -use directories_next::ProjectDirs; +use gdk::gdk_pixbuf::Pixbuf; +use gtk::gdk_pixbuf::InterpType; use gtk::prelude::*; use gtk::{CheckButton, Image, SelectionMode, TextView, TreeView}; -use image::imageops::FilterType; use crate::flg; -use czkawka_core::similar_images::{IMAGE_RS_EXTENSIONS, RAW_IMAGE_EXTENSIONS, SIMILAR_VALUES}; +use czkawka_core::similar_images::SIMILAR_VALUES; use czkawka_core::similar_videos::MAX_TOLERANCE; use crate::create_tree_view::*; @@ -682,130 +679,58 @@ fn show_preview( // Only show preview when selected is only one item, because there is no method to recognize current clicked item in multiselection if selected_rows.len() == 1 && check_button_settings_show_preview.is_active() { let tree_path = selected_rows[0].clone(); - if let Some(proj_dirs) = ProjectDirs::from("pl", "Qarmin", "Czkawka") { - // TODO labels on {} are in testing stage, so we just ignore for now this warning until found better idea how to fix this - #[allow(clippy::never_loop)] - 'dir: loop { - let cache_dir = proj_dirs.cache_dir(); - if cache_dir.exists() { - if !cache_dir.is_dir() { - add_text_to_text_view( - text_view_errors, - format!("Path {} doesn't point at folder, which is needed by image preview", cache_dir.display()).as_str(), - ); - break 'dir; - } - } else if let Err(e) = fs::create_dir_all(cache_dir) { + // TODO labels on {} are in testing stage, so we just ignore for now this warning until found better idea how to fix this + #[allow(clippy::never_loop)] + 'dir: loop { + let path = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_path).get::().unwrap(); + let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::().unwrap(); + + let file_name = get_full_name_from_path_name(&path, &name); + let file_name = file_name.as_str(); + + { + let preview_path = preview_path.borrow(); + let preview_path = preview_path.deref(); + if file_name == preview_path { + return; // Preview is already created, no need to recreate it + } + } + + let mut pixbuf = match Pixbuf::from_file(file_name) { + Ok(pixbuf) => pixbuf, + Err(e) => { add_text_to_text_view( text_view_errors, flg!( - "preview_failed_to_create_cache_dir", - generate_translation_hashmap(vec![("name", cache_dir.display().to_string()), ("reason", e.to_string())]) + "preview_image_opening_failure", + generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())]) ) .as_str(), ); break 'dir; } - let path = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_path).get::().unwrap(); - let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::().unwrap(); + }; - let file_name = get_full_name_from_path_name(&path, &name); - let file_name = file_name.as_str(); - - if let Some(extension) = Path::new(file_name).extension() { - let extension_lowercase = format!(".{}", extension.to_string_lossy().to_lowercase()); - - let is_raw_image = RAW_IMAGE_EXTENSIONS.contains(&extension_lowercase.as_str()); - if !IMAGE_RS_EXTENSIONS.contains(&extension_lowercase.as_str()) && !is_raw_image { - break 'dir; - } - - { - let preview_path = preview_path.borrow(); - let preview_path = preview_path.deref(); - if file_name == preview_path { - return; // Preview is already created, no need to recreate it - } - } - let img; - if !is_raw_image { - img = match image::open(&file_name) { - Ok(t) => t, - Err(e) => { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_temporary_file", - generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); - break 'dir; - } - }; - } else { - img = match get_dynamic_image_from_raw_image(file_name) { - Some(t) => t, - None => { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_temporary_file", - generate_translation_hashmap(vec![("name", file_name.to_string()), ("reason", "None".to_string())]) - ) - .as_str(), - ); - break 'dir; - } - } - } - if img.width() == 0 || img.height() == 0 { - add_text_to_text_view( - text_view_errors, - flg!("preview_0_size", generate_translation_hashmap(vec![("name", file_name.to_string())])).as_str(), - ); - break 'dir; - } - let img = resize_dynamic_image_dimension(img, (400, 400), &FilterType::Triangle); // Triangle and Nearest is the fastest - let file_dir = match is_raw_image { - true => cache_dir.join("cached_file.jpg"), - false => cache_dir.join(format!("cached_file.{}", extension.to_string_lossy().to_lowercase())), - }; - if let Err(e) = img.save(&file_dir) { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_temporary_image_save", - generate_translation_hashmap(vec![("name", file_dir.display().to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); - let _ = fs::remove_file(&file_dir); - break 'dir; - } - let string_dir = file_dir.to_string_lossy().to_string(); - image_preview.set_from_file(Some(string_dir)); - - { - let mut preview_path = preview_path.borrow_mut(); - *preview_path = file_name.to_string(); - } - - if let Err(e) = fs::remove_file(&file_dir) { - add_text_to_text_view( - text_view_errors, - flg!( - "preview_temporary_image_remove", - generate_translation_hashmap(vec![("name", file_dir.display().to_string()), ("reason", e.to_string())]) - ) - .as_str(), - ); - break 'dir; - } - created_image = true; + pixbuf = match resize_pixbuf_dimension(pixbuf, (400, 400), InterpType::Nearest) { + None => { + add_text_to_text_view( + text_view_errors, + flg!("preview_image_resize_failure", generate_translation_hashmap(vec![("name", file_name.to_string())])).as_str(), + ); + break 'dir; } - break 'dir; + Some(pixbuf) => pixbuf, + }; + + image_preview.set_pixbuf(Some(&pixbuf)); + { + let mut preview_path = preview_path.borrow_mut(); + *preview_path = file_name.to_string(); } + + created_image = true; + + break 'dir; } } if created_image {