From 581e4263fc05a197d81959d436f61a16792a499e Mon Sep 17 00:00:00 2001 From: Jean-Loup Beaussart Date: Mon, 14 Apr 2025 01:28:25 +0200 Subject: [PATCH] Implement fading --- src/image_provider.rs | 62 ++++++++++++++++++++++++++++++++++--------- src/main.rs | 38 ++++++++++++-------------- src/settings.rs | 2 +- 3 files changed, 68 insertions(+), 34 deletions(-) diff --git a/src/image_provider.rs b/src/image_provider.rs index 80fa016..413af42 100644 --- a/src/image_provider.rs +++ b/src/image_provider.rs @@ -1,28 +1,34 @@ use crate::dimensions::{calculate_resize_dimensions, Dimensions}; use image::ImageReader; -use macroquad::texture::Texture2D; +use macroquad::texture::{Image, Texture2D}; use std::path::{Path, PathBuf}; use walkdir::WalkDir; pub struct ImageProvider<'a> { path: &'a Path, directory_it: Box>>, + image: Option, texture: Option, + texture_darkening_factor: f32, screen_dimension: Dimensions, } impl<'a> ImageProvider<'a> { pub fn new(screen_dimension: Dimensions, path: &'a Path) -> Self { Self { - directory_it: Box::new(WalkDir::new(&path).max_open(8).into_iter()), + directory_it: Box::new(WalkDir::new(path).max_open(8).into_iter()), + image: None, texture: None, screen_dimension, path, + texture_darkening_factor: 1.0f32, } } pub fn load_next_image(&mut self) -> anyhow::Result<()> { let path = self.get_next_image_path()?; + println!("Loading image: {path:?}"); + let now = chrono::Local::now().time(); let image = ImageReader::open(path)?.decode()?; let image_dim = Dimensions::new(image.width(), image.height()); @@ -34,19 +40,51 @@ impl<'a> ImageProvider<'a> { image::imageops::FilterType::Lanczos3, ); - self.texture = Some(Texture2D::from_rgba8( - image.width().try_into()?, - image.height().try_into()?, - &image.into_rgba8(), - )); + let mut i = Image::empty(); + i.width = image.width() as u16; + i.height = image.height() as u16; + i.bytes = image.into_rgba8().into_raw(); + + self.image = Some(i); + self.texture = None; + + let elapsed = chrono::Local::now().time() - now; + println!( + "Loading image elapsed time: {}ms", + elapsed.num_milliseconds() + ); Ok(()) } - pub fn get_current_texture(&self) -> anyhow::Result<&Texture2D> { - self.texture - .as_ref() - .ok_or_else(|| anyhow::anyhow!("No image loaded")) + pub fn get_current_texture(&mut self, darkening_factor: f32) -> anyhow::Result<&Texture2D> { + if (darkening_factor - self.texture_darkening_factor).abs() > f32::EPSILON { + self.texture_darkening_factor = darkening_factor; + self.texture = None; + } + + if self.texture.is_none() { + let darken_image = self + .image + .as_ref() + .map(|i| { + let mut darken_image = i.clone(); + darken_image.bytes.iter_mut().for_each(|b| { + *b = (f32::from(*b) * darkening_factor) as u8; + }); + darken_image + }) + .ok_or_else(|| anyhow::anyhow!("No image loaded"))?; + + let text = + Texture2D::from_rgba8(darken_image.width, darken_image.height, &darken_image.bytes); + self.texture = Some(text); + } + + match self.texture { + Some(ref texture) => Ok(texture), + None => Err(anyhow::anyhow!("No texture available")), + } } fn get_next_image_path(&mut self) -> anyhow::Result { @@ -65,7 +103,7 @@ impl<'a> ImageProvider<'a> { 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()); + self.directory_it = Box::new(WalkDir::new(self.path).max_open(8).into_iter()); } } } diff --git a/src/main.rs b/src/main.rs index f276301..7068877 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,11 +31,6 @@ fn display_image_centered(texture: &Texture2D) { ); } -enum Mode { - Black, - Displayed, -} - #[macroquad::main(window_conf)] async fn main() -> anyhow::Result<()> { let settings = settings::Settings::new("settings.yml".into())?; @@ -51,37 +46,38 @@ async fn main() -> anyhow::Result<()> { } let now = chrono::Local::now().time(); + let start_fading = chrono::NaiveTime::from_hms_opt(22, 45, 0).unwrap(); let go_to_bed = chrono::NaiveTime::from_hms_opt(23, 0, 0).unwrap(); let wake_up = chrono::NaiveTime::from_hms_opt(9, 0, 0).unwrap(); + let full_brightness = chrono::NaiveTime::from_hms_opt(9, 15, 0).unwrap(); - let mode = if now < go_to_bed && now > wake_up { - Mode::Displayed + let darkening_factor = if now < start_fading && now > full_brightness { + 1.0f32 + } else if now > start_fading && now < go_to_bed { + (go_to_bed - now).num_seconds() as f32 / (go_to_bed - start_fading).num_seconds() as f32 + } else if now > wake_up && now < full_brightness { + (now - wake_up).num_seconds() as f32 / (full_brightness - wake_up).num_seconds() as f32 } else { - Mode::Black + 0.0f32 }; - match mode { - Mode::Black => { - clear_background(BLACK); - } - Mode::Displayed => { - display_frame(&provider)?; - if time.elapsed() > settings.duration { - provider.load_next_image()?; - time = Instant::now(); - } + display_frame(&mut provider, darkening_factor)?; + if time.elapsed() > settings.duration { + if darkening_factor > 0.0f32 { + provider.load_next_image()?; } + time = Instant::now(); } - thread::sleep(Duration::from_millis(250)); + thread::sleep(Duration::from_millis(400)); next_frame().await; } Ok(()) } -fn display_frame(provider: &ImageProvider) -> anyhow::Result<()> { - let image = provider.get_current_texture()?; +fn display_frame(provider: &mut ImageProvider, darkening_factor: f32) -> anyhow::Result<()> { + let image = provider.get_current_texture(darkening_factor)?; clear_background(BLACK); display_image_centered(image); diff --git a/src/settings.rs b/src/settings.rs index 00dd00c..719692d 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -11,7 +11,7 @@ impl Settings { .add_source(File::from(settings_file)) .build()?; - let photo_path = settings.get_string("photo_path")?.try_into()?; + let photo_path = settings.get_string("photo_path")?.into(); let duration = settings.get_int("duration")?; Ok(Self {