Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<C: PixelColor> SimulatorDisplay<C> {
/// [`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
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/output_image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ where
)
.unwrap();

if output_settings.scale == 1 {
if output_settings.scale == Size::new(1, 1) && output_settings.pixel_spacing == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch.

display
.bounding_box()
.points()
Expand All @@ -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();
Expand Down
36 changes: 27 additions & 9 deletions src/output_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
)
}
}

Expand All @@ -34,7 +36,7 @@ impl Default for OutputSettings {
/// Output settings builder.
#[derive(Default)]
pub struct OutputSettingsBuilder {
scale: Option<u32>,
scale: Option<Size>,
pixel_spacing: Option<u32>,
theme: BinaryColorTheme,
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
}
Expand Down
Loading