do not panic if bitmap has invalid parameters
This commit is contained in:
parent
427dd93088
commit
9bff9bd346
3 changed files with 84 additions and 82 deletions
162
src/bitmap.rs
162
src/bitmap.rs
|
|
@ -27,23 +27,30 @@ pub struct Bitmap {
|
|||
|
||||
impl Bitmap {
|
||||
/// Creates a new [Bitmap] with the specified dimensions.
|
||||
/// The initial state of the contained pixels is false.
|
||||
///
|
||||
/// The width has to be a multiple of [TILE_SIZE], otherwise this function returns None.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `width`: size in pixels in x-direction
|
||||
/// - `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: [Bitmap] initialized to all pixels off
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the width is not dividable by 8
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
assert_eq!(
|
||||
width % 8,
|
||||
0,
|
||||
"width must be a multiple of 8, but is {width}"
|
||||
);
|
||||
#[must_use]
|
||||
pub fn new(width: usize, height: usize) -> Option<Self> {
|
||||
if width % 8 != 0 {
|
||||
return None;
|
||||
}
|
||||
Some(Self::new_unchecked(width, height))
|
||||
}
|
||||
|
||||
/// Creates a new pixel grid with the size of the whole screen.
|
||||
#[must_use]
|
||||
pub fn max_sized() -> Self {
|
||||
Self::new_unchecked(PIXEL_WIDTH, PIXEL_HEIGHT)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn new_unchecked(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
|
|
@ -51,71 +58,57 @@ impl Bitmap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new pixel grid with the size of the whole screen.
|
||||
#[must_use]
|
||||
pub fn max_sized() -> Self {
|
||||
Self::new(PIXEL_WIDTH, PIXEL_HEIGHT)
|
||||
}
|
||||
|
||||
/// Loads a [Bitmap] with the specified dimensions from the provided data.
|
||||
///
|
||||
/// The data cannot be loaded on the following cases:
|
||||
/// - when the dimensions and data size do not match exactly.
|
||||
/// - when the width is not dividable by 8
|
||||
///
|
||||
/// In those cases, an Err is returned.
|
||||
///
|
||||
/// Otherwise, this returns a [Bitmap] that contains a copy of the provided data
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `width`: size in pixels in x-direction
|
||||
/// - `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: [Bitmap] that contains a copy of the provided data
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the dimensions and data size do not match exactly.
|
||||
/// - when the width is not dividable by 8
|
||||
#[must_use]
|
||||
pub fn load(width: usize, height: usize, data: &[u8]) -> Self {
|
||||
assert_eq!(
|
||||
width % 8,
|
||||
0,
|
||||
"width must be a multiple of 8, but is {width}"
|
||||
);
|
||||
assert_eq!(
|
||||
data.len(),
|
||||
height * width / 8,
|
||||
"data length must match dimensions, with 8 pixels per byte."
|
||||
);
|
||||
Self {
|
||||
pub fn load(width: usize, height: usize, data: &[u8]) -> Result<Self, LoadBitmapError> {
|
||||
if width % 8 != 0 {
|
||||
return Err(LoadBitmapError::InvalidWidth)
|
||||
}
|
||||
if data.len() != height * width / 8 {
|
||||
return Err(LoadBitmapError::InvalidDataSize)
|
||||
}
|
||||
Ok(Self {
|
||||
width,
|
||||
height,
|
||||
bit_vec: BitVec::from_slice(data),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a [Bitmap] with the specified width from the provided [BitVec] without copying it.
|
||||
///
|
||||
/// returns: [Bitmap] that contains the provided data.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the bitvec size is not dividable by the provided width
|
||||
/// The data cannot be loaded on the following cases:
|
||||
/// - when the data size is not divisible by the width (incomplete rows)
|
||||
/// - when the width is not dividable by 8
|
||||
#[must_use]
|
||||
pub fn from_bitvec(width: usize, bit_vec: BitVec) -> Self {
|
||||
assert_eq!(
|
||||
width % 8,
|
||||
0,
|
||||
"width must be a multiple of 8, but is {width}"
|
||||
);
|
||||
///
|
||||
/// In those cases, an Err is returned.
|
||||
/// Otherwise, this returns a [Bitmap] that contains the provided data.
|
||||
pub fn from_bitvec(width: usize, bit_vec: BitVec) -> Result<Self, LoadBitmapError> {
|
||||
if width % 8 != 0 {
|
||||
return Err(LoadBitmapError::InvalidWidth)
|
||||
}
|
||||
let len = bit_vec.len();
|
||||
let height = len / width;
|
||||
assert_eq!(
|
||||
0,
|
||||
len % width,
|
||||
"dimension mismatch - len {len} is not dividable by {width}"
|
||||
);
|
||||
Self {
|
||||
if len % width != 0 {
|
||||
return Err(LoadBitmapError::InvalidDataSize)
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
width,
|
||||
height,
|
||||
bit_vec,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Iterate over all cells in [Bitmap].
|
||||
|
|
@ -123,7 +116,7 @@ impl Bitmap {
|
|||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
/// # use servicepoint::{Bitmap, Grid};
|
||||
/// # let grid = Bitmap::new(8,2);
|
||||
/// # let grid = Bitmap::new(8, 2).unwrap();
|
||||
/// for y in 0..grid.height() {
|
||||
/// for x in 0..grid.width() {
|
||||
/// grid.get(x, y);
|
||||
|
|
@ -139,7 +132,7 @@ impl Bitmap {
|
|||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
/// # use servicepoint::{Bitmap, Grid};
|
||||
/// # let mut grid = Bitmap::new(8,2);
|
||||
/// # let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
/// # let value = false;
|
||||
/// for y in 0..grid.height() {
|
||||
/// for x in 0..grid.width() {
|
||||
|
|
@ -151,7 +144,7 @@ impl Bitmap {
|
|||
/// # Example
|
||||
/// ```
|
||||
/// # use servicepoint::{Bitmap, Grid};
|
||||
/// # let mut grid = Bitmap::new(8,2);
|
||||
/// # let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
/// # let value = false;
|
||||
/// for (index, mut pixel) in grid.iter_mut().enumerate() {
|
||||
/// pixel.set(index % 2 == 0)
|
||||
|
|
@ -236,18 +229,19 @@ impl From<Bitmap> for BitVec {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&ValueGrid<bool>> for Bitmap {
|
||||
impl TryFrom<&ValueGrid<bool>> for Bitmap {
|
||||
type Error = ();
|
||||
|
||||
/// Converts a grid of [bool]s into a [Bitmap].
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the width of `value` is not dividable by 8
|
||||
fn from(value: &ValueGrid<bool>) -> Self {
|
||||
let mut result = Self::new(value.width(), value.height());
|
||||
/// Returns Err if the width of `value` is not dividable by 8
|
||||
fn try_from(value: &ValueGrid<bool>) -> Result<Self, Self::Error> {
|
||||
let mut result = Self::new(value.width(), value.height())
|
||||
.ok_or(())?;
|
||||
for (mut to, from) in result.iter_mut().zip(value.iter()) {
|
||||
*to = *from;
|
||||
}
|
||||
result
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -282,13 +276,21 @@ impl<'t> Iterator for IterRows<'t> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum LoadBitmapError {
|
||||
#[error("The provided width is not divisible by 8.")]
|
||||
InvalidWidth,
|
||||
#[error("The provided data has an incorrect size for the provided dimensions.")]
|
||||
InvalidDataSize
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{BitVec, Bitmap, DataRef, Grid, ValueGrid};
|
||||
|
||||
#[test]
|
||||
fn fill() {
|
||||
let mut grid = Bitmap::new(8, 2);
|
||||
let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
assert_eq!(grid.data_ref(), [0x00, 0x00]);
|
||||
|
||||
grid.fill(true);
|
||||
|
|
@ -300,7 +302,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn get_set() {
|
||||
let mut grid = Bitmap::new(8, 2);
|
||||
let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
assert!(!grid.get(0, 0));
|
||||
assert!(!grid.get(1, 1));
|
||||
|
||||
|
|
@ -315,7 +317,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn load() {
|
||||
let mut grid = Bitmap::new(8, 3);
|
||||
let mut grid = Bitmap::new(8, 3).unwrap();
|
||||
for x in 0..grid.width {
|
||||
for y in 0..grid.height {
|
||||
grid.set(x, y, (x + y) % 2 == 0);
|
||||
|
|
@ -326,33 +328,33 @@ mod tests {
|
|||
|
||||
let data: Vec<u8> = grid.into();
|
||||
|
||||
let grid = Bitmap::load(8, 3, &data);
|
||||
let grid = Bitmap::load(8, 3, &data).unwrap();
|
||||
assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_x() {
|
||||
let vec = Bitmap::new(8, 2);
|
||||
let vec = Bitmap::new(8, 2).unwrap();
|
||||
vec.get(8, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_y() {
|
||||
let mut vec = Bitmap::new(8, 2);
|
||||
let mut vec = Bitmap::new(8, 2).unwrap();
|
||||
vec.set(1, 2, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter() {
|
||||
let grid = Bitmap::new(8, 2);
|
||||
let grid = Bitmap::new(8, 2).unwrap();
|
||||
assert_eq!(16, grid.iter().count())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_rows() {
|
||||
let grid = Bitmap::load(8, 2, &[0x04, 0x40]);
|
||||
let grid = Bitmap::load(8, 2, &[0x04, 0x40]).unwrap();
|
||||
let mut iter = grid.iter_rows();
|
||||
|
||||
assert_eq!(iter.next().unwrap().count_ones(), 1);
|
||||
|
|
@ -362,7 +364,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn iter_mut() {
|
||||
let mut grid = Bitmap::new(8, 2);
|
||||
let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
for (index, mut pixel) in grid.iter_mut().enumerate() {
|
||||
pixel.set(index % 2 == 0);
|
||||
}
|
||||
|
|
@ -371,7 +373,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn data_ref_mut() {
|
||||
let mut grid = Bitmap::new(8, 2);
|
||||
let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
let data = grid.data_ref_mut();
|
||||
data[1] = 0x0F;
|
||||
assert!(grid.get(7, 1));
|
||||
|
|
@ -379,7 +381,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn to_bitvec() {
|
||||
let mut grid = Bitmap::new(8, 2);
|
||||
let mut grid = Bitmap::new(8, 2).unwrap();
|
||||
grid.set(0, 0, true);
|
||||
let bitvec: BitVec = grid.into();
|
||||
assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]);
|
||||
|
|
@ -392,7 +394,7 @@ mod tests {
|
|||
1,
|
||||
&[true, false, true, false, true, false, true, false],
|
||||
);
|
||||
let converted = Bitmap::from(&original);
|
||||
let converted = Bitmap::try_from(&original).unwrap();
|
||||
let reconverted = ValueGrid::from(&converted);
|
||||
assert_eq!(original, reconverted);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ impl BitmapLinearWin {
|
|||
tile_w as usize * TILE_SIZE,
|
||||
pixel_h as usize,
|
||||
&payload,
|
||||
),
|
||||
).unwrap(),
|
||||
compression,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ mod tests {
|
|||
for compression in all_compressions().iter().copied() {
|
||||
let p: Packet = commands::BitmapLinearWin {
|
||||
origin: Origin::new(16, 8),
|
||||
bitmap: Bitmap::new(8, 8),
|
||||
bitmap: Bitmap::new(8, 8).unwrap(),
|
||||
compression,
|
||||
}
|
||||
.into();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue