use crate::pixmap::Color;
use std::cell::SyncUnsafeCell;
use thiserror::Error;
#[derive(Debug)]
pub struct Pixmap {
data: SyncUnsafeCell<Vec<Color>>,
width: usize,
height: usize,
}
#[derive(Debug, Error, Copy, Clone)]
#[error("Could not access invalid coordinates {}x{} on pixmap of size {}x{}", .target.0, .target.1, .pixmap_size.0, .pixmap_size.1)]
pub struct InvalidCoordinatesError {
target: (usize, usize),
pixmap_size: (usize, usize),
}
#[derive(Debug, Error, Copy, Clone)]
#[error("Given size {}x{} is not valid for constructing a pixmap: {details}", .size.0, .size.1)]
pub struct InvalidSizeError {
size: (usize, usize),
details: &'static str,
}
#[derive(Debug, Error, Copy, Clone)]
#[error("Cannot put data with size {data_len} into pixmap of dimensions {}x{} (expected data size = {}) ", .pixmap_size.0, .pixmap_size.1, .pixmap_size.0 * .pixmap_size.1)]
pub struct InvalidDataShapeError {
pixmap_size: (usize, usize),
data_len: usize,
}
impl Pixmap {
pub fn new(width: usize, height: usize) -> Result<Self, InvalidSizeError> {
if width == 0 || height == 0 {
return Err(InvalidSizeError {
size: (width, height),
details: "Width and Height must both be greater than 0",
});
}
Ok(Self {
data: SyncUnsafeCell::new(vec![Color::default(); width * height]),
width,
height,
})
}
pub fn get_size(&self) -> (usize, usize) {
(self.width, self.height)
}
pub fn get_pixel(&self, x: usize, y: usize) -> Result<Color, InvalidCoordinatesError> {
let i = y.saturating_mul(self.width).saturating_add(x);
match unsafe { self.get_color_data() }.get(i) {
None => Err(InvalidCoordinatesError {
target: (x, y),
pixmap_size: self.get_size(),
}),
Some(color) => Ok(*color),
}
}
pub fn set_pixel(&self, x: usize, y: usize, color: Color) -> Result<(), InvalidCoordinatesError> {
let i = y.saturating_mul(self.width).saturating_add(x);
match unsafe { self.get_color_data() }.get_mut(i) {
None => Err(InvalidCoordinatesError {
target: (x, y),
pixmap_size: self.get_size(),
}),
Some(stored_color) => {
*stored_color = color;
Ok(())
}
}
}
#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn get_color_data(&self) -> &mut [Color] {
&mut *self.data.get()
}
}
#[cfg(test)]
mod test {
use super::*;
use quickcheck::{quickcheck, TestResult};
quickcheck! {
fn test_set_and_get_pixel(x: usize, y: usize) -> TestResult {
let color = Color::from((0xAB, 0xAB, 0xAB));
let pixmap = Pixmap::new(80, 60).unwrap();
match pixmap.set_pixel(x, y, color) {
Err(_) => TestResult::discard(),
Ok(_) => {
let got_color = pixmap.get_pixel(x, y).unwrap();
TestResult::from_bool(color == got_color)
}
}
}
}
}