diff --git a/Cargo.lock b/Cargo.lock index 740fcac..f163543 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4206,8 +4206,8 @@ dependencies = [ "self_update", "serde", "serde_json", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum 0.26.1", + "strum_macros 0.26.1", "thiserror", "tiff", "tiny-skia 0.9.1", @@ -5014,9 +5014,9 @@ dependencies = [ [[package]] name = "rfd" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9e7b57df6e8472152674607f6cc68aa14a748a3157a857a94f516e11aeacc2" +checksum = "c0d8ab342bcc5436e04d3a4c1e09e17d74958bfaddf8d5fad6f85607df0f994f" dependencies = [ "block", "dispatch", @@ -5630,11 +5630,11 @@ dependencies = [ [[package]] name = "strum" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "723b93e8addf9aa965ebe2d11da6d7540fa2283fcea14b3371ff055f7ba13f5f" dependencies = [ - "strum_macros 0.25.3", + "strum_macros 0.26.1", ] [[package]] @@ -5652,9 +5652,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7a3417fc93d76740d974a01654a09777cb500428cc874ca9f45edfe0c4d4cd18" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index f5c24e5..d045446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,13 +47,13 @@ rand = "0.8" rand_chacha = "0.3" rayon = "1.7" resvg = "0.33.0" -rfd = {version = "0.12", optional = true} +rfd = {version = "0.13", optional = true} rgb = "0.8" self_update = {version = "0.39", default-features = false, features = ["rustls"], optional = true} serde = {version = "1.0", features = ["derive"]} serde_json = "1.0" -strum = {version = "0.25", features = ["derive"]} -strum_macros = "0.25" +strum = {version = "0.26", features = ["derive"]} +strum_macros = "0.26" tiny-skia = "0.9" turbojpeg = {version = "0.5", features = ["image"], optional = true} usvg = "0.33.0" @@ -81,7 +81,7 @@ webp-animation = { version = "0.9.0", features = ["static"] } heif = ["libheif-rs"] avif_native = ["avif-decode"] dav1d = ["libavif-image"] -default = ["turbo", "file_open", "avif_native", "update"] +default = ["turbo", "avif_native", "file_open", "update"] file_open = ["rfd"] turbo = ["turbojpeg"] update = ["self_update"] diff --git a/res/logo.svg b/res/logo.svg index f195707..546b1f1 100644 --- a/res/logo.svg +++ b/res/logo.svg @@ -1,358 +1 @@ - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/res/net.gif b/res/net.gif index e24547a..3656b7b 100644 Binary files a/res/net.gif and b/res/net.gif differ diff --git a/res/oculante.png b/res/oculante.png index 1ff2a91..65fcf17 100644 Binary files a/res/oculante.png and b/res/oculante.png differ diff --git a/res/premult.png b/res/premult.png index f2afbcd..2b2cc0f 100644 Binary files a/res/premult.png and b/res/premult.png differ diff --git a/res/screenshot_1.png b/res/screenshot_1.png index d296729..7650ae4 100644 Binary files a/res/screenshot_1.png and b/res/screenshot_1.png differ diff --git a/res/screenshot_exif.png b/res/screenshot_exif.png index c485c40..3c62a71 100644 Binary files a/res/screenshot_exif.png and b/res/screenshot_exif.png differ diff --git a/src/appstate.rs b/src/appstate.rs index 93cfe3a..5d7d13e 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -88,6 +88,7 @@ pub struct OculanteState { pub redraw: bool, pub first_start: bool, pub toasts: Toasts, + pub filebrowser_id: Option, } impl OculanteState { @@ -147,6 +148,7 @@ impl Default for OculanteState { redraw: Default::default(), first_start: true, toasts: Toasts::default().with_anchor(egui_notify::Anchor::BottomLeft), + filebrowser_id: None, } } } diff --git a/src/filebrowser.rs b/src/filebrowser.rs new file mode 100644 index 0000000..d44f88c --- /dev/null +++ b/src/filebrowser.rs @@ -0,0 +1,267 @@ +use anyhow::{Context, Result}; +use dirs; +use egui_phosphor::variants::regular::*; +use notan::egui::{self, *}; +use std::io::Write; + +use std::{ + fs::{self, read_to_string, File}, + path::{Path, PathBuf}, +}; + +fn load_recent_dir() -> Result { + Ok(PathBuf::from(read_to_string( + dirs::cache_dir() + .context("Can't get temp dir")? + .join(".efd_history"), + )?)) +} + +fn save_recent_dir(p: &Path) -> Result<()> { + let p = if p.is_file() { + p.parent().context("Can't get parent")?.to_path_buf() + } else { + p.to_path_buf() + }; + + let mut f = File::create( + dirs::cache_dir() + .context("Can't get temp dir")? + .join(".efd_history"), + )?; + write!(f, "{}", p.to_string_lossy())?; + Ok(()) +} + +pub fn browse_modal( + save: bool, + filter: &[&str], + mut callback: F, + ctx: &egui::Context, +) { + let mut path = ctx + .data(|r| r.get_temp::(Id::new("FBPATH"))) + .unwrap_or(load_recent_dir().unwrap_or_default()); + + let mut open = true; + + egui::Window::new(if save { "Save" } else { "Open" }) + .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) + .collapsible(false) + .open(&mut open) + .resizable(true) + .default_width(500.) + .default_height(600.) + // .auto_sized() + .show(ctx, |ui| { + browse( + &mut path, + filter, + save, + |p| { + callback(p); + ctx.memory_mut(|w| w.close_popup()); + }, + ui, + ); + + if ui.button("Cancel").clicked() { + ui.ctx().memory_mut(|w| w.close_popup()); + } + + ctx.data_mut(|w| w.insert_temp(Id::new("FBPATH"), path)); + }); + if !open { + ctx.memory_mut(|w| w.close_popup()); + } +} + +pub fn browse( + path: &mut PathBuf, + filter: &[&str], + save: bool, + mut callback: F, + ui: &mut Ui, +) { + let mut filename = ui + .ctx() + .data(|r| r.get_temp::(Id::new("FBFILENAME"))) + .unwrap_or(String::from("unnamed.png")); + + let d = fs::read_dir(&path).ok(); + ui.horizontal(|ui| { + ui.allocate_ui_with_layout( + Vec2::new(120., ui.available_height()), + Layout::top_down_justified(Align::LEFT), + |ui| { + if let Some(d) = dirs::desktop_dir() { + if ui.button(format!("{DESKTOP} Desktop")).clicked() { + *path = d; + } + } + if let Some(d) = dirs::home_dir() { + if ui.button(format!("{HOUSE} Home")).clicked() { + *path = d; + } + } + if let Some(d) = dirs::document_dir() { + if ui.button(format!("{FILE} Documents")).clicked() { + *path = d; + } + } + if let Some(d) = dirs::download_dir() { + if ui.button(format!("{DOWNLOAD} Downloads")).clicked() { + *path = d; + } + } + if let Some(d) = dirs::picture_dir() { + if ui.button(format!("{IMAGES} Pictures")).clicked() { + *path = d; + } + } + }, + ); + ui.separator(); + + ui.vertical(|ui| { + if ui.button(ARROW_BEND_LEFT_UP).clicked() { + if let Some(d) = path.parent() { + let p = d.to_path_buf(); + *path = p; + } + } + + ui.separator(); + + egui::ScrollArea::new([false, true]) + .max_width(100.) + .min_scrolled_height(400.) + .auto_shrink([true, false]) + .show(ui, |ui| match d { + Some(contents) => { + egui::Grid::new("browser") + .striped(true) + .num_columns(0) + .min_col_width(ui.available_width()) + .show(ui, |ui| { + let mut entries = contents + .into_iter() + .flat_map(|x| x) + .filter(|de| !de.file_name().to_string_lossy().starts_with(".")) + .filter(|de| { + de.path().is_dir() + || filter.contains( + &de.path() + .extension() + .map(|ext| ext.to_string_lossy().to_string()) + .unwrap_or_default() + .to_lowercase() + .as_str(), + ) + }) + .collect::>(); + + entries.sort_by(|a, b| { + a.file_name() + .to_string_lossy() + .to_lowercase() + .cmp(&b.file_name().to_string_lossy().to_lowercase()) + }); + + for de in entries { + if de.path().is_dir() { + if ui + .add( + egui::Button::new(format!( + "{FOLDER} {}", + de.file_name() + .to_string_lossy() + .chars() + .take(50) + .collect::() + )) + .frame(false), + ) + .clicked() + { + *path = de.path(); + } + } else { + if ui + .add( + egui::Button::new(format!( + "{IMAGE_SQUARE} {}", + de.file_name() + .to_string_lossy() + .chars() + .take(50) + .collect::() + )) + .frame(false), + ) + .clicked() + { + _ = save_recent_dir(&de.path()); + if !save { + callback(&de.path()); + } else { + filename = de + .path() + .to_path_buf() + .file_name() + .map(|f| f.to_string_lossy().to_string()) + .unwrap_or_default(); + ui.ctx().data_mut(|w| { + w.insert_temp( + Id::new("FBFILENAME"), + filename.clone(), + ) + }); + } + // self.result = Some(de.path().to_path_buf()); + } + } + ui.end_row(); + } + }); + } + None => { + ui.label("no contents"); + } + }); + ui.spacing(); + ui.separator(); + + if save { + ui.horizontal(|ui| { + ui.label("Filename"); + ui.add( + egui::TextEdit::singleline(&mut filename) + .desired_width(ui.available_width() - 10.), + ); + }); + + ui.horizontal(|ui| { + let ext = Path::new(&filename) + .extension() + .map(|e| e.to_string_lossy().to_string()) + .unwrap_or_default(); + for f in filter { + if ui.selectable_label(&ext == f, f.to_string()).clicked() { + filename = Path::new(&filename) + .with_extension(f) + .to_string_lossy() + .to_string(); + } + } + }); + + ui.ctx() + .data_mut(|w| w.insert_temp(Id::new("FBFILENAME"), filename.clone())); + if ui.button("Save").clicked() { + callback(&path.join(filename)); + } + } + }); + }); +} diff --git a/src/image_editing.rs b/src/image_editing.rs index 2e80877..9399b45 100644 --- a/src/image_editing.rs +++ b/src/image_editing.rs @@ -1,10 +1,12 @@ use std::collections::HashMap; use std::fmt; use std::num::NonZeroU32; -use std::path::{Path, PathBuf}; +use std::path::Path; use crate::paint::PaintStroke; use crate::ui::EguiExt; +#[cfg(not(feature = "file_open"))] +use crate::{filebrowser, SUPPORTED_EXTENSIONS}; use anyhow::Result; use evalexpr::*; @@ -224,11 +226,6 @@ impl ImageOperation { Self::Expression(expr) => ui.text_edit_singleline(expr), Self::LUT(lut_name) => { ui.scope(|ui| { - // let last_folder: &mut PathBuf = ui.ctx().data_mut(|w| w.get_temp_mut_or_default::(Id::new("lutsrc"))); - let last_folder: Option = ui - .ctx() - .data_mut(|w| w.get_persisted::(Id::new("lutsrc"))); - let mut x = ui.allocate_response(vec2(0.0, 0.0), Sense::click_and_drag()); ui.vertical(|ui| { let lut_fname = Path::new(lut_name) @@ -259,34 +256,64 @@ impl ImageOperation { } }); + #[cfg(not(feature = "file_open"))] + { + if ui.button("Load lut").clicked() { + ui.ctx().memory_mut(|w| w.open_popup(Id::new("LUT"))); + } + + if ui.ctx().memory(|w| w.is_popup_open(Id::new("LUT"))) { + filebrowser::browse_modal( + false, + SUPPORTED_EXTENSIONS, + |p| { + *lut_name = p.to_string_lossy().to_string(); + x.mark_changed(); + }, + ui.ctx(), + ); + } + } + #[cfg(feature = "file_open")] - if ui - .button("Load from disk") - .on_hover_ui(|ui| { - ui.label("Load Hald CLUT"); - }) - .clicked() { - if let Some(lut_file) = rfd::FileDialog::new() - .set_directory(last_folder.unwrap_or_default()) - .pick_file() + // let last_folder: &mut PathBuf = ui.ctx().data_mut(|w| w.get_temp_mut_or_default::(Id::new("lutsrc"))); + let last_folder: Option = ui.ctx().data_mut(|w| { + w.get_persisted::(Id::new("lutsrc")) + }); + if ui + .button("Load from disk") + .on_hover_ui(|ui| { + ui.label("Load Hald CLUT"); + }) + .clicked() { - *lut_name = lut_file.to_string_lossy().to_string(); - let parent = lut_file - .parent() - .map(|p| p.to_path_buf()) - .unwrap_or_default(); - ui.ctx() - .data_mut(|w| w.insert_persisted(Id::new("lutsrc"), parent)); + if let Some(lut_file) = rfd::FileDialog::new() + .set_directory(last_folder.unwrap_or_default()) + .pick_file() + { + *lut_name = lut_file.to_string_lossy().to_string(); + let parent = lut_file + .parent() + .map(|p| p.to_path_buf()) + .unwrap_or_default(); + ui.ctx().data_mut(|w| { + w.insert_persisted(Id::new("lutsrc"), parent) + }); + } + x.mark_changed(); } - x.mark_changed(); } + ui.label("Find more LUTs here:"); - ui.hyperlink_to( - "Cédric Eberhardt's collection", - "https://github.com/cedeber/hald-clut", - ); + if ui + .link("Cédric Eberhardt's collection") + .on_hover_text("You can find more interesting LUTs here to use") + .clicked() + { + _ = webbrowser::open("https://github.com/cedeber/hald-clut"); + } }); x }) diff --git a/src/main.rs b/src/main.rs index ce394cf..cf86c5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,8 @@ use utils::*; mod appstate; mod image_loader; use appstate::*; +#[cfg(not(feature = "file_open"))] +mod filebrowser; pub mod ktx2_loader; // mod events; @@ -196,7 +198,7 @@ fn init(gfx: &mut Graphics, plugins: &mut Plugins) -> OculanteState { Err(e) => { warn!("Settings failed to load: {e}. This may happen after application updates. Generating a fresh file."); state.persistent_settings = Default::default(); - state.persistent_settings.save(); + state.persistent_settings.save_blocking(); } } @@ -420,11 +422,16 @@ fn event(app: &mut App, state: &mut OculanteState, evt: Event) { } } } - #[cfg(feature = "file_open")] if key_pressed(app, state, Browse) { state.redraw = true; + #[cfg(feature = "file_open")] browse_for_image_path(state); + #[cfg(not(feature = "file_open"))] + { + state.filebrowser_id = Some("OPEN_SHORTCUT".into()); + } } + if key_pressed(app, state, NextImage) { if state.is_loaded { next_image(state) @@ -719,7 +726,6 @@ fn drawe(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut O state.persistent_settings.last_open_directory = dir.to_path_buf(); } state.current_path = Some(p); - _ = state.persistent_settings.save(); } // check if a new texture has been sent @@ -815,6 +821,9 @@ fn drawe(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut O FrameSource::Animation => { state.redraw = true; } + FrameSource::CompareResult => { + state.redraw = false; + } } if let Some(tex) = &mut state.current_texture { @@ -964,6 +973,23 @@ fn drawe(app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins, state: &mut O // ctx.request_repaint_after(Duration::from_secs(1)); state.toasts.show(ctx); + if let Some(id) = state.filebrowser_id.take() { + ctx.memory_mut(|w| w.open_popup(Id::new(id))); + } + #[cfg(not(feature = "file_open"))] + { + if ctx.memory(|w| w.is_popup_open(Id::new("OPEN_SHORTCUT"))) { + filebrowser::browse_modal( + false, + SUPPORTED_EXTENSIONS, + |p| { + let _ = state.load_channel.0.clone().send(p.to_path_buf()); + }, + ctx, + ); + } + } + if !state.persistent_settings.zen_mode { egui::TopBottomPanel::top("menu") .min_height(30.) diff --git a/src/settings.rs b/src/settings.rs index c665fcd..74f8f99 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -97,7 +97,7 @@ impl PersistentSettings { } // save settings in a thread so we don't block - pub fn save(&self) { + pub fn save_threaded(&self) { let settings = self.clone(); std::thread::spawn(move || { _ = save(&settings); @@ -112,7 +112,7 @@ impl PersistentSettings { fn save(s: &PersistentSettings) -> Result<()> { let local_dir = dirs::data_local_dir().ok_or(anyhow!("Can't get local dir"))?; let f = File::create(local_dir.join(".oculante"))?; - Ok(serde_json::to_writer_pretty(f, s)?) + Ok(serde_json::to_writer(f, s)?) } pub fn set_system_theme(ctx: &Context) { diff --git a/src/ui.rs b/src/ui.rs index 9509b03..5292c76 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "file_open")] -use crate::browse_for_image_path; use crate::{ appstate::{ImageGeometry, Message, OculanteState}, image_editing::{process_pixels, Channel, GradientStop, ImageOperation, ScaleFilter}, @@ -12,7 +10,10 @@ use crate::{ load_image_from_path, next_image, prev_image, send_extended_info, set_title, solo_channel, toggle_fullscreen, unpremult, ColorChannel, ImageExt, }, + FrameSource, }; +#[cfg(not(feature = "file_open"))] +use crate::{filebrowser, SUPPORTED_EXTENSIONS}; const ICON_SIZE: f32 = 24.; @@ -177,14 +178,12 @@ impl EguiExt for Ui { } } - /// Proof-of-concept funtion to draw texture completely with egui #[allow(unused)] pub fn image_ui(ctx: &Context, state: &mut OculanteState, gfx: &mut Graphics) { if let Some(texture) = &state.current_texture { let tex_id = gfx.egui_register_texture(texture); - let image_rect = Rect::from_center_size( Pos2::new( state.image_geometry.offset.x @@ -207,9 +206,8 @@ pub fn image_ui(ctx: &Context, state: &mut OculanteState, gfx: &mut Graphics) { tex_id.id, image_rect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), - Color32::WHITE + Color32::WHITE, ); - } // state.image_geometry.scale; @@ -385,22 +383,17 @@ pub fn info_ui(ctx: &Context, state: &mut OculanteState, gfx: &mut Graphics) { for (path, geo) in compare_list { if ui.selectable_label(p==&path, path.file_name().map(|f| f.to_string_lossy().to_string()).unwrap_or_default().to_string()).clicked(){ state.image_geometry = geo.clone(); - state.is_loaded = false; - state.current_image = None; state .player - .load(&path, state.message_channel.0.clone()); + .load_advanced(&path, Some(FrameSource::CompareResult), state.message_channel.0.clone()); state.current_path = Some(path); - state.persistent_settings.keep_view = true; } } if ui.button("Clear").clicked() { state.compare_list.clear(); } } - if state.is_loaded { - state.persistent_settings.keep_view = false; - } + }); }); @@ -1166,34 +1159,6 @@ pub fn edit_ui(app: &mut App, ctx: &Context, state: &mut OculanteState, gfx: &mu } } - #[cfg(not(feature = "file_open"))] - ui.horizontal(|ui| { - ui.label("File:"); - if let Some(p) = &mut state.current_path { - if let Some(pstem) = p.file_stem() { - let mut stem = pstem.to_string_lossy().to_string(); - if ui.text_edit_singleline(&mut stem).changed() { - if let Some(parent) = p.parent() { - *p = parent - .join(stem) - .with_extension(&state.edit_state.export_extension); - } - } - } - } - egui::ComboBox::from_id_source("ext") - .selected_text(&state.edit_state.export_extension) - .width(ui.available_width()) - .show_ui(ui, |ui| { - for f in ["png", "jpg", "bmp", "webp", "tif", "tga"] { - ui.selectable_value( - &mut state.edit_state.export_extension, - f.to_string(), - f, - ); - } - }); - }); #[cfg(feature = "turbo")] jpg_lossless_ui(state, ui); @@ -1263,6 +1228,55 @@ pub fn edit_ui(app: &mut App, ctx: &Context, state: &mut OculanteState, gfx: &mu } } + #[cfg(not(feature = "file_open"))] + if state.current_image.is_some() { + if ui.button(format!("{FLOPPY_DISK} Save as...")).clicked() { + ui.ctx().memory_mut(|w| w.open_popup(Id::new("SAVE"))); + + } + + + if ctx.memory(|w| w.is_popup_open(Id::new("SAVE"))) { + + + let msg_sender = state.message_channel.0.clone(); + + filebrowser::browse_modal( + true, + &["png", "jpg", "bmp", "webp", "tif", "tga"], + |p| { + match state.edit_state.result_pixel_op + .save(&p) { + Ok(_) => { + _ = msg_sender.send(Message::Saved(p.clone())); + debug!("Saved to {}", p.display()); + // Re-apply exif + if let Some(info) = &state.image_info { + debug!("Extended image info present"); + + // before doing anything, make sure we have raw exif data + if info.raw_exif.is_some() { + if let Err(e) = fix_exif(&p, info.raw_exif.clone()) { + error!("{e}"); + } else { + info!("Saved EXIF."); + _ = msg_sender.send(Message::Info("Exif metadata was saved to file".into())); + } + } else { + debug!("No raw exif"); + } + } + } + Err(e) => { + _ = msg_sender.send(Message::err(&format!("Error: Could not save: {e}"))); + } + } + }, + ctx, + ); + } + } + if let Some(p) = &state.current_path { let text = if p // .with_extension(&state.edit_state.export_extension) @@ -1796,12 +1810,29 @@ pub fn main_menu(ui: &mut Ui, state: &mut OculanteState, app: &mut App, gfx: &mu // ui.label("Channels"); - #[cfg(feature = "file_open")] if unframed_button(FOLDER, ui) .on_hover_text("Browse for image") .clicked() { - browse_for_image_path(state) + #[cfg(feature = "file_open")] + crate::browse_for_image_path(state); + #[cfg(not(feature = "file_open"))] + ui.ctx().memory_mut(|w| w.open_popup(Id::new("OPEN"))); + } + + #[cfg(not(feature = "file_open"))] + { + if ui.ctx().memory(|w| w.is_popup_open(Id::new("OPEN"))) { + filebrowser::browse_modal( + false, + SUPPORTED_EXTENSIONS, + |p| { + let _ = state.load_channel.0.clone().send(p.to_path_buf()); + ui.ctx().memory_mut(|w| w.close_popup()); + }, + ui.ctx(), + ); + } } let mut changed_channels = false; diff --git a/src/utils.rs b/src/utils.rs index 5e3c2c3..9d0dd24 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -190,6 +190,7 @@ pub struct Player { } impl Player { + /// Create a new Player pub fn new(image_sender: Sender, cache_size: usize, max_texture_size: u32) -> Player { let (stop_sender, _): (Sender<()>, Receiver<()>) = mpsc::channel(); Player { @@ -226,14 +227,23 @@ impl Player { } } - pub fn load(&mut self, img_location: &Path, message_sender: Sender) { + pub fn load_advanced( + &mut self, + img_location: &Path, + forced_frame_source: Option, + message_sender: Sender, + ) { debug!("Stopping player on load"); self.stop(); let (stop_sender, stop_receiver): (Sender<()>, Receiver<()>) = mpsc::channel(); self.stop_sender = stop_sender; if let Some(cached_image) = self.cache.get(img_location) { - _ = self.image_sender.send(Frame::new_still(cached_image)); + let mut frame = Frame::new_still(cached_image); + if let Some(fs) = forced_frame_source { + frame.source = fs; + } + _ = self.image_sender.send(frame); info!("Cache hit for {}", img_location.display()); return; } @@ -244,6 +254,7 @@ impl Player { message_sender, stop_receiver, self.max_texture_size, + forced_frame_source, ); if let Ok(meta) = std::fs::metadata(img_location) { @@ -253,6 +264,10 @@ impl Player { } } + pub fn load(&mut self, img_location: &Path, message_sender: Sender) { + self.load_advanced(img_location, None, message_sender); + } + pub fn stop(&self) { _ = self.stop_sender.send(()); } @@ -264,6 +279,7 @@ pub fn send_image_threaded( message_sender: Sender, stop_receiver: Receiver<()>, max_texture_size: u32, + forced_frame_source: Option, ) { let loc = img_location.to_owned(); @@ -278,7 +294,10 @@ pub fn send_image_threaded( // .send(Frame::new_reset(f.buffer.clone())); let mut first = true; - for f in frame_receiver.iter() { + for mut f in frame_receiver.iter() { + if let Some(ref fs) = forced_frame_source { + f.source = fs.clone(); + } if stop_receiver.try_recv().is_ok() { info!("Stopped from receiver."); return; @@ -304,6 +323,7 @@ pub fn send_image_threaded( ); let mut frame = f; + let op = ImageOperation::Resize { dimensions: new_dimensions, aspect: true, @@ -372,6 +392,7 @@ pub enum FrameSource { AnimationStart, Still, EditResult, + CompareResult, } /// A single frame @@ -795,9 +816,12 @@ pub fn compare_next(state: &mut OculanteState) { state.image_geometry = geo.clone(); state.is_loaded = false; state.current_image = None; - state.player.load(path, state.message_channel.0.clone()); + state.player.load_advanced( + path, + Some(FrameSource::CompareResult), + state.message_channel.0.clone(), + ); state.current_path = Some(path.clone()); - state.persistent_settings.keep_view = true; } } } diff --git a/tests/frstvisuals-lmV1g1UbdhQ-unsplash.jpg b/tests/frstvisuals-lmV1g1UbdhQ-unsplash.jpg index 8741370..8c610e8 100644 Binary files a/tests/frstvisuals-lmV1g1UbdhQ-unsplash.jpg and b/tests/frstvisuals-lmV1g1UbdhQ-unsplash.jpg differ diff --git a/tests/gray_8bpp.png b/tests/gray_8bpp.png index 4cbc694..360e4fc 100644 Binary files a/tests/gray_8bpp.png and b/tests/gray_8bpp.png differ diff --git a/tests/high.png b/tests/high.png index 9467c2a..b261b06 100644 Binary files a/tests/high.png and b/tests/high.png differ diff --git a/tests/johnny_automatic_lobster.svg b/tests/johnny_automatic_lobster.svg index dfd6917..44effcf 100644 --- a/tests/johnny_automatic_lobster.svg +++ b/tests/johnny_automatic_lobster.svg @@ -1,312 +1 @@ - - - - -]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -image/svg+xmlOpen Clip Art Librarylobster2006-10-11T19:01:07a stylized red lobsterhttp://openclipart.org/detail/513/lobster-by-johnny_automaticjohnny_automaticanimalanimalclip artclipartcontourcontourcrustaceancrustaceanfoodfoodimagelobsterlobstermediamonochromemonochromeoutlineoutlinepngpublic domainredredshellfishshellfishsvg + \ No newline at end of file diff --git a/tests/large.png b/tests/large.png index ec33748..cf23d00 100644 Binary files a/tests/large.png and b/tests/large.png differ diff --git a/tests/pngtest_16bit.png b/tests/pngtest_16bit.png index 3830893..93b8792 100644 Binary files a/tests/pngtest_16bit.png and b/tests/pngtest_16bit.png differ diff --git a/tests/rust.png b/tests/rust.png index 85d0003..5a73c90 100644 Binary files a/tests/rust.png and b/tests/rust.png differ diff --git a/tests/test.jpg b/tests/test.jpg index 86c7a08..76b4455 100644 Binary files a/tests/test.jpg and b/tests/test.jpg differ diff --git a/tests/test.png b/tests/test.png index da15d8b..9bcd22c 100644 Binary files a/tests/test.png and b/tests/test.png differ diff --git a/tests/unpremult.png b/tests/unpremult.png index 1a98a87..4f7b67c 100644 Binary files a/tests/unpremult.png and b/tests/unpremult.png differ diff --git a/tests/wide.png b/tests/wide.png index ca9cb2b..f9408b9 100644 Binary files a/tests/wide.png and b/tests/wide.png differ