use crate::{
containers::absolute_bounds_to_abs_range, DataRef, DisplayBitVec, Grid,
GridMut, Payload, ValueGrid, Window, WindowMut, PIXEL_HEIGHT, PIXEL_WIDTH,
};
use ::bitvec::{order::Msb0, prelude::BitSlice, slice::IterMut};
use std::ops::RangeBounds;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Bitmap {
width: usize,
height: usize,
bit_vec: DisplayBitVec,
}
impl Bitmap {
#[must_use]
pub fn new(width: usize, height: usize) -> Option<Self> {
assert!(width < isize::MAX as usize);
assert!(height < isize::MAX as usize);
if width % 8 != 0 {
return None;
}
Some(Self::new_unchecked(width, height))
}
#[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,
bit_vec: DisplayBitVec::repeat(false, width * height),
}
}
pub fn load(
width: usize,
height: usize,
data: &[u8],
) -> Result<Self, LoadBitmapError> {
assert!(width < isize::MAX as usize);
assert!(height < isize::MAX as usize);
if width % 8 != 0 {
return Err(LoadBitmapError::InvalidWidth);
}
if data.len() != height * width / 8 {
return Err(LoadBitmapError::InvalidDataSize);
}
Ok(Self {
width,
height,
bit_vec: DisplayBitVec::from_slice(data),
})
}
pub fn from_bitvec(
width: usize,
bit_vec: DisplayBitVec,
) -> Result<Self, LoadBitmapError> {
if width % 8 != 0 {
return Err(LoadBitmapError::InvalidWidth);
}
let len = bit_vec.len();
let height = len / width;
assert!(width < isize::MAX as usize);
assert!(height < isize::MAX as usize);
if len % width != 0 {
return Err(LoadBitmapError::InvalidDataSize);
}
Ok(Self {
width,
height,
bit_vec,
})
}
pub fn iter(&self) -> impl Iterator<Item = &bool> {
self.bit_vec.iter().by_refs()
}
#[must_use]
#[allow(clippy::iter_without_into_iter)]
pub fn iter_mut(&mut self) -> IterMut<'_, u8, Msb0> {
self.bit_vec.iter_mut()
}
pub fn iter_rows(&self) -> impl Iterator<Item = &BitSlice<u8, Msb0>> {
IterRows {
bitmap: self,
row: 0,
}
}
#[must_use]
pub fn window(
&self,
xs: impl RangeBounds<usize>,
ys: impl RangeBounds<usize>,
) -> Option<Window<'_, bool, Self>> {
let xs = absolute_bounds_to_abs_range(xs, self.width)?;
let ys = absolute_bounds_to_abs_range(ys, self.height)?;
Window::new(self, xs, ys)
}
pub fn window_mut(
&mut self,
xs: impl RangeBounds<usize>,
ys: impl RangeBounds<usize>,
) -> Option<WindowMut<'_, bool, Self>> {
let xs = absolute_bounds_to_abs_range(xs, self.width)?;
let ys = absolute_bounds_to_abs_range(ys, self.height)?;
WindowMut::new(self, xs, ys)
}
}
impl Grid<bool> for Bitmap {
fn get_optional(&self, x: usize, y: usize) -> Option<bool> {
let index = x + y * self.width;
if self.is_in_bounds(x, y) {
Some(self.bit_vec[index])
} else {
None
}
}
fn width(&self) -> usize {
self.width
}
fn height(&self) -> usize {
self.height
}
}
impl GridMut<bool> for Bitmap {
fn set_optional(&mut self, x: usize, y: usize, value: bool) -> bool {
if self.is_in_bounds(x, y) {
self.bit_vec.set(x + y * self.width, value);
true
} else {
false
}
}
fn fill(&mut self, value: bool) {
self.bit_vec.fill(value);
}
}
impl DataRef<u8> for Bitmap {
fn data_ref_mut(&mut self) -> &mut [u8] {
self.bit_vec.as_raw_mut_slice()
}
fn data_ref(&self) -> &[u8] {
self.bit_vec.as_raw_slice()
}
}
impl From<Bitmap> for Vec<u8> {
fn from(value: Bitmap) -> Self {
value.bit_vec.into()
}
}
impl From<Bitmap> for DisplayBitVec {
fn from(value: Bitmap) -> Self {
value.bit_vec
}
}
impl TryFrom<&ValueGrid<bool>> for Bitmap {
type Error = ();
fn try_from(value: &ValueGrid<bool>) -> Result<Self, Self::Error> {
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
result.deref_assign(value);
Ok(result)
}
}
impl<T: Grid<bool>> TryFrom<&Window<'_, bool, T>> for Bitmap {
type Error = ();
fn try_from(value: &Window<bool, T>) -> Result<Self, Self::Error> {
let mut result = Self::new(value.width(), value.height()).ok_or(())?;
result.deref_assign(value);
Ok(result)
}
}
impl From<&Bitmap> for Payload {
fn from(value: &Bitmap) -> Self {
value.bit_vec.as_raw_slice().into()
}
}
#[must_use]
struct IterRows<'t> {
bitmap: &'t Bitmap,
row: usize,
}
impl<'t> Iterator for IterRows<'t> {
type Item = &'t BitSlice<u8, Msb0>;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.bitmap.height {
return None;
}
let start = self.row * self.bitmap.width;
let end = start + self.bitmap.width;
self.row += 1;
Some(&self.bitmap.bit_vec[start..end])
}
}
#[derive(thiserror::Error, Debug, PartialEq)]
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::{
Bitmap, DataRef, DisplayBitVec, Grid, GridMut, LoadBitmapError,
ValueGrid,
};
#[test]
fn fill() {
let mut grid = Bitmap::new(8, 2).unwrap();
assert_eq!(grid.data_ref(), [0x00, 0x00]);
grid.fill(true);
assert_eq!(grid.data_ref(), [0xFF, 0xFF]);
grid.fill(false);
assert_eq!(grid.data_ref(), [0x00, 0x00]);
}
#[test]
fn get_set() {
let mut grid = Bitmap::new(8, 2).unwrap();
assert!(!grid.get(0, 0));
assert!(!grid.get(1, 1));
grid.set(5, 0, true);
grid.set(1, 1, true);
assert_eq!(grid.data_ref(), [0x04, 0x40]);
assert!(grid.get(5, 0));
assert!(grid.get(1, 1));
assert!(!grid.get(1, 0));
}
#[test]
fn load() {
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);
}
}
assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]);
let data: Vec<u8> = grid.into();
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).unwrap();
_ = vec.get(8, 1);
}
#[test]
#[should_panic]
fn out_of_bounds_y() {
let mut vec = Bitmap::new(8, 2).unwrap();
vec.set(1, 2, false);
}
#[test]
fn iter() {
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]).unwrap();
let mut iter = grid.iter_rows();
assert_eq!(iter.next().unwrap().count_ones(), 1);
assert_eq!(iter.next().unwrap().count_ones(), 1);
assert_eq!(None, iter.next());
}
#[test]
fn iter_mut() {
let mut grid = Bitmap::new(8, 2).unwrap();
for (index, mut pixel) in grid.iter_mut().enumerate() {
pixel.set(index % 2 == 0);
}
assert_eq!(grid.data_ref(), [0xAA, 0xAA]);
}
#[test]
fn data_ref_mut() {
let mut grid = Bitmap::new(8, 2).unwrap();
let data = grid.data_ref_mut();
data[1] = 0x0F;
assert!(grid.get(7, 1));
}
#[test]
fn to_bitvec() {
let mut grid = Bitmap::new(8, 2).unwrap();
grid.set(0, 0, true);
let bitvec: DisplayBitVec = grid.into();
assert_eq!(bitvec.as_raw_slice(), [0x80, 0x00]);
}
#[test]
fn from_bool_grid() {
let original = ValueGrid::load(
8,
1,
&[true, false, true, false, true, false, true, false],
)
.unwrap();
let converted = Bitmap::try_from(&original).unwrap();
let reconverted = ValueGrid::from(&converted);
assert_eq!(original, reconverted);
}
#[test]
fn load_invalid_width() {
let data = DisplayBitVec::repeat(false, 7 * 3).into_vec();
assert_eq!(
Bitmap::load(7, 3, &data),
Err(LoadBitmapError::InvalidWidth)
);
}
#[test]
fn load_invalid_size() {
let data = DisplayBitVec::repeat(false, 8 * 4).into_vec();
assert_eq!(
Bitmap::load(8, 3, &data),
Err(LoadBitmapError::InvalidDataSize)
);
}
#[test]
fn from_vec_invalid_width() {
let data = DisplayBitVec::repeat(false, 7 * 3);
assert_eq!(
Bitmap::from_bitvec(7, data),
Err(LoadBitmapError::InvalidWidth)
);
}
#[test]
fn from_vec_invalid_size() {
let data = DisplayBitVec::repeat(false, 7 * 4);
assert_eq!(
Bitmap::from_bitvec(8, data),
Err(LoadBitmapError::InvalidDataSize)
);
}
#[test]
fn from_vec() {
let orig = Bitmap::new(8, 3).unwrap();
assert_eq!(Bitmap::from_bitvec(8, orig.bit_vec.clone()).unwrap(), orig);
}
#[test]
fn new_invalid_width() {
assert_eq!(Bitmap::new(7, 2), None);
}
}