1use crate::{InvalidNumberOfPlanesError, StrictApi as _, plane_decs::*, planes::read_planes};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
5#[non_exhaustive]
6pub enum PixelFormat {
7 #[cfg(feature = "I420")]
9 I420,
10
11 #[cfg(feature = "I422")]
13 I422,
14
15 #[cfg(feature = "I444")]
17 I444,
18
19 #[cfg(feature = "I010")]
21 I010,
22
23 #[cfg(feature = "I012")]
25 I012,
26
27 #[cfg(feature = "I210")]
29 I210,
30
31 #[cfg(feature = "I212")]
33 I212,
34
35 #[cfg(feature = "I410")]
37 I410,
38
39 #[cfg(feature = "I412")]
41 I412,
42
43 #[cfg(feature = "NV12")]
45 NV12,
46
47 #[cfg(feature = "P010")]
49 P010,
50
51 #[cfg(feature = "P012")]
53 P012,
54
55 #[cfg(feature = "YUYV")]
57 YUYV,
58
59 #[cfg(feature = "RGBA")]
61 RGBA,
62
63 #[cfg(feature = "BGRA")]
65 BGRA,
66
67 #[cfg(feature = "ARGB")]
69 ARGB,
70
71 #[cfg(feature = "ABGR")]
73 ABGR,
74
75 #[cfg(feature = "RGB")]
77 RGB,
78
79 #[cfg(feature = "BGR")]
81 BGR,
82}
83
84impl PixelFormat {
85 #[deny(clippy::arithmetic_side_effects)]
89 pub fn buffer_size(self, width: usize, height: usize) -> usize {
90 fn buffer_size(planes: &[PlaneDesc], width: usize, height: usize) -> usize {
91 let mut size = 0;
92
93 for plane in planes {
94 let w = plane.width_op.op(width);
95 let h = plane.height_op.op(height);
96
97 size = size.strict_add_(w.strict_mul_(h).strict_mul_(plane.bytes_per_primitive));
98 }
99
100 size
101 }
102
103 buffer_size(self.plane_desc(), width, height)
104 }
105
106 #[deny(clippy::arithmetic_side_effects)]
108 pub fn packed_strides(self, width: usize) -> Vec<usize> {
109 fn packed_strides(planes: &[PlaneDesc], width: usize) -> Vec<usize> {
110 planes
111 .iter()
112 .map(|desc| desc.packed_stride(width))
113 .collect()
114 }
115
116 packed_strides(self.plane_desc(), width)
117 }
118
119 #[deny(clippy::arithmetic_side_effects)]
121 pub fn bounds_check<'a>(
122 self,
123 planes: impl Iterator<Item = (&'a [u8], usize)>,
124 width: usize,
125 height: usize,
126 ) -> Result<(), BoundsCheckError> {
127 use PixelFormat::*;
128
129 fn bounds_check<const N: usize>(
130 planes: [PlaneDesc; N],
131 got: [(&[u8], usize); N],
132 width: usize,
133 height: usize,
134 ) -> Result<(), BoundsCheckError> {
135 for (i, (plane, (slice, stride))) in planes.into_iter().zip(got).enumerate() {
136 let min_stride = plane.packed_stride(width);
138
139 if min_stride > stride {
140 return Err(BoundsCheckError::InvalidStride {
141 plane: i,
142 minimum: min_stride,
143 got: stride,
144 });
145 }
146
147 let min_len = stride.strict_mul_(plane.height_op.op(height));
149
150 if min_len > slice.len() {
151 return Err(BoundsCheckError::InvalidPlaneSize {
152 plane: i,
153 minimum: min_len,
154 got: slice.len(),
155 });
156 }
157 }
158
159 Ok(())
160 }
161
162 match self {
163 #[cfg(feature = "I420")]
164 I420 => bounds_check(I420_PLANES, read_planes(planes)?, width, height),
165 #[cfg(feature = "I422")]
166 I422 => bounds_check(I422_PLANES, read_planes(planes)?, width, height),
167 #[cfg(feature = "I444")]
168 I444 => bounds_check(I444_PLANES, read_planes(planes)?, width, height),
169 #[cfg(feature = "I010")]
170 I010 => bounds_check(I01X_PLANES, read_planes(planes)?, width, height),
171 #[cfg(feature = "I012")]
172 I012 => bounds_check(I01X_PLANES, read_planes(planes)?, width, height),
173 #[cfg(feature = "I210")]
174 I210 => bounds_check(I21X_PLANES, read_planes(planes)?, width, height),
175 #[cfg(feature = "I212")]
176 I212 => bounds_check(I21X_PLANES, read_planes(planes)?, width, height),
177 #[cfg(feature = "I410")]
178 I410 => bounds_check(I41X_PLANES, read_planes(planes)?, width, height),
179 #[cfg(feature = "I412")]
180 I412 => bounds_check(I41X_PLANES, read_planes(planes)?, width, height),
181 #[cfg(feature = "NV12")]
182 NV12 => bounds_check(NV12_PLANES, read_planes(planes)?, width, height),
183 #[cfg(feature = "P010")]
184 P010 => bounds_check(P01X_PLANES, read_planes(planes)?, width, height),
185 #[cfg(feature = "P012")]
186 P012 => bounds_check(P01X_PLANES, read_planes(planes)?, width, height),
187 #[cfg(feature = "YUYV")]
188 YUYV => bounds_check(YUYV_PLANES, read_planes(planes)?, width, height),
189 #[cfg(feature = "RGBA")]
190 RGBA => bounds_check(RGBA_PLANES, read_planes(planes)?, width, height),
191 #[cfg(feature = "BGRA")]
192 BGRA => bounds_check(RGBA_PLANES, read_planes(planes)?, width, height),
193 #[cfg(feature = "ARGB")]
194 ARGB => bounds_check(RGBA_PLANES, read_planes(planes)?, width, height),
195 #[cfg(feature = "ABGR")]
196 ABGR => bounds_check(RGBA_PLANES, read_planes(planes)?, width, height),
197 #[cfg(feature = "RGB")]
198 RGB => bounds_check(RGB_PLANES, read_planes(planes)?, width, height),
199 #[cfg(feature = "BGR")]
200 BGR => bounds_check(RGB_PLANES, read_planes(planes)?, width, height),
201 }
202 }
203
204 pub fn bits_per_component(&self) -> usize {
205 match self {
206 #[cfg(feature = "I420")]
207 PixelFormat::I420 => 8,
208 #[cfg(feature = "I422")]
209 PixelFormat::I422 => 8,
210 #[cfg(feature = "I444")]
211 PixelFormat::I444 => 8,
212 #[cfg(feature = "I010")]
213 PixelFormat::I010 => 10,
214 #[cfg(feature = "I012")]
215 PixelFormat::I012 => 12,
216 #[cfg(feature = "I210")]
217 PixelFormat::I210 => 10,
218 #[cfg(feature = "I212")]
219 PixelFormat::I212 => 12,
220 #[cfg(feature = "I410")]
221 PixelFormat::I410 => 10,
222 #[cfg(feature = "I412")]
223 PixelFormat::I412 => 12,
224 #[cfg(feature = "NV12")]
225 PixelFormat::NV12 => 8,
226 #[cfg(feature = "P010")]
227 PixelFormat::P010 => 10,
228 #[cfg(feature = "P012")]
229 PixelFormat::P012 => 12,
230 #[cfg(feature = "YUYV")]
231 PixelFormat::YUYV => 8,
232 #[cfg(feature = "RGBA")]
233 PixelFormat::RGBA => 8,
234 #[cfg(feature = "BGRA")]
235 PixelFormat::BGRA => 8,
236 #[cfg(feature = "ARGB")]
237 PixelFormat::ARGB => 8,
238 #[cfg(feature = "ABGR")]
239 PixelFormat::ABGR => 8,
240 #[cfg(feature = "RGB")]
241 PixelFormat::RGB => 8,
242 #[cfg(feature = "BGR")]
243 PixelFormat::BGR => 8,
244 }
245 }
246
247 pub(crate) fn plane_desc(&self) -> &'static [PlaneDesc] {
248 use PixelFormat::*;
249
250 match self {
251 #[cfg(feature = "I420")]
252 I420 => &I420_PLANES,
253 #[cfg(feature = "I422")]
254 I422 => &I422_PLANES,
255 #[cfg(feature = "I444")]
256 I444 => &I444_PLANES,
257 #[cfg(feature = "I010")]
258 I010 => &I01X_PLANES,
259 #[cfg(feature = "I012")]
260 I012 => &I01X_PLANES,
261 #[cfg(feature = "I210")]
262 I210 => &I21X_PLANES,
263 #[cfg(feature = "I212")]
264 I212 => &I21X_PLANES,
265 #[cfg(feature = "I410")]
266 I410 => &I41X_PLANES,
267 #[cfg(feature = "I412")]
268 I412 => &I41X_PLANES,
269 #[cfg(feature = "NV12")]
270 NV12 => &NV12_PLANES,
271 #[cfg(feature = "P010")]
272 P010 => &P01X_PLANES,
273 #[cfg(feature = "P012")]
274 P012 => &P01X_PLANES,
275 #[cfg(feature = "YUYV")]
276 YUYV => &YUYV_PLANES,
277 #[cfg(feature = "RGBA")]
278 RGBA => &RGBA_PLANES,
279 #[cfg(feature = "BGRA")]
280 BGRA => &RGBA_PLANES,
281 #[cfg(feature = "ARGB")]
282 ARGB => &RGBA_PLANES,
283 #[cfg(feature = "ABGR")]
284 ABGR => &RGBA_PLANES,
285 #[cfg(feature = "RGB")]
286 RGB => &RGB_PLANES,
287 #[cfg(feature = "BGR")]
288 BGR => &RGB_PLANES,
289 }
290 }
291
292 pub fn variants() -> impl IntoIterator<Item = Self> {
293 use PixelFormat::*;
294
295 [
296 #[cfg(feature = "I420")]
297 I420,
298 #[cfg(feature = "I422")]
299 I422,
300 #[cfg(feature = "I444")]
301 I444,
302 #[cfg(feature = "I010")]
303 I010,
304 #[cfg(feature = "I012")]
305 I012,
306 #[cfg(feature = "I210")]
307 I210,
308 #[cfg(feature = "I212")]
309 I212,
310 #[cfg(feature = "I410")]
311 I410,
312 #[cfg(feature = "I412")]
313 I412,
314 #[cfg(feature = "NV12")]
315 NV12,
316 #[cfg(feature = "P010")]
317 P010,
318 #[cfg(feature = "P012")]
319 P012,
320 #[cfg(feature = "YUYV")]
321 YUYV,
322 #[cfg(feature = "RGBA")]
323 RGBA,
324 #[cfg(feature = "BGRA")]
325 BGRA,
326 #[cfg(feature = "ARGB")]
327 ARGB,
328 #[cfg(feature = "ABGR")]
329 ABGR,
330 #[cfg(feature = "RGB")]
331 RGB,
332 #[cfg(feature = "BGR")]
333 BGR,
334 ]
335 }
336}
337
338#[derive(Debug, thiserror::Error)]
339pub enum BoundsCheckError {
340 #[error(transparent)]
341 InvalidNumberOfPlanes(#[from] InvalidNumberOfPlanesError),
342
343 #[error("invalid stride at plane {plane}, expected it to be at least {minimum}, but got {got}")]
344 InvalidStride {
345 plane: usize,
346 minimum: usize,
347 got: usize,
348 },
349
350 #[error(
351 "invalid plane size at plane {plane}, expected it to be at least {minimum}, but got {got}"
352 )]
353 InvalidPlaneSize {
354 plane: usize,
355 minimum: usize,
356 got: usize,
357 },
358}