use crate::geom::{Point, Size};
use crate::surface::SurfaceError;
pub trait CursorPlane {
fn cursor_limit(&self) -> Size;
fn set_cursor(
&mut self,
pixels: &[u32],
size: Size,
hotspot: Point,
) -> Result<(), SurfaceError>;
fn move_cursor(&mut self, position: Point) -> Result<(), SurfaceError>;
fn hide_cursor(&mut self) -> Result<(), SurfaceError>;
}
pub fn pad_into(
sprite: &[u32],
size: Size,
out: &mut [u32],
limit: Size,
) -> Result<usize, SurfaceError> {
if size.width > limit.width || size.height > limit.height {
return Err(SurfaceError::CursorTooLarge {
limit,
requested: size,
});
}
let needed = (size.width as usize) * (size.height as usize);
if sprite.len() < needed {
return Err(SurfaceError::BufferTooSmall {
required: needed,
actual: sprite.len(),
});
}
let capacity = (limit.width as usize) * (limit.height as usize);
if out.len() < capacity {
return Err(SurfaceError::BufferTooSmall {
required: capacity,
actual: out.len(),
});
}
out[..capacity].fill(0);
for row in 0..size.height as usize {
let from = row * size.width as usize;
let to = row * limit.width as usize;
out[to..to + size.width as usize]
.copy_from_slice(&sprite[from..from + size.width as usize]);
}
Ok(capacity)
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
const LIMIT: Size = Size::new(4, 4);
#[test]
fn a_sprite_lands_top_left_and_the_rest_is_transparent() {
let sprite = [0xFF00_0001, 0xFF00_0002, 0xFF00_0003, 0xFF00_0004];
let mut out = vec![0xDEAD_BEEF; 16];
let written = pad_into(&sprite, Size::new(2, 2), &mut out, LIMIT).expect("pad");
assert_eq!(written, 16);
assert_eq!(&out[0..4], &[0xFF00_0001, 0xFF00_0002, 0, 0]);
assert_eq!(&out[4..8], &[0xFF00_0003, 0xFF00_0004, 0, 0]);
assert!(
out[8..].iter().all(|&p| p == 0),
"padding must be transparent"
);
}
#[test]
fn padding_is_transparent_rather_than_black() {
let sprite = [0xFFFF_FFFF];
let mut out = vec![0u32; 16];
pad_into(&sprite, Size::new(1, 1), &mut out, LIMIT).expect("pad");
assert_eq!(out[0] >> 24, 0xFF, "the sprite keeps its alpha");
assert!(
out[1..].iter().all(|&p| p >> 24 == 0),
"every padded pixel must be fully transparent"
);
}
#[test]
fn a_sprite_larger_than_the_plane_is_refused_rather_than_cropped() {
let sprite = vec![0u32; 25];
let mut out = vec![0u32; 16];
let result = pad_into(&sprite, Size::new(5, 5), &mut out, LIMIT);
assert!(matches!(result, Err(SurfaceError::CursorTooLarge { .. })));
let sprite = vec![0u32; 20];
assert!(pad_into(&sprite, Size::new(5, 4), &mut out, LIMIT).is_err());
assert!(pad_into(&sprite, Size::new(4, 5), &mut out, LIMIT).is_err());
}
#[test]
fn a_sprite_that_lies_about_its_size_is_refused() {
let sprite = [0u32; 3];
let mut out = vec![0u32; 16];
assert!(matches!(
pad_into(&sprite, Size::new(2, 2), &mut out, LIMIT),
Err(SurfaceError::BufferTooSmall {
required: 4,
actual: 3
})
));
}
#[test]
fn exactly_filling_the_plane_is_allowed() {
let sprite = vec![0xFF12_3456u32; 16];
let mut out = vec![0u32; 16];
pad_into(&sprite, LIMIT, &mut out, LIMIT).expect("a sprite may fill the plane");
assert!(out.iter().all(|&p| p == 0xFF12_3456));
}
}