ezk_image/
image.rs

1use crate::{BoundsCheckError, ColorInfo, ImageMut, ImageRef, ImageRefExt, PixelFormat, infer};
2
3/// Basic wrapper around any image, implementing the [`ImageRef`] and [`ImageMut`] trait
4#[derive(Debug, Clone)]
5pub struct Image<S> {
6    format: PixelFormat,
7    buffer: BufferKind<S>,
8    strides: Vec<usize>,
9    width: usize,
10    height: usize,
11
12    color: ColorInfo,
13}
14
15#[derive(Debug, Clone)]
16pub enum BufferKind<S> {
17    Whole(S),
18    Split(Vec<S>),
19}
20
21/// Everything that can go wrong when constructing an [`Image`]
22#[derive(Debug, thiserror::Error)]
23pub enum ImageError {
24    #[error("width or height must not be zero")]
25    InvalidDimensions,
26
27    #[error(transparent)]
28    BoundsCheck(#[from] BoundsCheckError),
29}
30
31impl Image<Vec<u8>> {
32    pub fn blank(format: PixelFormat, width: usize, height: usize, color: ColorInfo) -> Self {
33        Self {
34            format,
35            buffer: BufferKind::Whole(vec![0u8; format.buffer_size(width, height)]),
36            strides: format.packed_strides(width),
37            width,
38            height,
39            color,
40        }
41    }
42}
43
44impl<S> Image<S>
45where
46    Image<S>: ImageRef,
47{
48    pub fn from_buffer(
49        format: PixelFormat,
50        buffer: S,
51        strides: Option<Vec<usize>>,
52        width: usize,
53        height: usize,
54        color: ColorInfo,
55    ) -> Result<Self, ImageError> {
56        Self::new(
57            format,
58            BufferKind::Whole(buffer),
59            strides,
60            width,
61            height,
62            color,
63        )
64    }
65
66    pub fn from_planes(
67        format: PixelFormat,
68        planes: Vec<S>,
69        strides: Option<Vec<usize>>,
70        width: usize,
71        height: usize,
72        color: ColorInfo,
73    ) -> Result<Self, ImageError> {
74        Self::new(
75            format,
76            BufferKind::Split(planes),
77            strides,
78            width,
79            height,
80            color,
81        )
82    }
83
84    fn new(
85        format: PixelFormat,
86        buffer: BufferKind<S>,
87        strides: Option<Vec<usize>>,
88        width: usize,
89        height: usize,
90        color: ColorInfo,
91    ) -> Result<Self, ImageError> {
92        if width == 0 || height == 0 {
93            return Err(ImageError::InvalidDimensions);
94        }
95
96        let strides = strides.unwrap_or_else(|| format.packed_strides(width));
97
98        let this = Self {
99            format,
100            buffer,
101            strides,
102            width,
103            height,
104            color,
105        };
106
107        this.bounds_check()?;
108
109        Ok(this)
110    }
111
112    pub fn buffer(&self) -> &BufferKind<S> {
113        &self.buffer
114    }
115
116    pub fn into_buffer(self) -> BufferKind<S> {
117        self.buffer
118    }
119}
120
121unsafe impl<S: AsRef<[u8]>> ImageRef for Image<S> {
122    fn format(&self) -> PixelFormat {
123        self.format
124    }
125    fn width(&self) -> usize {
126        self.width
127    }
128    fn height(&self) -> usize {
129        self.height
130    }
131
132    fn planes(&self) -> Box<dyn Iterator<Item = (&[u8], usize)> + '_> {
133        match &self.buffer {
134            BufferKind::Whole(buffer) => Box::new(
135                infer(
136                    self.format,
137                    buffer.as_ref(),
138                    self.width,
139                    self.height,
140                    Some(&self.strides),
141                )
142                .zip(self.strides.iter().copied()),
143            ),
144            BufferKind::Split(planes) => Box::new(
145                planes
146                    .iter()
147                    .map(|p| p.as_ref())
148                    .zip(self.strides.iter().copied()),
149            ),
150        }
151    }
152    fn color(&self) -> ColorInfo {
153        self.color
154    }
155}
156
157unsafe impl<S: AsRef<[u8]> + AsMut<[u8]>> ImageMut for Image<S> {
158    fn planes_mut(&mut self) -> Box<dyn Iterator<Item = (&mut [u8], usize)> + '_> {
159        match &mut self.buffer {
160            BufferKind::Whole(buffer) => Box::new(
161                infer(
162                    self.format,
163                    buffer.as_mut(),
164                    self.width,
165                    self.height,
166                    Some(&self.strides),
167                )
168                .zip(self.strides.iter().copied()),
169            ),
170            BufferKind::Split(planes) => Box::new(
171                planes
172                    .iter_mut()
173                    .map(|plane| plane.as_mut())
174                    .zip(self.strides.iter().copied()),
175            ),
176        }
177    }
178}