diff --git a/Cargo.lock b/Cargo.lock index b38f99f1..847b85ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -503,6 +503,7 @@ dependencies = [ "embedded-graphics-simulator", "enum-map", "evdev", + "fast_image_resize", "fluent-templates", "framebuffer", "image 0.23.14", @@ -771,6 +772,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" +[[package]] +name = "fast_image_resize" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc789a40040e11bbe4ba31ca319406805a12fe3f8d71314bbc4bd076602ad55a" +dependencies = [ + "num-traits", + "thiserror", +] + [[package]] name = "fdeflate" version = "0.3.0" diff --git a/common/Cargo.toml b/common/Cargo.toml index a13130e2..d068a704 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -37,6 +37,7 @@ embedded-graphics-simulator = { version = "0.5.0", optional = true } sdl2 = { version = "0.35.2", optional = true } sysfs_gpio = { version = "0.6.1", optional = true } wait-timeout = "0.2.0" +fast_image_resize = "2.7.3" [target.'cfg(target_arch = "arm")'.dependencies] evdev = { version = "0.12.1", features = ["tokio"], optional = true } diff --git a/common/src/view/image.rs b/common/src/view/image.rs index 857cb96d..f56e8b72 100644 --- a/common/src/view/image.rs +++ b/common/src/view/image.rs @@ -1,4 +1,5 @@ use std::collections::VecDeque; +use std::num::NonZeroU32; use std::path::{Path, PathBuf}; use anyhow::Result; @@ -141,20 +142,65 @@ impl View for Image { } fn image(path: &Path, rect: Rect, mode: ImageMode, border_radius: u32) -> Option { - let mut image = ::image::open(path) + let image = ::image::open(path) .map_err(|e| error!("Failed to load image at {}: {}", path.display(), e)) .ok()?; - match mode { - ImageMode::Raw => {} + let mut image = match mode { + ImageMode::Raw => image.to_rgba8(), ImageMode::Cover => { - image = image.resize_to_fill(rect.w, rect.h, image::imageops::FilterType::Nearest); + if image.width() == rect.w && image.height() == rect.h { + image.to_rgba8() + } else { + let src_image = fast_image_resize::Image::from_vec_u8( + NonZeroU32::new(image.width())?, + NonZeroU32::new(image.height())?, + image.to_rgba8().into_raw(), + fast_image_resize::PixelType::U8x3, + ) + .map_err(|e| error!("Failed to load image at {}: {}", path.display(), e)) + .ok()?; + let mut dst_image = fast_image_resize::Image::new( + NonZeroU32::new(rect.w)?, + NonZeroU32::new(rect.h)?, + src_image.pixel_type(), + ); + let mut resizer = + fast_image_resize::Resizer::new(fast_image_resize::ResizeAlg::Nearest); + resizer + .resize(&src_image.view(), &mut dst_image.view_mut()) + .ok()?; + RgbaImage::from_raw(rect.w, rect.h, dst_image.into_vec())? + } } ImageMode::Contain => { - let new_height = rect.h.min(rect.w * image.height() / image.width()); - image = image.resize_to_fill(rect.w, new_height, image::imageops::FilterType::Nearest); + println!("contain!: {:?}", rect); + if image.width() == rect.w && image.height() == rect.h { + image.to_rgba8() + } else { + let new_height = rect.h.min(rect.w * image.height() / image.width()); + let src_image = fast_image_resize::Image::from_vec_u8( + NonZeroU32::new(image.width())?, + NonZeroU32::new(image.height())?, + image.to_rgba8().into_raw(), + fast_image_resize::PixelType::U8x4, + ) + .map_err(|e| error!("Failed to load image at {}: {}", path.display(), e)) + .ok()?; + let mut dst_image = fast_image_resize::Image::new( + NonZeroU32::new(rect.w)?, + NonZeroU32::new(new_height)?, + src_image.pixel_type(), + ); + let mut resizer = + fast_image_resize::Resizer::new(fast_image_resize::ResizeAlg::Nearest); + resizer + .resize(&src_image.view(), &mut dst_image.view_mut()) + .ok()?; + RgbaImage::from_raw(rect.w, new_height, dst_image.into_vec())? + } } - } - let mut image = image.to_rgba8(); + }; + println!("image: {:?}", image.dimensions()); if border_radius != 0 { round(&mut image, border_radius); }