diff --git a/CHANGELOG.md b/CHANGELOG.md index bf2824d..68a0102 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ ## [Unreleased] - ReleaseDate +### Added + +- **(breaking)** [#71](https://github.com/embedded-graphics/simulator/pull/71) Added support for non-square pixels. + +### Fixed + +- [#71](https://github.com/embedded-graphics/simulator/pull/71) Fixed pixel spacing for unscaled outputs. + ## [0.8.0] - 2025-10-10 ### Added diff --git a/src/display.rs b/src/display.rs index 7f17bfc..402e800 100644 --- a/src/display.rs +++ b/src/display.rs @@ -102,7 +102,7 @@ impl SimulatorDisplay { /// [`pixel_spacing`](OutputSettings::pixel_spacing) settings to determine /// the size of this display in output pixels. pub fn output_size(&self, output_settings: &OutputSettings) -> Size { - self.size * output_settings.scale + self.size.component_mul(output_settings.scale) + self.size.saturating_sub(Size::new_equal(1)) * output_settings.pixel_spacing } } diff --git a/src/output_image.rs b/src/output_image.rs index 033381c..6d36188 100644 --- a/src/output_image.rs +++ b/src/output_image.rs @@ -64,7 +64,7 @@ where ) .unwrap(); - if output_settings.scale == 1 { + if output_settings.scale == Size::new(1, 1) && output_settings.pixel_spacing == 0 { display .bounding_box() .points() @@ -78,16 +78,16 @@ where .draw(self) .unwrap(); } else { - let pixel_pitch = (output_settings.scale + output_settings.pixel_spacing) as i32; - let pixel_size = Size::new(output_settings.scale, output_settings.scale); - for p in display.bounding_box().points() { let raw_color = display.get_pixel(p).into(); let themed_color = output_settings.theme.convert(raw_color); let output_color = C::from(themed_color); self.fill_solid( - &Rectangle::new(p * pixel_pitch + position, pixel_size), + &Rectangle::new( + p.component_mul(output_settings.pixel_pitch()) + position, + output_settings.scale, + ), output_color, ) .unwrap(); diff --git a/src/output_settings.rs b/src/output_settings.rs index c8766cf..f8a4232 100644 --- a/src/output_settings.rs +++ b/src/output_settings.rs @@ -4,8 +4,8 @@ use embedded_graphics::prelude::*; /// Output settings. #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct OutputSettings { - /// Pixel scale. - pub scale: u32, + /// Pixel scale, allowing for non-square pixels. + pub scale: Size, /// Spacing between pixels. pub pixel_spacing: u32, /// Binary color theme. @@ -16,12 +16,14 @@ pub struct OutputSettings { impl OutputSettings { /// Translates a output coordinate to the corresponding display coordinate. pub(crate) const fn output_to_display(&self, output_point: Point) -> Point { - let pitch = self.pixel_pitch() as i32; - Point::new(output_point.x / pitch, output_point.y / pitch) + output_point.component_div(self.pixel_pitch()) } - pub(crate) const fn pixel_pitch(&self) -> u32 { - self.scale + self.pixel_spacing + pub(crate) const fn pixel_pitch(&self) -> Point { + Point::new( + (self.scale.width + self.pixel_spacing) as i32, + (self.scale.height + self.pixel_spacing) as i32, + ) } } @@ -34,7 +36,7 @@ impl Default for OutputSettings { /// Output settings builder. #[derive(Default)] pub struct OutputSettingsBuilder { - scale: Option, + scale: Option, pixel_spacing: Option, theme: BinaryColorTheme, } @@ -55,6 +57,22 @@ impl OutputSettingsBuilder { pub fn scale(mut self, scale: u32) -> Self { assert!(scale > 0, "scale must be > 0"); + self.scale = Some(Size::new(scale, scale)); + + self + } + + /// Sets a non-square pixel scale. + /// + /// This is useful for simulating a display with a non-square pixel aspect ratio. + /// + /// # Panics + /// + /// Panics if `width` or `height` is `0`. + pub fn scale_non_square(mut self, scale: Size) -> Self { + assert!(scale.width > 0, "width must be > 0"); + assert!(scale.height > 0, "height must be > 0"); + self.scale = Some(scale); self @@ -77,7 +95,7 @@ impl OutputSettingsBuilder { pub fn theme(mut self, theme: BinaryColorTheme) -> Self { self.theme = theme; - self.scale.get_or_insert(3); + self.scale.get_or_insert(Size::new_equal(3)); self.pixel_spacing.get_or_insert(1); self @@ -97,7 +115,7 @@ impl OutputSettingsBuilder { /// Builds the output settings. pub fn build(self) -> OutputSettings { OutputSettings { - scale: self.scale.unwrap_or(1), + scale: self.scale.unwrap_or(Size::new_equal(1)), pixel_spacing: self.pixel_spacing.unwrap_or(0), theme: self.theme, }