Simplify and speedup preview creating (#660)
* Simplify and speedup preview creating * Speedup also comparing images
This commit is contained in:
parent
f843673c7e
commit
39b2f4bc36
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Pixbuf> {
|
||||
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 {
|
||||
|
|
|
@ -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::<String>().unwrap();
|
||||
let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::<String>().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::<String>().unwrap();
|
||||
let name = tree_model.value(&tree_model.iter(&tree_path).unwrap(), column_name).get::<String>().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 {
|
||||
|
|
Loading…
Reference in New Issue