Compare commits

...

2 Commits

Author SHA1 Message Date
Jean-Loup Beaussart
cc0038c1cd
feat: enable lints and use the image provider module 2024-11-14 23:00:10 +01:00
Jean-Loup Beaussart
f0ba4e47d9
feat: add image provider module 2024-11-14 22:59:06 +01:00
4 changed files with 146 additions and 29 deletions

View File

@ -3,6 +3,46 @@ name = "photo-frame"
version = "0.1.0"
edition = "2021"
[lints.rust]
unsafe_code = "forbid"
[lints.clippy]
all = "deny"
nursery = "deny"
pedantic = "deny"
complexity = "deny"
perf = "deny"
style = "deny"
suspicious = "deny"
unwrap_in_result = { level = "deny", priority = 1}
unwrap_used = { level = "deny", priority = 1}
expect_used = { level = "deny", priority = 1}
verbose_file_reads = { level = "deny", priority = 1}
unneeded_field_pattern = { level = "deny", priority = 1}
tests_outside_test_module = { level = "deny", priority = 1}
shadow_same = { level = "deny", priority = 1}
same_name_method = { level = "deny", priority = 1}
rest_pat_in_fully_bound_structs = { level = "deny", priority = 1}
redundant_type_annotations = { level = "deny", priority = 1}
panic = { level = "deny", priority = 1}
# Allow some annoying lints
module_name_repetitions = {level = "allow", priority = 1}
missing_errors_doc = {level = "allow", priority = 1}
needless_pass_by_value = {level = "allow", priority = 1}
or_fun_call = {level = "allow", priority = 1}
implicit_hasher = {level = "allow", priority = 1}
significant_drop_tightening = {level = "allow", priority = 1}
cast_possible_truncation = {level = "allow", priority = 1}
cast_sign_loss = {level = "allow", priority = 1}
cast_precision_loss = {level = "allow", priority = 1}
future_not_send = {level = "allow", priority = 1}
[profile.release]
strip = true
lto = "thin"
[dependencies]
anyhow = "1.0.93"
image = "0.25.4"
macroquad = { version = "0.4.13" }
walkdir = "2.5.0"

View File

@ -5,11 +5,11 @@ pub struct Dimensions {
}
impl Dimensions {
pub fn new(width: u32, height: u32) -> Self {
pub const fn new(width: u32, height: u32) -> Self {
Self { width, height }
}
pub fn aspect_ratio(&self) -> f32 {
pub const fn aspect_ratio(self) -> f32 {
self.width as f32 / self.height as f32
}
}

73
src/image_provider.rs Normal file
View File

@ -0,0 +1,73 @@
use crate::dimensions::{calculate_resize_dimensions, Dimensions};
use image::ImageReader;
use macroquad::texture::Texture2D;
use std::path::PathBuf;
use walkdir::WalkDir;
pub struct ImageProvider {
path: PathBuf,
directory_it: Box<dyn Iterator<Item = Result<walkdir::DirEntry, walkdir::Error>>>,
texture: Option<Texture2D>,
screen_dimension: Dimensions,
}
impl ImageProvider {
pub fn new(screen_dimension: Dimensions, path: PathBuf) -> Self {
Self {
directory_it: Box::new(WalkDir::new(&path).max_open(8).into_iter()),
texture: None,
screen_dimension,
path,
}
}
pub fn load_next_image(&mut self) -> anyhow::Result<()> {
let path = self.get_next_image_path()?;
let image = ImageReader::open(path)?.decode()?;
let image_dim = Dimensions::new(image.width(), image.height());
let new_dim = calculate_resize_dimensions(self.screen_dimension, image_dim);
let image = image.resize(
new_dim.width,
new_dim.height,
image::imageops::FilterType::Lanczos3,
);
self.texture = Some(Texture2D::from_rgba8(
image.width().try_into()?,
image.height().try_into()?,
&image.into_rgba8(),
));
Ok(())
}
pub fn get_current_texture(&self) -> anyhow::Result<&Texture2D> {
self.texture
.as_ref()
.ok_or_else(|| anyhow::anyhow!("No image loaded"))
}
fn get_next_image_path(&mut self) -> anyhow::Result<PathBuf> {
loop {
match self.directory_it.next() {
Some(Ok(entry)) => {
if entry.file_type().is_file()
&& entry
.path()
.extension()
.is_some_and(|ext| ext == "jpg" || ext == "jpeg" || ext == "png")
{
return Ok(entry.path().to_path_buf());
}
}
Some(Err(e)) => return Err(e.into()),
None => {
// Reset the iterator when we reach the end
self.directory_it = Box::new(WalkDir::new(&self.path).max_open(8).into_iter());
}
}
}
}
}

View File

@ -1,8 +1,13 @@
use dimensions::{calculate_resize_dimensions, Dimensions};
use image::ImageReader;
use dimensions::Dimensions;
use image_provider::ImageProvider;
use macroquad::prelude::*;
use std::{
thread,
time::{Duration, Instant},
};
mod dimensions;
mod image_provider;
fn window_conf() -> Conf {
Conf {
@ -14,11 +19,11 @@ fn window_conf() -> Conf {
}
}
fn display_image(texture: &Texture2D) {
fn display_image_centered(texture: &Texture2D) {
let screen_width = screen_width();
let screen_height = screen_height();
draw_texture(
&texture,
texture,
clamp(screen_width - texture.width(), 0.0f32, screen_width) / 2.0f32,
clamp(screen_height - texture.height(), 0.0f32, screen_height) / 2.0f32,
WHITE,
@ -26,36 +31,35 @@ fn display_image(texture: &Texture2D) {
}
#[macroquad::main(window_conf)]
async fn main() {
async fn main() -> anyhow::Result<()> {
show_mouse(false);
let image = ImageReader::open("examples/test.jpg")
.unwrap()
.decode()
.unwrap();
let screen_dim = Dimensions::new(screen_width() as u32, screen_height() as u32);
let image_dim = Dimensions::new(image.width(), image.height());
let new_dim = calculate_resize_dimensions(screen_dim, image_dim);
let image = image.resize(
new_dim.width,
new_dim.height,
image::imageops::FilterType::Lanczos3,
);
let texture = Texture2D::from_rgba8(
image.width().try_into().unwrap(),
image.height().try_into().unwrap(),
&image.into_rgba8().to_vec(),
);
let mut provider = ImageProvider::new(screen_dim, "/mnt/c/Users/beaus/Downloads".into());
provider.load_next_image()?;
let mut time = Instant::now();
loop {
clear_background(BLACK);
if is_quit_requested() || is_key_released(KeyCode::Escape) {
break;
}
display_image(&texture);
next_frame().await
display_frame(&provider)?;
if time.elapsed().as_secs() > 5 {
provider.load_next_image()?;
time = Instant::now();
}
thread::sleep(Duration::from_millis(250));
next_frame().await;
}
Ok(())
}
fn display_frame(provider: &ImageProvider) -> anyhow::Result<()> {
let image = provider.get_current_texture()?;
clear_background(BLACK);
display_image_centered(image);
Ok(())
}