use crate::locate_io::overlay::display_size;
use slint::{ComponentHandle, Image, Rgba8Pixel, SharedPixelBuffer};
use std::path::{Path, PathBuf};
use tracing::warn;
pub(super) struct Decoded {
pub buffer: SharedPixelBuffer<Rgba8Pixel>,
pub full_size: [u32; 2],
}
pub(super) struct Photo {
pub path: PathBuf,
pub image: Image,
pub size: [u32; 2],
}
impl Photo {
pub(super) fn new(path: PathBuf, decoded: Decoded) -> Self {
Self {
path,
image: Image::from_rgba8(decoded.buffer),
size: decoded.full_size,
}
}
pub(super) fn file_name(&self) -> String {
file_name(&self.path)
}
}
pub(super) fn file_name(path: &Path) -> String {
path.file_name().map_or_else(
|| path.display().to_string(),
|name| name.to_string_lossy().into_owned(),
)
}
fn decode(path: &Path) -> Result<Decoded, String> {
let name = file_name(path);
let reader = image::ImageReader::open(path)
.map_err(|error| format!("Could not open {name}: {error}"))?
.with_guessed_format()
.map_err(|error| format!("Could not read {name}: {error}"))?;
let decoded = reader.decode().map_err(|error| {
format!("{name} is not an image the program reads (PNG or JPEG): {error}")
})?;
let full_size = [decoded.width(), decoded.height()];
let [width, height] = display_size(full_size);
let rgba = if [width, height] == full_size {
decoded.to_rgba8()
} else {
decoded
.resize_exact(width, height, image::imageops::FilterType::Triangle)
.to_rgba8()
};
let mut buffer = SharedPixelBuffer::<Rgba8Pixel>::new(width, height);
buffer
.make_mut_slice()
.copy_from_slice(bytemuck::cast_slice(rgba.as_raw()));
Ok(Decoded { buffer, full_size })
}
pub(super) fn spawn_decode<W: ComponentHandle + 'static>(
weak: slint::Weak<W>,
path: PathBuf,
done: impl FnOnce(PathBuf, Result<Decoded, String>) + Send + 'static,
) {
let spawned = std::thread::Builder::new()
.name("locate-photo".to_string())
.spawn(move || {
let result = decode(&path);
let _ = weak.upgrade_in_event_loop(move |_window| done(path, result));
});
if let Err(error) = spawned {
warn!("Locate: could not start the photo thread: {error}");
}
}