Skip to content

Commit

Permalink
fix: Thumbails have correct aspect ratio and reload if loaded while b…
Browse files Browse the repository at this point in the history
…eing written
  • Loading branch information
woelper committed Jan 29, 2025
1 parent b6377ce commit 61cbce7
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 116 deletions.
37 changes: 37 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ dicom-pixeldata = { version = "0.8.0", features = ["image"] }
dicom-object = "0.8.0"
unicode-segmentation = "1.12.0"
font-kit = "0.14.2"
open = "5.3.2"

[features]
default = [
Expand Down
6 changes: 1 addition & 5 deletions src/appstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,7 @@ impl<'b> Default for OculanteState {
cursor: Default::default(),
cursor_relative: Default::default(),
sampled_color: [0., 0., 0., 0.],
player: Player::new(
tx_channel.0.clone(),
20,
msg_channel.0.clone(),
),
player: Player::new(tx_channel.0.clone(), 20, msg_channel.0.clone()),
texture_channel: tx_channel,
message_channel: msg_channel,
load_channel: mpsc::channel(),
Expand Down
4 changes: 1 addition & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ fn init(_app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins) -> OculanteSt
// Set up egui style / theme
plugins.egui(|ctx| {
// FIXME: Wait for https://github.com/Nazariglez/notan/issues/315 to close, then remove

let mut fonts = FontDefinitions::default();
egui_extras::install_image_loaders(ctx);

Expand Down Expand Up @@ -349,10 +349,8 @@ fn init(_app: &mut App, gfx: &mut Graphics, plugins: &mut Plugins) -> OculanteSt
.unwrap()
.insert(0, "inter".to_owned());


let fonts = load_system_fonts(fonts);


debug!("Theme {:?}", state.persistent_settings.theme);
apply_theme(&mut state, ctx);
ctx.set_fonts(fonts);
Expand Down
80 changes: 41 additions & 39 deletions src/thumbnails.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub const THUMB_CAPTION_HEIGHT: u32 = 24;
pub const MAX_THREADS: usize = 4;

use std::{
collections::HashSet,
fs::{create_dir_all, File},
hash::{DefaultHasher, Hash, Hasher},
path::{Path, PathBuf},
Expand All @@ -19,7 +20,7 @@ use crate::image_loader::open_image;
#[derive(Debug, Default, Clone)]
pub struct Thumbnails {
/// The known thumbnail ids. This is used to avoid re-generating known thumbnails
ids: Vec<PathBuf>,
ids: HashSet<PathBuf>,
/// Number of thumbnails being created at a given time
pool: Arc<Mutex<usize>>,
}
Expand Down Expand Up @@ -58,7 +59,7 @@ impl Thumbnails {
let num = *pool.lock().unwrap();
*pool.lock().unwrap() = num.saturating_sub(1);
});
self.ids.push(cached_path);
self.ids.insert(cached_path);
bail!("Thumbnail not yet present.");
}
Ok(cached_path)
Expand All @@ -74,7 +75,7 @@ pub fn path_to_id<P: AsRef<Path>>(path: P) -> PathBuf {
PathBuf::from(format!("{}_{size}", hasher.finish())).with_extension("png")
}

fn get_disk_cache_path() -> Result<PathBuf> {
pub fn get_disk_cache_path() -> Result<PathBuf> {
Ok(dirs::data_local_dir()
.ok_or(anyhow!("Can't get local dir"))?
.join("oculante")
Expand Down Expand Up @@ -104,41 +105,40 @@ pub fn generate<P: AsRef<Path>>(source_path: P) -> Result<()> {
}

pub fn from_existing<P: AsRef<Path>>(dest_path: P, image: &DynamicImage) -> Result<()> {
let (mut width, mut height) = image.dimensions();
let x = 0;
let mut y = 0;

if width < height {
height = (width as f32 * 1. / 1.3333) as u32;
y = (image.dimensions().0 as f32 - height as f32 / 2.) as u32;
}

if width > height {
width = (height as f32 * 1.3333) as u32;
}

if width == height {
height = (width as f32 * 1. / 1.3333) as u32;
}

let mut d = DynamicImage::ImageRgba8(image.crop_imm(x, y, width, height).to_rgba8());
debug!("\tDim: {:?}", d.dimensions());

// let op = ImageOperation::Resize {
// dimensions: (THUMB_SIZE[0], THUMB_SIZE[1]),
// aspect: false,
// filter: crate::image_editing::ScaleFilter::Bilinear,
// };
// op.process_image(&mut d)?;

d = d.resize(
THUMB_SIZE[0],
THUMB_SIZE[1],
imageops::FilterType::CatmullRom,
);
debug!("\tDim: {:?}", d.dimensions());
debug!("TMB=> Original image size: {:?}", image.dimensions());

let target_width = 120;
let target_height = 90;
let desired_aspect = target_width as f32 / target_height as f32;

let (orig_width, orig_height) = image.dimensions();
let orig_aspect = orig_width as f32 / orig_height as f32;

let (crop_width, crop_height) = if orig_aspect > desired_aspect {
// Image is too wide. Crop horizontally.
let crop_w = (desired_aspect * orig_height as f32).round() as u32;
(crop_w, orig_height)
} else {
// Image is too tall or just right width. Crop vertically.
let crop_h = (orig_width as f32 / desired_aspect).round() as u32;
(orig_width, crop_h)
};

let x_offset = (orig_width - crop_width) / 2;
let y_offset = (orig_height - crop_height) / 2;

let cropped_img =
imageops::crop_imm(image, x_offset, y_offset, crop_width, crop_height).to_image();

let mut d = DynamicImage::ImageRgba8(cropped_img);
let op = crate::image_editing::ImageOperation::Resize {
dimensions: (target_width, target_height),
aspect: false,
filter: crate::image_editing::ScaleFilter::Bilinear,
};
op.process_image(&mut d)?;
d.save(&dest_path)?;
debug!("\tSaved to {}.", dest_path.as_ref().display());

Ok(())
}

Expand All @@ -147,6 +147,8 @@ fn test_thumbs() {
std::env::set_var("RUST_LOG", "debug");
let _ = env_logger::try_init();
let mut thumbs = Thumbnails::default();
thumbs.get("tests/rust.png").unwrap();
std::thread::sleep(std::time::Duration::from_millis(3000));
_ = thumbs.get("tests/rust.png");
_ = thumbs.get("tests/ultrahigh.png");
_ = thumbs.get("tests/mohsen-karimi.webp");
std::thread::sleep(std::time::Duration::from_millis(1000));
}
2 changes: 1 addition & 1 deletion src/ui/edit_ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ pub fn edit_ui(app: &mut App, ctx: &Context, state: &mut OculanteState, gfx: &mu

state.image_geometry.dimensions = state.edit_state.result_pixel_op.dimensions();


});
}

Expand Down
61 changes: 53 additions & 8 deletions src/ui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ impl EguiExt for Ui {
let icon = icon.unwrap_or_default();

self.with_layout(egui::Layout::left_to_right(Align::Center), |ui| {
ui.add(
egui::Label::new(RichText::new(icon).color(ui.style().visuals.selection.bg_fill)),
);
ui.add(egui::Label::new(
RichText::new(icon).color(ui.style().visuals.selection.bg_fill),
));
ui.label(
RichText::new(description).color(ui.style().visuals.noninteractive().text_color()),
);
Expand Down Expand Up @@ -465,7 +465,6 @@ impl EguiExt for Ui {
}
}


fn parse_icon_plus_text(line: &str) -> (Option<String>, String) {
use unicode_segmentation::UnicodeSegmentation;

Expand Down Expand Up @@ -740,6 +739,37 @@ pub fn drag_area(ui: &mut Ui, state: &mut OculanteState, app: &mut App) {
}
}

pub fn paint_texture_load_result(
ui: &Ui,
tlr: &load::TextureLoadResult,
rect: Rect,
show_loading_spinner: Option<bool>,
options: &ImageOptions,
) {
match tlr {
Ok(load::TexturePoll::Ready { texture }) => {
paint_texture_at(ui.painter(), rect, options, texture);
}
Ok(load::TexturePoll::Pending { .. }) => {
let show_loading_spinner =
show_loading_spinner.unwrap_or(ui.visuals().image_loading_spinners);
if show_loading_spinner {
Spinner::new().paint_at(ui, rect);
}
}
Err(_) => {
let font_id = TextStyle::Body.resolve(ui.style());
ui.painter().text(
rect.center(),
Align2::CENTER_CENTER,
"⚠",
font_id,
ui.visuals().error_fg_color,
);
}
}
}

pub fn render_file_icon(icon_path: &Path, ui: &mut Ui, thumbnails: &mut Thumbnails) -> Response {
let scroll = false;

Expand Down Expand Up @@ -773,8 +803,24 @@ pub fn render_file_icon(icon_path: &Path, ui: &mut Ui, thumbnails: &mut Thumbnai
} else {
match thumbnails.get(icon_path) {
Ok(tp) => {
let image = egui::Image::new(format!("file://{}", tp.display())).rounding(rounding);
image.paint_at(ui, image_rect);
let image = egui::Image::new(format!("file://{}", tp.display()))
.rounding(rounding)
.show_loading_spinner(true);

let load_result = image.load_for_size(ui.ctx(), image_rect.size());

paint_texture_load_result(
ui,
&load_result,
image_rect,
None,
&ImageOptions::default(),
);
if load_result.is_err() {
// If an image could not be loaded, reload. This is usually because the image
// is being written while loading.
ui.ctx().forget_image(&format!("file://{}", tp.display()));
}
}
Err(_) => {
// warn!("{e}");
Expand Down Expand Up @@ -810,6 +856,7 @@ pub fn render_file_icon(icon_path: &Path, ui: &mut Ui, thumbnails: &mut Thumbnai

if response.hovered() {
// the generic hover effect, a rect over everything
// ui.ctx().forget_image(uri);
ui.painter()
.rect_filled(response.rect, rounding, Color32::from_white_alpha(5));

Expand Down Expand Up @@ -868,8 +915,6 @@ pub fn blank_icon(
) {
}



fn caret_icon(ui: &mut egui::Ui, openness: f32, response: &egui::Response) {
let galley = ui.ctx().fonts(|fonts| {
fonts.layout(
Expand Down
13 changes: 13 additions & 0 deletions src/ui/settings_ui.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use std::fs::remove_dir_all;

use super::*;
use crate::appstate::OculanteState;
use crate::thumbnails::get_disk_cache_path;
use crate::utils::*;
#[cfg(not(any(target_os = "netbsd", target_os = "freebsd")))]
use notan::egui::*;
Expand Down Expand Up @@ -288,6 +291,16 @@ pub fn settings_ui(app: &mut App, ctx: &Context, state: &mut OculanteState, _gfx
configuration_item_ui("Enable experimental features", "Turn on features that are not yet finished.", |ui| {
ui.styled_checkbox(&mut state.persistent_settings.experimental_features, "");
}, ui);

configuration_item_ui("Thumbnails", "Functionality to test thumbnail generation.", |ui| {
if ui.button("Delete thumbnails").clicked() {
_ = get_disk_cache_path().map(|p|remove_dir_all(p));
}
if ui.button("Open thumb dir").clicked() {
_ = get_disk_cache_path().map(|p|open::that(p));
}

}, ui);
});
});
});
Expand Down
Loading

0 comments on commit 61cbce7

Please sign in to comment.