diff --git a/src/image_rendering.rs b/src/image_rendering.rs index 11d1582..e643244 100644 --- a/src/image_rendering.rs +++ b/src/image_rendering.rs @@ -46,6 +46,7 @@ pub struct ImageBuilder { scale: f32, font_size: f32, text: Option, + text_color: [u8; 3], image: Option, } @@ -59,6 +60,8 @@ impl Default for ImageBuilder { scale: 60.0, font_size: 16.0, text: None, + // black + text_color: [255, 255, 255], image: None, } } @@ -92,6 +95,12 @@ impl ImageBuilder { self } + #[allow(dead_code)] + pub fn set_text_color(mut self, text_color: [u8; 3]) -> Self { + self.text_color = text_color; + self + } + #[allow(dead_code)] pub fn set_image(mut self, image: DynamicImage) -> Self { self.image = Some(image); @@ -108,6 +117,7 @@ impl ImageBuilder { image: self.image.unwrap(), scale: self.scale, font_size: self.font_size, + text_color: self.text_color, text: self.text.unwrap(), }; return c.render(); @@ -116,6 +126,7 @@ impl ImageBuilder { height: self.height, width: self.width, font_size: self.font_size, + text_color: self.text_color, text, }; return c.render(); @@ -170,6 +181,7 @@ struct TextComponent { height: usize, width: usize, font_size: f32, + text_color: [u8; 3], text: String, } @@ -177,27 +189,10 @@ impl Component for TextComponent { fn render(&self) -> DynamicImage { let mut image = RgbImage::new(self.width as u32, self.height as u32); - let scale = Scale::uniform(self.font_size); - let font = &GLOBAL_FONT.get().unwrap(); + let font_scale = Scale::uniform(self.font_size); + let text = wrap_text(self.height as u32, font_scale, &self.text); - let v_metrics = font.v_metrics(scale); - let height = (v_metrics.ascent - v_metrics.descent + v_metrics.line_gap).round() as i32; - - // start at y = 10 - let mut y_pos = 10; - - for line in self.text.split("\n") { - draw_text_mut( - &mut image, - Rgb([255, 255, 255]), - 10, - y_pos, - scale, - &GLOBAL_FONT.get().unwrap(), - &line, - ); - y_pos += height; - } + draw_text_on_image(&text, &mut image, Rgb(self.text_color), font_scale); image::DynamicImage::ImageRgb8(image) } @@ -210,6 +205,7 @@ struct ImageTextComponent { image: DynamicImage, scale: f32, font_size: f32, + text_color: [u8; 3], text: String, } @@ -224,19 +220,9 @@ impl Component for ImageTextComponent { let mut base_image = RgbImage::new(self.height as u32, self.width as u32); - let font = &GLOBAL_FONT.get().unwrap(); let font_scale = Scale::uniform(self.font_size); - - // TODO: allow new line - draw_text_mut( - &mut base_image, - Rgb([255, 255, 255]), - 0, - 0, - font_scale, - font, - &self.text, - ); + let text = wrap_text(self.height as u32, font_scale, &self.text); + draw_text_on_image(&text, &mut base_image, Rgb(self.text_color), font_scale); // position at the middle let free_space = self.width - image.width() as usize; // TODO: allow padding to be manually set @@ -250,3 +236,37 @@ impl Component for ImageTextComponent { image::DynamicImage::ImageRgb8(base_image) } } + +fn draw_text_on_image(text: &String, image: &mut RgbImage, color: Rgb, font_scale: Scale) { + let font = &GLOBAL_FONT.get().unwrap(); + let v_metrics = font.v_metrics(font_scale); + + let line_height = (v_metrics.ascent - v_metrics.descent + v_metrics.line_gap).round() as i32; + let mut y_pos = 0; + + for line in text.split('\n') { + draw_text_mut(image, color, 0, y_pos, font_scale, font, &line); + y_pos += line_height + } +} + +/// This functions adds '\n' to the line endings. It does not wrap +/// words but characters. +pub fn wrap_text(max_width: u32, font_size: Scale, text: &String) -> String { + let font = &GLOBAL_FONT.get().unwrap(); + + let mut new_text: Vec = Vec::new(); + let mut line_size = 0.0; + + for character in text.chars() { + let h_size = font.glyph(character).scaled(font_size).h_metrics(); + let complete_width = h_size.advance_width + h_size.left_side_bearing; + if (line_size + complete_width) as u32 > max_width { + new_text.push('\n'); + line_size = 0.0; + } + new_text.push(character); + line_size += h_size.advance_width + h_size.left_side_bearing; + } + String::from_iter(new_text) +} diff --git a/src/modules.rs b/src/modules.rs index 938a0cf..91b83f7 100644 --- a/src/modules.rs +++ b/src/modules.rs @@ -11,7 +11,7 @@ use self::space::Space; // other things use crate::config::{Button, ButtonConfigError}; use crate::device::ImageCache; -use crate::image_rendering::load_image; +use crate::image_rendering::{load_image, ImageBuilder}; use ::image::imageops::FilterType; use ::image::DynamicImage; use async_trait::async_trait; @@ -91,13 +91,13 @@ pub async fn start_module( button.index, device.kind().key_image_format().size, ); - let da = DeviceAccess::new(device, button.index).await; + let da = DeviceAccess::new(device.clone(), button.index).await; // init // // This function should be called after the config was checked, // otherwise it will panic and the module wont be started. - let mut module = match module_init_function(button, mc).await { + let mut module = match module_init_function(button.clone(), mc).await { Ok(m) => m, Err(e) => panic!("{}", e), }; @@ -105,7 +105,19 @@ pub async fn start_module( // then run module match module.run(da, br).await { Ok(_) => debug!("RETURNED"), - Err(e) => error!("RETURNED_ERROR: {}", e), + // TODO: maybe find calculation for font size + // print error on display + Err(e) => { + error!("{e}"); + let da = DeviceAccess::new(device, button.index).await; + let res = da.resolution(); + let image = ImageBuilder::new(res.0, res.1) + .set_text(format!("E: {}", e)) + .set_font_size(12.0) + .set_text_color([255, 0, 0]) + .build(); + da.write_img(image).await.unwrap(); + } } } @@ -236,8 +248,6 @@ pub trait Module: Sync + Send { Self: Sized; /// Function for actually running the module and interacting with the device. Errors that /// happen here should be mostly prevented. - /// - /// TODO: The return error is not sent anywhere and is just a panic async fn run( &mut self, device: DeviceAccess, diff --git a/src/modules/counter.rs b/src/modules/counter.rs index 1ac214b..730b4c9 100644 --- a/src/modules/counter.rs +++ b/src/modules/counter.rs @@ -7,6 +7,7 @@ use super::{ ReturnError, }; +use crate::image_rendering::wrap_text; use crate::GLOBAL_FONT; use image::{DynamicImage, Rgb, RgbImage}; use imageproc::drawing::draw_text_mut; @@ -96,18 +97,19 @@ fn render_text( let scale = Scale::uniform(title_size); let font = &GLOBAL_FONT.get().unwrap(); - let v_metrics = font.v_metrics(scale); let height = (v_metrics.ascent - v_metrics.descent + v_metrics.line_gap).round() as i32; - // start at y = 10 - let mut y_pos = 10; + let text = wrap_text(image.width(), scale, &title); - for line in title.split("\n") { + // start at y = 0 + let mut y_pos = 0; + + for line in text.split("\n") { draw_text_mut( &mut image, Rgb([255, 255, 255]), - 10, + 0, y_pos, Scale::uniform(title_size), &GLOBAL_FONT.get().unwrap(), @@ -119,7 +121,7 @@ fn render_text( draw_text_mut( &mut image, Rgb([255, 255, 255]), - 10, + 0, y_pos, Scale::uniform(number_size), &GLOBAL_FONT.get().unwrap(),