From 260ae6a1a031e8b8a96003bd0abb0be04be3361a Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sat, 5 Jul 2025 14:30:21 +0200 Subject: [PATCH] add Window --- examples/brightness_tester.rs | 2 +- examples/fewer_copies.rs | 2 +- examples/moving_line.rs | 2 +- examples/wiping_clear.rs | 2 +- src/commands/bitmap.rs | 4 +- src/containers/bitmap.rs | 48 +++++---- src/containers/brightness_grid.rs | 4 +- src/containers/char_grid.rs | 2 +- src/containers/cp437_grid.rs | 2 +- src/containers/grid.rs | 71 +++++++------ src/containers/grid_view.rs | 128 ------------------------ src/containers/mod.rs | 6 +- src/containers/value_grid.rs | 68 +++++++------ src/containers/window.rs | 160 ++++++++++++++++++++++++++++++ 14 files changed, 277 insertions(+), 224 deletions(-) delete mode 100644 src/containers/grid_view.rs create mode 100644 src/containers/window.rs diff --git a/examples/brightness_tester.rs b/examples/brightness_tester.rs index 825dd34..353ed3c 100644 --- a/examples/brightness_tester.rs +++ b/examples/brightness_tester.rs @@ -3,7 +3,7 @@ use clap::Parser; use servicepoint::{ Bitmap, BitmapCommand, Brightness, BrightnessGrid, BrightnessGridCommand, - DataRef, Grid, UdpSocketExt, TILE_HEIGHT, TILE_WIDTH, + DataRef, UdpSocketExt, TILE_HEIGHT, TILE_WIDTH, }; use std::net::UdpSocket; diff --git a/examples/fewer_copies.rs b/examples/fewer_copies.rs index 6e1e848..396f143 100644 --- a/examples/fewer_copies.rs +++ b/examples/fewer_copies.rs @@ -2,7 +2,7 @@ use clap::Parser; use servicepoint::{ - Bitmap, BitmapCommand, CompressionCode, Grid, Origin, Packet, UdpSocketExt, + Bitmap, BitmapCommand, CompressionCode, Origin, Packet, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, }; use std::{net::UdpSocket, thread, time::Duration}; diff --git a/examples/moving_line.rs b/examples/moving_line.rs index 05fa8e5..805dc71 100644 --- a/examples/moving_line.rs +++ b/examples/moving_line.rs @@ -2,7 +2,7 @@ use clap::Parser; use servicepoint::{ - Bitmap, BitmapCommand, CompressionCode, Grid, UdpSocketExt, FRAME_PACING, + Bitmap, BitmapCommand, CompressionCode, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, }; use std::{net::UdpSocket, thread}; diff --git a/examples/wiping_clear.rs b/examples/wiping_clear.rs index 97579a3..cb878dd 100644 --- a/examples/wiping_clear.rs +++ b/examples/wiping_clear.rs @@ -2,7 +2,7 @@ use clap::Parser; use servicepoint::{ - Bitmap, BitmapCommand, Grid, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT, + Bitmap, BitmapCommand, UdpSocketExt, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH, }; use std::{net::UdpSocket, thread, time::Duration}; diff --git a/src/commands/bitmap.rs b/src/commands/bitmap.rs index 4e88705..f18fa22 100644 --- a/src/commands/bitmap.rs +++ b/src/commands/bitmap.rs @@ -2,7 +2,7 @@ use crate::{ command_code::{CommandCode, InvalidCommandCodeError}, commands::errors::{TryFromPacketError, TryIntoPacketError}, compression::{compress, decompress, CompressionError}, - Bitmap, CompressionCode, DataRef, Grid, Header, Origin, Packet, Pixels, + Bitmap, CompressionCode, DataRef, Header, Origin, Packet, Pixels, TypedCommand, TILE_SIZE, }; @@ -304,7 +304,7 @@ mod tests { crate::commands::tests::round_trip( BitmapCommand { origin: Origin::ZERO, - bitmap: Bitmap::max_sized(), + bitmap: Bitmap::new(8, 2).unwrap(), compression: *compression, } .into(), diff --git a/src/containers/bitmap.rs b/src/containers/bitmap.rs index 26de804..2655944 100644 --- a/src/containers/bitmap.rs +++ b/src/containers/bitmap.rs @@ -1,6 +1,6 @@ use crate::{ - DataRef, DisplayBitVec, Grid, Payload, ValueGrid, WindowMut, PIXEL_HEIGHT, - PIXEL_WIDTH, + DataRef, DisplayBitVec, Grid, GridMut, Payload, ValueGrid, Window, + PIXEL_HEIGHT, PIXEL_WIDTH, }; use ::bitvec::{order::Msb0, prelude::BitSlice, slice::IterMut}; use inherent::inherent; @@ -180,6 +180,26 @@ impl Bitmap { #[inherent] impl Grid for Bitmap { + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn get(&self, x: usize, y: usize) -> bool { + self.assert_in_bounds(x, y); + self.bit_vec[x + y * self.width] + } + + #[must_use] + pub fn width(&self) -> usize { + self.width + } + + #[must_use] + pub fn height(&self) -> usize { + self.height + } +} + +#[inherent] +impl GridMut for Bitmap { /// Sets the value of the specified position in the [Bitmap]. /// /// # Arguments @@ -193,17 +213,11 @@ impl Grid for Bitmap { /// /// When accessing `x` or `y` out of bounds. #[allow(unused, reason = "False positive because of #[inherent]")] - fn set(&mut self, x: usize, y: usize, value: bool) { + pub fn set(&mut self, x: usize, y: usize, value: bool) { self.assert_in_bounds(x, y); self.bit_vec.set(x + y * self.width, value); } - #[allow(unused, reason = "False positive because of #[inherent]")] - fn get(&self, x: usize, y: usize) -> bool { - self.assert_in_bounds(x, y); - self.bit_vec[x + y * self.width] - } - /// Sets the state of all pixels in the [Bitmap]. /// /// # Arguments @@ -211,17 +225,9 @@ impl Grid for Bitmap { /// - `this`: instance to write to /// - `value`: the value to set all pixels to #[allow(unused, reason = "False positive because of #[inherent]")] - fn fill(&mut self, value: bool) { + pub fn fill(&mut self, value: bool) { self.bit_vec.fill(value); } - - fn width(&self) -> usize { - self.width - } - - fn height(&self) -> usize { - self.height - } } impl DataRef for Bitmap { @@ -263,10 +269,10 @@ impl TryFrom<&ValueGrid> for Bitmap { } } -impl> TryFrom<&WindowMut<'_, bool, G>> for Bitmap { +impl> TryFrom<&Window<'_, bool, G>> for Bitmap { type Error = (); - fn try_from(value: &WindowMut<'_, bool, G>) -> Result { + fn try_from(value: &Window<'_, bool, G>) -> Result { let mut result = Self::new(value.width(), value.height()).ok_or(())?; for y in 0..value.height() { for x in 0..value.width() { @@ -380,7 +386,7 @@ mod tests { #[should_panic] fn out_of_bounds_x() { let vec = Bitmap::new(8, 2).unwrap(); - vec.get(8, 1); + _ = vec.get(8, 1); } #[test] diff --git a/src/containers/brightness_grid.rs b/src/containers/brightness_grid.rs index 563f3cc..4d741dd 100644 --- a/src/containers/brightness_grid.rs +++ b/src/containers/brightness_grid.rs @@ -1,4 +1,4 @@ -use crate::{Brightness, ByteGrid, Grid, ValueGrid}; +use crate::{Brightness, ByteGrid, ValueGrid}; /// A grid containing brightness values. /// @@ -93,7 +93,7 @@ impl TryFrom for BrightnessGrid { #[cfg(test)] mod tests { - use crate::{Brightness, BrightnessGrid, DataRef, Grid, ValueGrid}; + use crate::{Brightness, BrightnessGrid, DataRef, ValueGrid}; #[test] fn to_u8_grid() { diff --git a/src/containers/char_grid.rs b/src/containers/char_grid.rs index 10eeb2f..12c9de5 100644 --- a/src/containers/char_grid.rs +++ b/src/containers/char_grid.rs @@ -1,4 +1,4 @@ -use crate::{Grid, SetValueSeriesError, TryLoadValueGridError, ValueGrid}; +use crate::{SetValueSeriesError, TryLoadValueGridError, ValueGrid}; use std::string::FromUtf8Error; /// A grid containing UTF-8 characters. diff --git a/src/containers/cp437_grid.rs b/src/containers/cp437_grid.rs index b74eba7..bb7daa5 100644 --- a/src/containers/cp437_grid.rs +++ b/src/containers/cp437_grid.rs @@ -1,4 +1,4 @@ -use crate::{Grid, ValueGrid}; +use crate::ValueGrid; /// A grid containing codepage 437 characters. /// diff --git a/src/containers/grid.rs b/src/containers/grid.rs index e81e048..c3531b0 100644 --- a/src/containers/grid.rs +++ b/src/containers/grid.rs @@ -1,16 +1,5 @@ -/// A two-dimensional grid of `T` +/// A two-dimensional readonly grid of `T` pub trait Grid { - /// Sets the value at the specified position - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. - fn set(&mut self, x: usize, y: usize, value: T); - /// Get the current value at the specified position /// /// # Arguments @@ -20,6 +9,7 @@ pub trait Grid { /// # Panics /// /// When accessing `x` or `y` out of bounds. + #[must_use] fn get(&self, x: usize, y: usize) -> T; /// Get the current value at the specified position if the position is inside of bounds @@ -29,6 +19,7 @@ pub trait Grid { /// - `x` and `y`: position of the cell to read /// /// returns: Value at position or None + #[must_use] fn get_optional(&self, x: usize, y: usize) -> Option { if self.is_in_bounds(x, y) { Some(self.get(x, y)) @@ -37,32 +28,16 @@ pub trait Grid { } } - /// Sets the value at the specified position if the position is inside of bounds - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// returns: the old value or None - fn set_optional(&mut self, x: usize, y: usize, value: T) -> bool { - if self.is_in_bounds(x, y) { - self.set(x, y, value); - true - } else { - false - } - } - - /// Sets all cells in the grid to the specified value - fn fill(&mut self, value: T); - /// the size in x-direction + #[must_use] fn width(&self) -> usize; /// the height in y-direction + #[must_use] fn height(&self) -> usize; /// Checks whether the specified signed position is in grid bounds + #[must_use] fn is_in_bounds(&self, x: usize, y: usize) -> bool { x < self.width() && y < self.height() } @@ -79,3 +54,37 @@ pub trait Grid { assert!(y < height, "cannot access index [{x}, {y}] because y is outside of bounds [0..{height})"); } } + +/// A two-dimensional mutable grid of `T` +pub trait GridMut: Grid { + /// Sets the value at the specified position + /// + /// # Arguments + /// + /// - `x` and `y`: position of the cell to read + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. + fn set(&mut self, x: usize, y: usize, value: T); + + /// Sets the value at the specified position if the position is inside of bounds + /// + /// # Arguments + /// + /// - `x` and `y`: position of the cell to read + /// + /// returns: true if the value has been set + #[must_use] + fn set_optional(&mut self, x: usize, y: usize, value: T) -> bool { + if self.is_in_bounds(x, y) { + self.set(x, y, value); + true + } else { + false + } + } + + /// Sets all cells in the grid to the specified value + fn fill(&mut self, value: T); +} diff --git a/src/containers/grid_view.rs b/src/containers/grid_view.rs deleted file mode 100644 index 9d57524..0000000 --- a/src/containers/grid_view.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::Grid; -use std::marker::PhantomData; - -#[derive(Debug)] -pub struct WindowMut<'t, TElement: Copy, TGrid: Grid> { - grid: &'t mut TGrid, - x: usize, - y: usize, - width: usize, - height: usize, - phantom: PhantomData, -} - -impl<'t, TElement: Copy, TGrid: Grid> WindowMut<'t, TElement, TGrid> { - #[must_use] - pub fn new( - grid: &'t mut TGrid, - x: usize, - y: usize, - width: usize, - height: usize, - ) -> Option { - if width == 0 || height == 0 { - return None; - } - if !grid.is_in_bounds(x + width - 1, y + height - 1) { - return None; - } - - Some(Self { - grid, - x, - y, - width, - height, - phantom: PhantomData, - }) - } -} - -impl> Grid - for WindowMut<'_, TElement, TGrid> -{ - fn set(&mut self, x: usize, y: usize, value: TElement) { - self.grid.set(x + self.x, y + self.y, value) - } - fn get(&self, x: usize, y: usize) -> TElement { - self.grid.get(x + self.x, y + self.y) - } - - fn fill(&mut self, value: TElement) { - for y in self.y..(self.y + self.height) { - for x in self.x..(self.x + self.width) { - self.grid.set(x, y, value); - } - } - } - - fn width(&self) -> usize { - self.width - } - - fn height(&self) -> usize { - self.height - } -} - -#[cfg(test)] -mod tests { - use crate::containers::grid_view::WindowMut; - use crate::{Bitmap, CharGrid, DataRef, Grid}; - - #[test] - fn test_grid_view_bitmap() { - let mut bitmap = Bitmap::new(8, 4).unwrap(); - - // non-byte-aligned views work - let mut view = WindowMut::new(&mut bitmap, 3, 1, 4, 2).unwrap(); - view.fill(true); - - assert_eq!(bitmap.data_ref(), &[0, 30, 30, 0]); - - // full size view works - _ = WindowMut::new(&mut bitmap, 0, 0, 8, 4).unwrap(); - - // zero size view does not work - assert!(WindowMut::new(&mut bitmap, 1, 2, 3, 0).is_none()); - assert!(WindowMut::new(&mut bitmap, 1, 2, 0, 1).is_none()); - - // oob does not work - assert!(WindowMut::new(&mut bitmap, 30, 43, 3, 1).is_none()); - assert!(WindowMut::new(&mut bitmap, 0, 0, 9, 1).is_none()); - assert!(WindowMut::new(&mut bitmap, 0, 0, 1, 5).is_none()); - } - - #[test] - fn test_grid_view_char_grid() { - let mut grid = CharGrid::new(3, 4); - grid.fill(' '); - - let mut view = WindowMut::new(&mut grid, 1, 1, 1, 2).unwrap(); - view.fill('#'); - - assert_eq!( - grid.data_ref(), - &[' ', ' ', ' ', ' ', '#', ' ', ' ', '#', ' ', ' ', ' ', ' '] - ); - - // full size view works - _ = WindowMut::new(&mut grid, 0, 0, 3, 4).unwrap(); - - // zero size view does not work - assert!(WindowMut::new(&mut grid, 1, 2, 2, 0).is_none()); - assert!(WindowMut::new(&mut grid, 1, 2, 0, 1).is_none()); - } - - #[test] - fn round_trip_bitmap() { - let mut bitmap = Bitmap::new(8, 4).unwrap(); - - let non_aligned = WindowMut::new(&mut bitmap, 3, 1, 4, 2).unwrap(); - assert_eq!(Bitmap::try_from(&non_aligned), Err(())); - - let aligned = WindowMut::new(&mut bitmap, 0, 1, 8, 2).unwrap(); - - assert!(matches!(Bitmap::try_from(&aligned), Ok(_))); - } -} diff --git a/src/containers/mod.rs b/src/containers/mod.rs index 1005baf..4ab8af3 100644 --- a/src/containers/mod.rs +++ b/src/containers/mod.rs @@ -6,8 +6,8 @@ mod char_grid; mod cp437_grid; mod data_ref; mod grid; -mod grid_view; mod value_grid; +mod window; pub use bit_vec::{bitvec, DisplayBitVec}; pub use bitmap::*; @@ -16,8 +16,8 @@ pub use byte_grid::ByteGrid; pub use char_grid::CharGrid; pub use cp437_grid::Cp437Grid; pub use data_ref::DataRef; -pub use grid::Grid; -pub use grid_view::WindowMut; +pub use grid::{Grid, GridMut}; pub use value_grid::{ IterGridRows, SetValueSeriesError, TryLoadValueGridError, Value, ValueGrid, }; +pub use window::{Window, WindowMut}; diff --git a/src/containers/value_grid.rs b/src/containers/value_grid.rs index f0c8b1a..3129f38 100644 --- a/src/containers/value_grid.rs +++ b/src/containers/value_grid.rs @@ -1,4 +1,4 @@ -use crate::{DataRef, Grid, WindowMut}; +use crate::{DataRef, Grid, GridMut, Window}; use inherent::inherent; use std::fmt::Debug; use std::slice::{Iter, IterMut}; @@ -322,6 +322,35 @@ pub enum TryLoadValueGridError { #[inherent] impl Grid for ValueGrid { + /// Gets the current value at the specified position. + /// + /// # Arguments + /// + /// - `x` and `y`: position of the cell to read + /// + /// # Panics + /// + /// When accessing `x` or `y` out of bounds. + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn get(&self, x: usize, y: usize) -> T { + self.assert_in_bounds(x, y); + self.data[x + y * self.width] + } + + #[must_use] + pub fn width(&self) -> usize { + self.width + } + + #[must_use] + pub fn height(&self) -> usize { + self.height + } +} + +#[inherent] +impl GridMut for ValueGrid { /// Sets the value of the cell at the specified position in the grid. /// /// # Arguments @@ -333,38 +362,15 @@ impl Grid for ValueGrid { /// /// When accessing `x` or `y` out of bounds. #[allow(unused, reason = "False positive because of #[inherent]")] - fn set(&mut self, x: usize, y: usize, value: T) { + pub fn set(&mut self, x: usize, y: usize, value: T) { self.assert_in_bounds(x, y); self.data[x + y * self.width] = value; } - /// Gets the current value at the specified position. - /// - /// # Arguments - /// - /// - `x` and `y`: position of the cell to read - /// - /// # Panics - /// - /// When accessing `x` or `y` out of bounds. #[allow(unused, reason = "False positive because of #[inherent]")] - fn get(&self, x: usize, y: usize) -> T { - self.assert_in_bounds(x, y); - self.data[x + y * self.width] - } - - #[allow(unused, reason = "False positive because of #[inherent]")] - fn fill(&mut self, value: T) { + pub fn fill(&mut self, value: T) { self.data.fill(value); } - - fn width(&self) -> usize { - self.width - } - - fn height(&self) -> usize { - self.height - } } impl DataRef for ValueGrid { @@ -441,8 +447,8 @@ impl Iterator for EnumerateGrid<'_, T> { } } -impl From<&WindowMut<'_, E, Self>> for ValueGrid { - fn from(value: &WindowMut<'_, E, Self>) -> Self { +impl From<&Window<'_, E, Self>> for ValueGrid { + fn from(value: &Window<'_, E, Self>) -> Self { let mut result = Self::new(value.width(), value.height()); for y in 0..value.height() { for x in 0..value.width() { @@ -552,7 +558,7 @@ mod tests { #[should_panic] fn out_of_bounds_y() { let vec = ValueGrid::load(2, 2, &[0, 1, 2, 3]).unwrap(); - vec.get(1, 2); + _ = vec.get(1, 2); } #[test] @@ -575,8 +581,8 @@ mod tests { #[test] fn optional() { let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]).unwrap(); - grid.set_optional(0, 0, 5); - grid.set_optional(0, 8, 42); + assert!(grid.set_optional(0, 0, 5)); + assert!(!grid.set_optional(0, 8, 42)); assert_eq!(grid.data, [5, 1, 2, 3]); assert_eq!(grid.get_optional(0, 0), Some(5)); diff --git a/src/containers/window.rs b/src/containers/window.rs new file mode 100644 index 0000000..541005d --- /dev/null +++ b/src/containers/window.rs @@ -0,0 +1,160 @@ +use crate::{Grid, GridMut}; +use std::marker::PhantomData; + +macro_rules! define_window { + ($name:ident, $grid:ty) => { + /// A window into a 2D grid. + /// + /// All operations are done directly on the contained reference, + /// but translated to where the window is. + #[derive(Debug)] + pub struct $name<'t, TElement: Copy, TGrid: Grid> { + grid: $grid, + x: usize, + y: usize, + width: usize, + height: usize, + phantom: PhantomData, + } + + impl<'t, TElement: Copy, TGrid: Grid> + $name<'t, TElement, TGrid> + { + /// Create a new window into `grid`. + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn new( + grid: $grid, + x: usize, + y: usize, + width: usize, + height: usize, + ) -> Option { + if width == 0 || height == 0 { + return None; + } + if !grid.is_in_bounds(x + width - 1, y + height - 1) { + return None; + } + + Some(Self { + grid, + x, + y, + width, + height, + phantom: PhantomData, + }) + } + } + + #[inherent::inherent] + impl> Grid + for $name<'_, TElement, TGrid> + { + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn get(&self, x: usize, y: usize) -> TElement { + self.grid.get(x + self.x, y + self.y) + } + + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn width(&self) -> usize { + self.width + } + + #[must_use] + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn height(&self) -> usize { + self.height + } + } + }; +} + +define_window!(Window, &'t TGrid); +define_window!(WindowMut, &'t mut TGrid); + +#[inherent::inherent] +impl> GridMut + for WindowMut<'_, TElement, TGrid> +{ + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn set(&mut self, x: usize, y: usize, value: TElement) { + self.grid.set(x + self.x, y + self.y, value); + } + + #[allow(unused, reason = "False positive because of #[inherent]")] + pub fn fill(&mut self, value: TElement) { + for y in self.y..(self.y + self.height) { + for x in self.x..(self.x + self.width) { + self.grid.set(x, y, value); + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::containers::window::{Window, WindowMut}; + use crate::{Bitmap, CharGrid, DataRef, GridMut}; + + #[test] + fn test_grid_view_bitmap() { + let mut bitmap = Bitmap::new(8, 4).unwrap(); + + // non-byte-aligned views work + let mut view = WindowMut::new(&mut bitmap, 3, 1, 4, 2).unwrap(); + view.fill(true); + + assert_eq!(bitmap.data_ref(), &[0, 30, 30, 0]); + + assert_eq!(bitmap.set_optional(99, 99, false), false); + + // full size view works + _ = Window::new(&mut bitmap, 0, 0, 8, 4).unwrap(); + + // zero size view does not work + assert!(Window::new(&mut bitmap, 1, 2, 3, 0).is_none()); + assert!(WindowMut::new(&mut bitmap, 1, 2, 0, 1).is_none()); + + // oob does not work + assert!(Window::new(&mut bitmap, 30, 43, 3, 1).is_none()); + assert!(WindowMut::new(&mut bitmap, 0, 0, 9, 1).is_none()); + assert!(Window::new(&mut bitmap, 0, 0, 1, 5).is_none()); + } + + #[test] + fn test_grid_view_char_grid() { + let mut grid = CharGrid::new(3, 4); + grid.fill(' '); + + let mut view = WindowMut::new(&mut grid, 1, 1, 1, 2).unwrap(); + view.fill('#'); + + assert_eq!( + grid.data_ref(), + &[' ', ' ', ' ', ' ', '#', ' ', ' ', '#', ' ', ' ', ' ', ' '] + ); + + // full size view works + _ = Window::new(&mut grid, 0, 0, 3, 4).unwrap(); + + // zero size view does not work + assert!(Window::new(&mut grid, 1, 2, 2, 0).is_none()); + assert!(Window::new(&mut grid, 1, 2, 0, 1).is_none()); + } + + #[test] + fn round_trip_bitmap() { + let mut bitmap = Bitmap::new(8, 4).unwrap(); + + let non_aligned = Window::new(&mut bitmap, 3, 1, 4, 2).unwrap(); + assert_eq!(Bitmap::try_from(&non_aligned), Err(())); + + let aligned = Window::new(&mut bitmap, 0, 1, 8, 2).unwrap(); + + assert!(matches!(Bitmap::try_from(&aligned), Ok(_))); + } +}