ezk_image/
crop.rs

1use crate::{
2    AnySlice, BoundsCheckError, ColorInfo, ImageMut, ImageRef, ImageRefExt, PixelFormat,
3    plane_decs::PlaneDesc,
4};
5
6/// Error indicating an invalid [`Window`] for a given image
7#[derive(Debug, thiserror::Error)]
8pub enum CropError {
9    #[error("the given window coordinates go out of the parent's image bounds")]
10    WindowSizeOutOfBounds,
11
12    #[error("The parent image doesn't pass the bounds check: {0}")]
13    BoundsCheck(#[from] BoundsCheckError),
14}
15
16/// Rect used to mark the "cropping" window
17#[derive(Debug, Clone, Copy)]
18pub struct Window {
19    pub x: usize,
20    pub y: usize,
21    pub width: usize,
22    pub height: usize,
23}
24
25/// Wrapper around [`ImageRef`]/[`ImageMut`] and a [`Window`] cropping the wrapped image
26pub struct Cropped<T>(T, Window);
27
28impl<T: ImageRef + ImageRefExt> Cropped<T> {
29    pub fn new(t: T, window: Window) -> Result<Self, CropError> {
30        t.bounds_check()?;
31
32        let w = window
33            .x
34            .checked_add(window.width)
35            .ok_or(CropError::WindowSizeOutOfBounds)?;
36
37        let h = window
38            .y
39            .checked_add(window.height)
40            .ok_or(CropError::WindowSizeOutOfBounds)?;
41
42        if (w > t.width()) || (h > t.height()) {
43            return Err(CropError::WindowSizeOutOfBounds);
44        }
45
46        Ok(Self(t, window))
47    }
48
49    pub fn into_inner(self) -> T {
50        self.0
51    }
52}
53
54unsafe impl<T: ImageRef> ImageRef for Cropped<T> {
55    fn format(&self) -> PixelFormat {
56        self.0.format()
57    }
58
59    fn width(&self) -> usize {
60        self.1.width
61    }
62
63    fn height(&self) -> usize {
64        self.1.height
65    }
66
67    fn planes(&self) -> Box<dyn Iterator<Item = (&[u8], usize)> + '_> {
68        crop_planes(self.format().plane_desc(), self.0.planes(), self.1)
69    }
70
71    fn color(&self) -> ColorInfo {
72        self.0.color()
73    }
74}
75
76unsafe impl<T: ImageMut> ImageMut for Cropped<T> {
77    fn planes_mut(&mut self) -> Box<dyn Iterator<Item = (&mut [u8], usize)> + '_> {
78        crop_planes(self.format().plane_desc(), self.0.planes_mut(), self.1)
79    }
80}
81
82fn crop_planes<'a, S: AnySlice + 'a>(
83    plane_desc: &'static [PlaneDesc],
84    planes: Box<dyn Iterator<Item = (S, usize)> + 'a>,
85    window: Window,
86) -> Box<dyn Iterator<Item = (S, usize)> + 'a> {
87    Box::new(
88        plane_desc
89            .iter()
90            .zip(planes)
91            .map(move |(plane_desc, (slice, stride))| {
92                let x = plane_desc.width_op.op(window.x);
93                let y = plane_desc.height_op.op(window.y);
94
95                // First trim the bytes byte "in front" of the window
96                let split_at = (y * stride + x) * plane_desc.bytes_per_primitive;
97                let (_, slice) = slice.slice_split_at(split_at);
98
99                // Trim the bytes at the end of the window
100                let split_at = plane_desc.height_op.op(window.height) * stride;
101                let (slice, _) = slice.slice_split_at(split_at);
102
103                (slice, stride)
104            }),
105    )
106}