1use crate::yuv_support::YuvChromaSubsampling;
30use std::error::Error;
31use std::fmt::{Display, Formatter};
32
33#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
34pub struct MismatchedSize {
36 pub expected: usize,
37 pub received: usize,
38}
39
40#[derive(Debug)]
41pub enum YuvError {
43 DestinationSizeMismatch(MismatchedSize),
44 MinimumStrideSizeMismatch(MismatchedSize),
45 PointerOverflow,
46 ZeroBaseSize,
47 LumaPlaneSizeMismatch(MismatchedSize),
48 LumaPlaneMinimumSizeMismatch(MismatchedSize),
49 ChromaPlaneMinimumSizeMismatch(MismatchedSize),
50 ChromaPlaneSizeMismatch(MismatchedSize),
51 PackedFrameSizeMismatch(MismatchedSize),
52 ImagesSizesNotMatch,
53 ImageDimensionsNotMatch,
54}
55
56impl Display for YuvError {
57 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
58 match self {
59 YuvError::ImageDimensionsNotMatch => f.write_str("Buffer must match image dimensions"),
60 YuvError::ImagesSizesNotMatch => {
61 f.write_str("All images size must match in one function")
62 }
63 YuvError::PackedFrameSizeMismatch(size) => f.write_fmt(format_args!(
64 "Packed YUV frame has invalid size, it must be {}, but it was {}",
65 size.expected, size.received
66 )),
67 YuvError::ChromaPlaneSizeMismatch(size) => f.write_fmt(format_args!(
68 "Chroma plane have invalid size, it must be {}, but it was {}",
69 size.expected, size.received
70 )),
71 YuvError::LumaPlaneSizeMismatch(size) => f.write_fmt(format_args!(
72 "Luma plane have invalid size, it must be {}, but it was {}",
73 size.expected, size.received
74 )),
75 YuvError::LumaPlaneMinimumSizeMismatch(size) => f.write_fmt(format_args!(
76 "Luma plane have invalid size, it must be at least {}, but it was {}",
77 size.expected, size.received
78 )),
79 YuvError::ChromaPlaneMinimumSizeMismatch(size) => f.write_fmt(format_args!(
80 "Chroma plane have invalid size, it must be at least {}, but it was {}",
81 size.expected, size.received
82 )),
83 YuvError::PointerOverflow => f.write_str("Image size overflow pointer capabilities"),
84 YuvError::ZeroBaseSize => f.write_str("Zero sized images is not supported"),
85 YuvError::DestinationSizeMismatch(size) => f.write_fmt(format_args!(
86 "Destination size mismatch: expected={}, received={}",
87 size.expected, size.received
88 )),
89 YuvError::MinimumStrideSizeMismatch(size) => f.write_fmt(format_args!(
90 "Minimum stride must have size at least {} but it is {}",
91 size.expected, size.received
92 )),
93 }
94 }
95}
96
97impl Error for YuvError {}
98
99#[inline]
100pub(crate) fn check_overflow_v2(v0: usize, v1: usize) -> Result<(), YuvError> {
101 let (_, overflow) = v0.overflowing_mul(v1);
102 if overflow {
103 return Err(YuvError::PointerOverflow);
104 }
105 Ok(())
106}
107
108#[inline]
109pub(crate) fn check_overflow_v3(v0: usize, v1: usize, v2: usize) -> Result<(), YuvError> {
110 let (product0, overflow) = v0.overflowing_mul(v1);
111 if overflow {
112 return Err(YuvError::PointerOverflow);
113 }
114 let (_, overflow) = product0.overflowing_mul(v2);
115 if overflow {
116 return Err(YuvError::PointerOverflow);
117 }
118 Ok(())
119}
120
121#[inline]
122pub(crate) fn check_rgba_destination<V>(
123 arr: &[V],
124 rgba_stride: u32,
125 width: u32,
126 height: u32,
127 channels: usize,
128) -> Result<(), YuvError> {
129 if width == 0 || height == 0 {
130 return Err(YuvError::ZeroBaseSize);
131 }
132 check_overflow_v3(width as usize, height as usize, channels)?;
133 if arr.len() < rgba_stride as usize * (height as usize - 1) + width as usize * channels {
134 return Err(YuvError::DestinationSizeMismatch(MismatchedSize {
135 expected: rgba_stride as usize * height as usize,
136 received: arr.len(),
137 }));
138 }
139 if (rgba_stride as usize) < (width as usize * channels) {
140 return Err(YuvError::MinimumStrideSizeMismatch(MismatchedSize {
141 expected: width as usize * channels,
142 received: rgba_stride as usize,
143 }));
144 }
145 Ok(())
146}
147
148#[inline]
149pub(crate) fn check_yuv_packed422<V>(
150 data: &[V],
151 stride: u32,
152 width: u32,
153 height: u32,
154) -> Result<(), YuvError> {
155 if width == 0 || height == 0 {
156 return Err(YuvError::ZeroBaseSize);
157 }
158 check_overflow_v2(stride as usize, height as usize)?;
159 check_overflow_v2(width as usize, height as usize)?;
160 let full_size = if width % 2 == 0 {
161 2 * width as usize * height as usize
162 } else {
163 2 * (width as usize + 1) * height as usize
164 };
165 if data.len() != full_size {
166 return Err(YuvError::PackedFrameSizeMismatch(MismatchedSize {
167 expected: stride as usize * height as usize,
168 received: data.len(),
169 }));
170 }
171 Ok(())
172}
173
174#[inline]
175pub(crate) fn check_y8_channel<V>(
176 data: &[V],
177 stride: u32,
178 width: u32,
179 height: u32,
180) -> Result<(), YuvError> {
181 if width == 0 || height == 0 {
182 return Err(YuvError::ZeroBaseSize);
183 }
184 check_overflow_v2(stride as usize, height as usize)?;
185 check_overflow_v2(width as usize, height as usize)?;
186 if (stride as usize * height as usize) < (width as usize * height as usize) {
187 return Err(YuvError::LumaPlaneMinimumSizeMismatch(MismatchedSize {
188 expected: width as usize * height as usize,
189 received: stride as usize * height as usize,
190 }));
191 }
192 if data.len() < stride as usize * (height as usize - 1) + width as usize {
193 return Err(YuvError::LumaPlaneSizeMismatch(MismatchedSize {
194 expected: stride as usize * height as usize,
195 received: data.len(),
196 }));
197 }
198 Ok(())
199}
200
201#[inline]
202pub(crate) fn check_chroma_channel<V>(
203 data: &[V],
204 stride: u32,
205 image_width: u32,
206 image_height: u32,
207 sampling: YuvChromaSubsampling,
208) -> Result<(), YuvError> {
209 if image_width == 0 || image_height == 0 {
210 return Err(YuvError::ZeroBaseSize);
211 }
212 let chroma_min_width = match sampling {
213 YuvChromaSubsampling::Yuv420 | YuvChromaSubsampling::Yuv422 => image_width.div_ceil(2),
214 YuvChromaSubsampling::Yuv444 => image_width,
215 };
216 let chroma_height = match sampling {
217 YuvChromaSubsampling::Yuv420 => image_height.div_ceil(2),
218 YuvChromaSubsampling::Yuv422 | YuvChromaSubsampling::Yuv444 => image_height,
219 };
220 check_overflow_v2(stride as usize, chroma_height as usize)?;
221 check_overflow_v2(chroma_min_width as usize, chroma_height as usize)?;
222 if (stride as usize * chroma_height as usize)
223 < (chroma_min_width as usize * chroma_height as usize)
224 {
225 return Err(YuvError::ChromaPlaneMinimumSizeMismatch(MismatchedSize {
226 expected: chroma_min_width as usize * chroma_height as usize,
227 received: stride as usize * chroma_height as usize,
228 }));
229 }
230 if data.len() < stride as usize * (chroma_height as usize - 1) + chroma_min_width as usize {
231 return Err(YuvError::ChromaPlaneMinimumSizeMismatch(MismatchedSize {
232 expected: stride as usize * (chroma_height as usize - 1) + chroma_min_width as usize,
233 received: data.len(),
234 }));
235 }
236 Ok(())
237}
238
239#[inline]
240pub(crate) fn check_interleaved_chroma_channel<V>(
241 data: &[V],
242 stride: u32,
243 image_width: u32,
244 image_height: u32,
245 sampling: YuvChromaSubsampling,
246) -> Result<(), YuvError> {
247 if image_width == 0 || image_height == 0 {
248 return Err(YuvError::ZeroBaseSize);
249 }
250 let chroma_min_width = match sampling {
251 YuvChromaSubsampling::Yuv420 | YuvChromaSubsampling::Yuv422 => image_width.div_ceil(2) * 2,
252 YuvChromaSubsampling::Yuv444 => image_width * 2,
253 };
254 let chroma_height = match sampling {
255 YuvChromaSubsampling::Yuv420 => image_height.div_ceil(2),
256 YuvChromaSubsampling::Yuv422 | YuvChromaSubsampling::Yuv444 => image_height,
257 };
258 check_overflow_v2(stride as usize, chroma_height as usize)?;
259 check_overflow_v2(chroma_min_width as usize, chroma_height as usize)?;
260 if (stride as usize * chroma_height as usize)
261 < (chroma_min_width as usize * chroma_height as usize)
262 {
263 return Err(YuvError::ChromaPlaneMinimumSizeMismatch(MismatchedSize {
264 expected: chroma_min_width as usize * chroma_height as usize,
265 received: stride as usize * chroma_height as usize,
266 }));
267 }
268 if stride as usize * chroma_height as usize != data.len()
269 || chroma_min_width as usize * chroma_height as usize != data.len()
270 {
271 return Err(YuvError::ChromaPlaneSizeMismatch(MismatchedSize {
272 expected: stride as usize * chroma_height as usize,
273 received: data.len(),
274 }));
275 }
276 Ok(())
277}