use stipple_geometry::{PhysicalSize, Rect};
#[derive(Clone, Debug)]
pub struct Pixmap {
size: PhysicalSize,
data: Vec<u8>,
}
impl Pixmap {
pub fn new(size: PhysicalSize) -> Self {
Self {
size,
data: vec![0u8; size.pixel_count() as usize * 4],
}
}
pub fn from_rgba8(size: PhysicalSize, data: Vec<u8>) -> Self {
assert_eq!(
data.len(),
size.pixel_count() as usize * 4,
"pixmap byte length must equal width*height*4"
);
Self { size, data }
}
#[inline]
pub fn size(&self) -> PhysicalSize {
self.size
}
#[inline]
pub fn stride(&self) -> usize {
self.size.width as usize * 4
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
#[inline]
pub fn as_bytes_mut(&mut self) -> &mut [u8] {
&mut self.data
}
pub fn blit(&mut self, src: &Pixmap, dst_x: u32, dst_y: u32) {
let dst_w = self.size.width;
let dst_h = self.size.height;
let copy_w = src.size.width.min(dst_w.saturating_sub(dst_x));
let copy_h = src.size.height.min(dst_h.saturating_sub(dst_y));
if copy_w == 0 || copy_h == 0 {
return;
}
let (src_stride, dst_stride) = (src.stride(), self.stride());
let row_bytes = copy_w as usize * 4;
for row in 0..copy_h as usize {
let s = row * src_stride;
let d = (dst_y as usize + row) * dst_stride + dst_x as usize * 4;
self.data[d..d + row_bytes].copy_from_slice(&src.data[s..s + row_bytes]);
}
}
pub fn pixel(&self, x: u32, y: u32) -> Option<[u8; 4]> {
if x >= self.size.width || y >= self.size.height {
return None;
}
let i = y as usize * self.stride() + x as usize * 4;
Some([
self.data[i],
self.data[i + 1],
self.data[i + 2],
self.data[i + 3],
])
}
}
pub trait Surface {
fn resize(&mut self, size: PhysicalSize);
fn size(&self) -> PhysicalSize;
fn present(&mut self, pixmap: &Pixmap, damage: &[Rect]);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pixel_access_bounds() {
let mut pm = Pixmap::new(PhysicalSize::new(2, 2));
pm.as_bytes_mut()[4..8].copy_from_slice(&[10, 20, 30, 40]);
assert_eq!(pm.pixel(1, 0), Some([10, 20, 30, 40]));
assert_eq!(pm.pixel(2, 0), None);
assert_eq!(pm.stride(), 8);
}
#[test]
fn blit_composites_at_offset() {
let mut dst = Pixmap::new(PhysicalSize::new(4, 4));
let mut src = Pixmap::new(PhysicalSize::new(2, 2));
for px in src.as_bytes_mut().chunks_exact_mut(4) {
px.copy_from_slice(&[1, 2, 3, 4]);
}
dst.blit(&src, 1, 1);
assert_eq!(dst.pixel(0, 0), Some([0, 0, 0, 0]));
assert_eq!(dst.pixel(1, 1), Some([1, 2, 3, 4]));
assert_eq!(dst.pixel(2, 2), Some([1, 2, 3, 4]));
assert_eq!(dst.pixel(3, 3), Some([0, 0, 0, 0]));
}
#[test]
fn blit_clips_to_destination_bounds() {
let mut dst = Pixmap::new(PhysicalSize::new(2, 2));
let mut src = Pixmap::new(PhysicalSize::new(2, 2));
for px in src.as_bytes_mut().chunks_exact_mut(4) {
px.copy_from_slice(&[9, 9, 9, 9]);
}
dst.blit(&src, 1, 1);
assert_eq!(dst.pixel(1, 1), Some([9, 9, 9, 9]));
assert_eq!(dst.pixel(0, 0), Some([0, 0, 0, 0]));
dst.blit(&src, 5, 5);
}
}