1use crate::{
2 AnySlice, BoundsCheckError, ColorInfo, ImageMut, ImageRef, ImageRefExt, PixelFormat,
3 plane_decs::PlaneDesc,
4};
5
6#[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#[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
25pub 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 let split_at = (y * stride + x) * plane_desc.bytes_per_primitive;
97 let (_, slice) = slice.slice_split_at(split_at);
98
99 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}