Remove thumbnail letterboxing, fix scroll
This commit is contained in:
parent
b00bbbadc0
commit
ec879c31b1
37
src/app.rs
37
src/app.rs
@ -1,10 +1,12 @@
|
|||||||
use crate::egui_tools::EguiRenderer;
|
use crate::egui_tools::EguiRenderer;
|
||||||
use egui::load::{ImageLoadResult, ImageLoader};
|
use egui::load::{ImageLoadResult, ImageLoader};
|
||||||
use egui::{Align2, Color32, ColorImage, Event, Image, ImageSource, Key, PointerButton, Sense};
|
use egui::{
|
||||||
|
Align, Align2, Color32, ColorImage, Event, Image, ImageSource, Key, PointerButton, Sense,
|
||||||
|
};
|
||||||
use egui_wgpu::wgpu::SurfaceError;
|
use egui_wgpu::wgpu::SurfaceError;
|
||||||
use egui_wgpu::{ScreenDescriptor, wgpu};
|
use egui_wgpu::{ScreenDescriptor, wgpu};
|
||||||
use image::metadata::Orientation;
|
use image::metadata::Orientation;
|
||||||
use imflow::image::{ImageFormat, swap_wh};
|
use imflow::image::{ImageData, ImageFormat, swap_wh};
|
||||||
use imflow::store::{FileFilters, ImageStore};
|
use imflow::store::{FileFilters, ImageStore};
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -224,6 +226,7 @@ pub struct AppState {
|
|||||||
pub transform_buffer: wgpu::Buffer,
|
pub transform_buffer: wgpu::Buffer,
|
||||||
pub transform_data: TransformData,
|
pub transform_data: TransformData,
|
||||||
pub filters: FileFilters,
|
pub filters: FileFilters,
|
||||||
|
pub selected_image: ImageData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
@ -282,7 +285,11 @@ impl AppState {
|
|||||||
|
|
||||||
let egui_renderer = EguiRenderer::new(&device, surface_config.format, None, 1, window);
|
let egui_renderer = EguiRenderer::new(&device, surface_config.format, None, 1, window);
|
||||||
|
|
||||||
let store = Arc::new(RwLock::new(ImageStore::new(path)));
|
let image_store = ImageStore::new(path);
|
||||||
|
|
||||||
|
// TODO: verify
|
||||||
|
let selected_image = image_store.current_image_path.clone();
|
||||||
|
let store = Arc::new(RwLock::new(image_store));
|
||||||
|
|
||||||
let loader = ImflowEguiLoader::new(store.clone());
|
let loader = ImflowEguiLoader::new(store.clone());
|
||||||
|
|
||||||
@ -317,6 +324,7 @@ impl AppState {
|
|||||||
transform_buffer,
|
transform_buffer,
|
||||||
transform_data,
|
transform_data,
|
||||||
filters: FileFilters::default(),
|
filters: FileFilters::default(),
|
||||||
|
selected_image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,7 +395,12 @@ impl App {
|
|||||||
|
|
||||||
pub fn update_texture(&mut self) {
|
pub fn update_texture(&mut self) {
|
||||||
let state = self.state.as_mut().unwrap();
|
let state = self.state.as_mut().unwrap();
|
||||||
|
{
|
||||||
|
let store = state.store.read().unwrap();
|
||||||
|
if state.selected_image == store.current_image_path {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
let mut store = state.store.write().unwrap();
|
let mut store = state.store.write().unwrap();
|
||||||
store.check_loaded_images();
|
store.check_loaded_images();
|
||||||
@ -641,12 +654,15 @@ impl App {
|
|||||||
let filename;
|
let filename;
|
||||||
let window;
|
let window;
|
||||||
let filtered_images;
|
let filtered_images;
|
||||||
|
let current_image;
|
||||||
|
let mut selected_image = None;
|
||||||
{
|
{
|
||||||
let store = state.store.read().unwrap();
|
let store = state.store.read().unwrap();
|
||||||
rating = store.get_current_rating();
|
rating = store.get_current_rating();
|
||||||
path = store.current_image_path.clone();
|
path = store.current_image_path.clone();
|
||||||
current_id = store.current_image_id;
|
current_id = store.current_image_id;
|
||||||
image_count = store.available_images.len();
|
image_count = store.available_images.len();
|
||||||
|
current_image = store.current_image_path.clone();
|
||||||
filtered_images = store.get_filtered_images(&state.filters);
|
filtered_images = store.get_filtered_images(&state.filters);
|
||||||
filename = path.path.file_name().unwrap();
|
filename = path.path.file_name().unwrap();
|
||||||
window = self.window.as_ref().unwrap();
|
window = self.window.as_ref().unwrap();
|
||||||
@ -690,10 +706,11 @@ impl App {
|
|||||||
.corner_radius(10)
|
.corner_radius(10)
|
||||||
.sense(Sense::click()),
|
.sense(Sense::click()),
|
||||||
);
|
);
|
||||||
|
if current_image == image {
|
||||||
|
image_widget.scroll_to_me(Some(Align::Center));
|
||||||
|
}
|
||||||
if image_widget.clicked() {
|
if image_widget.clicked() {
|
||||||
image_widget.scroll_to_me(None);
|
selected_image = Some(image);
|
||||||
// ui.scroll_to_rect(image_widget.rect, None);
|
|
||||||
println!("{}", image.get_hash_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -712,6 +729,10 @@ impl App {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if let Some(selected_image) = selected_image {
|
||||||
|
state.store.write().unwrap().select_image(selected_image);
|
||||||
|
}
|
||||||
|
|
||||||
state.egui_renderer.end_frame_and_draw(
|
state.egui_renderer.end_frame_and_draw(
|
||||||
&state.device,
|
&state.device,
|
||||||
&state.queue,
|
&state.queue,
|
||||||
@ -724,6 +745,8 @@ impl App {
|
|||||||
|
|
||||||
state.queue.submit(Some(encoder.finish()));
|
state.queue.submit(Some(encoder.finish()));
|
||||||
surface_texture.present();
|
surface_texture.present();
|
||||||
|
|
||||||
|
self.update_texture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use egui::{Color32, Context, Visuals};
|
use egui::{vec2, Color32, Context, Rangef, Style, Visuals};
|
||||||
use egui_wgpu::wgpu::{CommandEncoder, Device, Queue, StoreOp, TextureFormat, TextureView};
|
use egui_wgpu::wgpu::{CommandEncoder, Device, Queue, StoreOp, TextureFormat, TextureView};
|
||||||
use egui_wgpu::{Renderer, ScreenDescriptor, wgpu};
|
use egui_wgpu::{Renderer, ScreenDescriptor, wgpu};
|
||||||
use egui_winit::State;
|
use egui_winit::State;
|
||||||
@ -25,6 +25,7 @@ impl EguiRenderer {
|
|||||||
) -> EguiRenderer {
|
) -> EguiRenderer {
|
||||||
let egui_context = Context::default();
|
let egui_context = Context::default();
|
||||||
egui_context.options_mut(|o| o.line_scroll_speed = 200.0);
|
egui_context.options_mut(|o| o.line_scroll_speed = 200.0);
|
||||||
|
egui_context.style_mut(|s| s.scroll_animation.duration = Rangef::new(0.1, 5.0));
|
||||||
egui_context.set_visuals_of(
|
egui_context.set_visuals_of(
|
||||||
egui::Theme::Dark,
|
egui::Theme::Dark,
|
||||||
Visuals {
|
Visuals {
|
||||||
|
16
src/image.rs
16
src/image.rs
@ -348,9 +348,19 @@ pub fn load_thumbnail_exif(path: &ImageData) -> Option<ImflowImageBuffer> {
|
|||||||
let decoder = image::ImageReader::new(Cursor::new(thumbnail))
|
let decoder = image::ImageReader::new(Cursor::new(thumbnail))
|
||||||
.with_guessed_format()
|
.with_guessed_format()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut image = decoder.decode().unwrap();
|
let image = decoder.decode().unwrap();
|
||||||
|
|
||||||
let orientation = path.orientation;
|
let orientation = path.orientation;
|
||||||
|
|
||||||
|
let width = image.width();
|
||||||
|
let height = image.height();
|
||||||
|
// TODO: extract from image
|
||||||
|
let ratio_image = 1.5;
|
||||||
|
let ratio_thumbnail = width as f32 / height as f32;
|
||||||
|
let crop = ratio_thumbnail / ratio_image;
|
||||||
|
let start = ((0.5 - (crop / 2.0)) * height as f32).round();
|
||||||
|
let cropped_height = (height as f32 * crop) as u32;
|
||||||
|
let mut image = image.crop_imm(0, start as u32, width, cropped_height);
|
||||||
|
|
||||||
image.apply_orientation(orientation);
|
image.apply_orientation(orientation);
|
||||||
let width: usize = image.width() as usize;
|
let width: usize = image.width() as usize;
|
||||||
let height: usize = image.height() as usize;
|
let height: usize = image.height() as usize;
|
||||||
@ -457,7 +467,7 @@ pub fn load_heif(path: &ImageData, resize: bool) -> ImflowImageBuffer {
|
|||||||
rgba_buffer: u32_slice.to_vec(),
|
rgba_buffer: u32_slice.to_vec(),
|
||||||
rating,
|
rating,
|
||||||
// TODO: verify
|
// TODO: verify
|
||||||
orientation: path.orientation,
|
orientation: Orientation::NoTransforms,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
src/store.rs
39
src/store.rs
@ -9,7 +9,7 @@ use std::path::PathBuf;
|
|||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use threadpool::ThreadPool;
|
use threadpool::ThreadPool;
|
||||||
|
|
||||||
const PRELOAD_NEXT_IMAGE_N: usize = 16;
|
const PRELOAD_NEXT_IMAGE_N: usize = 0;
|
||||||
|
|
||||||
pub struct FileFilters {
|
pub struct FileFilters {
|
||||||
pub rating: [bool; 6],
|
pub rating: [bool; 6],
|
||||||
@ -47,7 +47,6 @@ impl ImageStore {
|
|||||||
pub fn new(path: PathBuf) -> Self {
|
pub fn new(path: PathBuf) -> Self {
|
||||||
let current_image_id: usize = 0;
|
let current_image_id: usize = 0;
|
||||||
let mut loaded_images: HashMap<ImageData, ImflowImageBuffer> = HashMap::new();
|
let mut loaded_images: HashMap<ImageData, ImflowImageBuffer> = HashMap::new();
|
||||||
// let mut loaded_thumbnails: HashMap<ImageData, ImflowImageBuffer> = HashMap::new();
|
|
||||||
let available_images = load_available_images(path);
|
let available_images = load_available_images(path);
|
||||||
let new_path = available_images[0].clone();
|
let new_path = available_images[0].clone();
|
||||||
|
|
||||||
@ -68,10 +67,10 @@ impl ImageStore {
|
|||||||
available_images
|
available_images
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.for_each_with(sender, |s, path| {
|
.for_each_with(sender, |s, path| {
|
||||||
if path.embedded_thumbnail {
|
// if path.embedded_thumbnail {
|
||||||
let buf = load_thumbnail(path);
|
let buf = load_thumbnail(path);
|
||||||
s.send((path.clone(), buf)).unwrap();
|
s.send((path.clone(), buf)).unwrap();
|
||||||
}
|
// }
|
||||||
});
|
});
|
||||||
let loaded_thumbnails: HashMap<_, _> = receiver.iter().collect();
|
let loaded_thumbnails: HashMap<_, _> = receiver.iter().collect();
|
||||||
let total_time = total_start.elapsed();
|
let total_time = total_start.elapsed();
|
||||||
@ -81,9 +80,6 @@ impl ImageStore {
|
|||||||
loaded_thumbnails.len()
|
loaded_thumbnails.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// let path = available_images[0].clone();
|
|
||||||
// let image = load_image(&path.clone());
|
|
||||||
// loaded_images.insert(path, image);
|
|
||||||
let (path, image) = first_image_thread.join().unwrap();
|
let (path, image) = first_image_thread.join().unwrap();
|
||||||
loaded_images.insert(path, image);
|
loaded_images.insert(path, image);
|
||||||
let mut state = Self {
|
let mut state = Self {
|
||||||
@ -125,16 +121,15 @@ impl ImageStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_rating(&self) -> i32 {
|
pub fn get_current_rating(&self) -> i32 {
|
||||||
let imbuf = if let Some(full) = self.get_current_image() {
|
self.current_image_path.rating
|
||||||
// println!("full");
|
// let imbuf = if let Some(full) = self.get_current_image() {
|
||||||
full
|
// // println!("full");
|
||||||
} else {
|
// full
|
||||||
// TODO: this assumes loaded thumbnail
|
// } else {
|
||||||
self.loaded_images_thumbnails
|
// // TODO: this assumes loaded thumbnail
|
||||||
.get(&self.current_image_path)
|
|
||||||
.unwrap()
|
// };
|
||||||
};
|
// imbuf.rating
|
||||||
imbuf.rating
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preload_next_images(&mut self, n: usize) {
|
pub fn preload_next_images(&mut self, n: usize) {
|
||||||
@ -150,7 +145,6 @@ impl ImageStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn request_load(&mut self, path: ImageData) {
|
pub fn request_load(&mut self, path: ImageData) {
|
||||||
// return;
|
|
||||||
if self.loaded_images.contains_key(&path) || self.currently_loading.contains(&path) {
|
if self.loaded_images.contains_key(&path) || self.currently_loading.contains(&path) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -183,6 +177,14 @@ impl ImageStore {
|
|||||||
self.preload_next_images(PRELOAD_NEXT_IMAGE_N);
|
self.preload_next_images(PRELOAD_NEXT_IMAGE_N);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn select_image(&mut self, selected_image: ImageData) {
|
||||||
|
if !self.loaded_images.contains_key(&selected_image) {
|
||||||
|
self.request_load(selected_image.clone());
|
||||||
|
}
|
||||||
|
self.current_image_path = selected_image;
|
||||||
|
self.preload_next_images(PRELOAD_NEXT_IMAGE_N);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_current_image(&self) -> Option<&ImflowImageBuffer> {
|
pub fn get_current_image(&self) -> Option<&ImflowImageBuffer> {
|
||||||
self.loaded_images.get(&self.current_image_path)
|
self.loaded_images.get(&self.current_image_path)
|
||||||
}
|
}
|
||||||
@ -214,7 +216,6 @@ impl ImageStore {
|
|||||||
.get(&self.current_image_path)
|
.get(&self.current_image_path)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
// panic!();
|
|
||||||
|
|
||||||
let buf = load_thumbnail(&self.current_image_path);
|
let buf = load_thumbnail(&self.current_image_path);
|
||||||
self.loaded_images_thumbnails
|
self.loaded_images_thumbnails
|
||||||
|
Loading…
x
Reference in New Issue
Block a user