1use crate::err::ColorError;
8use std::borrow::Cow;
9use std::fmt::Debug;
10use std::ops::{Index, Range, RangeFrom};
11
12pub struct ImageBuffer<'a, F>
13where
14 [F]: ToOwned<Owned = Vec<F>>,
15{
16 pub data: std::borrow::Cow<'a, [F]>,
17 pub width: u32,
18 pub height: u32,
19 pub stride: u32,
20 pub channels: u32,
21}
22
23impl<'a, F: Sized> ImageBuffer<'a, F>
24where
25 [F]: ToOwned<Owned = Vec<F>>,
26{
27 pub fn new(data: &'a [F], width: u32, height: u32, channels: u32) -> Result<Self, ColorError> {
28 let min_stride = width * channels;
29 let required = min_stride as usize * height as usize;
30 if data.len() < required {
31 return Err(ColorError::BufferTooSmall {
32 expected: required,
33 got: data.len(),
34 });
35 }
36 Ok(Self {
37 data: Cow::Borrowed(data),
38 width,
39 height,
40 stride: min_stride,
41 channels,
42 })
43 }
44
45 pub fn from_vec(
46 data: Vec<F>,
47 width: u32,
48 height: u32,
49 channels: u32,
50 ) -> Result<Self, ColorError> {
51 let required = width as usize * channels as usize * height as usize;
52 if data.len() < required {
53 return Err(ColorError::BufferTooSmall {
54 expected: required,
55 got: data.len(),
56 });
57 }
58 Ok(Self {
59 data: Cow::Owned(data),
60 width,
61 height,
62 stride: width * channels,
63 channels,
64 })
65 }
66
67 pub fn try_match(&self, other: &ImageBuffer<'_, F>) -> Result<(), ColorError> {
68 if self.width != other.width || self.height != other.height {
69 return Err(ColorError::DimensionMismatch {
70 expected: (self.width, self.height),
71 got: (other.width, other.height),
72 });
73 }
74 Ok(())
75 }
76
77 pub fn validate(&self) -> Result<(), ColorError> {
78 let min_stride = self
79 .width
80 .checked_mul(self.channels)
81 .ok_or(ColorError::DimensionOverflow)?;
82
83 if self.stride < min_stride {
84 return Err(ColorError::StrideTooNarrow {
85 min: min_stride,
86 given: self.stride,
87 });
88 }
89
90 let required = self
91 .stride()
92 .checked_mul(self.height as usize)
93 .ok_or(ColorError::DimensionOverflow)?;
94
95 if required == 0 {
96 return Err(ColorError::ZeroImageSize);
97 }
98
99 if self.data.len() < required {
100 return Err(ColorError::BufferTooSmall {
101 expected: required,
102 got: self.data.len(),
103 });
104 }
105
106 Ok(())
107 }
108
109 pub fn stride(&self) -> usize {
110 if self.stride == 0 {
111 return self.width as usize * self.channels as usize;
112 }
113 self.stride as usize
114 }
115}
116
117#[derive(Debug)]
118pub enum BufferStore<'a, T: Debug> {
119 Borrowed(&'a mut [T]),
120 Owned(Vec<T>),
121}
122
123impl<T: Copy + Debug> BufferStore<'_, T> {
124 #[allow(clippy::should_implement_trait)]
125 pub fn borrow(&self) -> &[T] {
126 match self {
127 Self::Borrowed(p_ref) => p_ref,
128 Self::Owned(vec) => vec,
129 }
130 }
131
132 #[allow(clippy::should_implement_trait)]
133 pub fn borrow_mut(&mut self) -> &mut [T] {
134 match self {
135 Self::Borrowed(p_ref) => p_ref,
136 Self::Owned(vec) => vec,
137 }
138 }
139}
140
141impl<T: Copy + Debug> Index<usize> for BufferStore<'_, T> {
142 type Output = T;
143
144 fn index(&self, index: usize) -> &Self::Output {
145 match self {
146 Self::Borrowed(p_ref) => &p_ref[index],
147 Self::Owned(vec) => &vec[index],
148 }
149 }
150}
151
152impl<T: Copy + Debug> Index<Range<usize>> for BufferStore<'_, T> {
153 type Output = [T];
154
155 fn index(&self, index: Range<usize>) -> &Self::Output {
156 match self {
157 Self::Borrowed(p_ref) => &p_ref[index],
158 Self::Owned(vec) => &vec[index],
159 }
160 }
161}
162
163impl<T: Copy + Debug> Index<RangeFrom<usize>> for BufferStore<'_, T> {
164 type Output = [T];
165
166 fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
167 match self {
168 Self::Borrowed(p_ref) => &p_ref[index],
169 Self::Owned(vec) => &vec[index],
170 }
171 }
172}
173
174#[derive(Debug)]
175pub struct ImageBufferMut<'a, F: Copy + Debug> {
176 pub data: BufferStore<'a, F>,
177 pub width: u32,
178 pub height: u32,
179 pub stride: u32,
180 pub channels: u32,
181}
182
183impl<'a, F: Copy + Debug + Sized> ImageBufferMut<'a, F> {
184 pub fn new(
185 data: BufferStore<'a, F>,
186 width: u32,
187 height: u32,
188 channels: u32,
189 ) -> Result<Self, ColorError> {
190 let buf = Self {
191 data,
192 width,
193 height,
194 stride: width * channels,
195 channels,
196 };
197 buf.validate()?;
198 Ok(buf)
199 }
200
201 pub fn validate(&self) -> Result<(), ColorError> {
202 let min_stride = self
203 .width
204 .checked_mul(self.channels)
205 .ok_or(ColorError::DimensionOverflow)?;
206
207 if self.stride < min_stride {
208 return Err(ColorError::StrideTooNarrow {
209 min: min_stride,
210 given: self.stride,
211 });
212 }
213
214 let required = self
215 .stride()
216 .checked_mul(self.height as usize)
217 .ok_or(ColorError::DimensionOverflow)?;
218
219 if required == 0 {
220 return Err(ColorError::ZeroImageSize);
221 }
222
223 if self.data.borrow().len() < required {
224 return Err(ColorError::BufferTooSmall {
225 expected: required,
226 got: self.data.borrow().len(),
227 });
228 }
229
230 Ok(())
231 }
232
233 pub fn try_match(&self, other: &ImageBufferMut<'_, F>) -> Result<(), ColorError> {
234 if self.width != other.width || self.height != other.height {
235 return Err(ColorError::DimensionMismatch {
236 expected: (self.width, self.height),
237 got: (other.width, other.height),
238 });
239 }
240 Ok(())
241 }
242
243 pub fn try_match_immutable<Z>(&self, other: &ImageBuffer<'_, Z>) -> Result<(), ColorError>
244 where
245 [Z]: ToOwned<Owned = Vec<Z>>,
246 {
247 if self.width != other.width || self.height != other.height {
248 return Err(ColorError::DimensionMismatch {
249 expected: (self.width, self.height),
250 got: (other.width, other.height),
251 });
252 }
253 Ok(())
254 }
255
256 pub fn try_match_immutable_with_channels<Z>(
257 &self,
258 other: &ImageBuffer<'_, Z>,
259 ) -> Result<(), ColorError>
260 where
261 [Z]: ToOwned<Owned = Vec<Z>>,
262 {
263 if self.width != other.width || self.height != other.height {
264 return Err(ColorError::DimensionMismatch {
265 expected: (self.width, self.height),
266 got: (other.width, other.height),
267 });
268 }
269 if self.channels != other.channels {
270 return Err(ColorError::ChannelCountMismatch {
271 expected: self.channels,
272 got: other.channels,
273 });
274 }
275 Ok(())
276 }
277
278 pub fn stride(&self) -> usize {
279 if self.stride == 0 {
280 return self.width as usize * self.channels as usize;
281 }
282 self.stride as usize
283 }
284}