1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 pub mod io;
16 pub mod pixels;
17 pub mod stream;
18
19 use crate::parser::mp4box::*;
20 use crate::utils::*;
21 use crate::*;
22
23 use std::num::NonZero;
24 use std::ops::Range;
25
26 // 'clap' fractions do not follow this pattern: both numerators and denominators
27 // are used as i32, but they are signalled as u32 according to the specification
28 // as of 2024. This may be fixed in later versions of the specification, see
29 // https://github.com/AOMediaCodec/libavif/pull/1749#discussion_r1391612932.
30 #[derive(Clone, Copy, Debug, Default)]
31 pub struct IFraction(pub i32, pub i32);
32
33 impl TryFrom<UFraction> for IFraction {
34 type Error = AvifError;
35
try_from(uf: UFraction) -> AvifResult<IFraction>36 fn try_from(uf: UFraction) -> AvifResult<IFraction> {
37 Ok(IFraction(uf.0 as i32, i32_from_u32(uf.1)?))
38 }
39 }
40
41 impl IFraction {
gcd(a: i32, b: i32) -> i3242 fn gcd(a: i32, b: i32) -> i32 {
43 let mut a = if a < 0 { -a as i64 } else { a as i64 };
44 let mut b = if b < 0 { -b as i64 } else { b as i64 };
45 while b != 0 {
46 let r = a % b;
47 a = b;
48 b = r;
49 }
50 a as i32
51 }
52
simplified(n: i32, d: i32) -> Self53 pub(crate) fn simplified(n: i32, d: i32) -> Self {
54 let mut fraction = IFraction(n, d);
55 fraction.simplify();
56 fraction
57 }
58
simplify(&mut self)59 pub(crate) fn simplify(&mut self) {
60 let gcd = Self::gcd(self.0, self.1);
61 if gcd > 1 {
62 self.0 /= gcd;
63 self.1 /= gcd;
64 }
65 }
66
get_i32(&self) -> i3267 pub(crate) fn get_i32(&self) -> i32 {
68 assert!(self.1 != 0);
69 self.0 / self.1
70 }
71
get_u32(&self) -> AvifResult<u32>72 pub(crate) fn get_u32(&self) -> AvifResult<u32> {
73 u32_from_i32(self.get_i32())
74 }
75
is_integer(&self) -> bool76 pub(crate) fn is_integer(&self) -> bool {
77 self.0 % self.1 == 0
78 }
79
common_denominator(&mut self, val: &mut IFraction) -> AvifResult<()>80 fn common_denominator(&mut self, val: &mut IFraction) -> AvifResult<()> {
81 self.simplify();
82 if self.1 == val.1 {
83 return Ok(());
84 }
85 let self_d = self.1;
86 self.0 = self
87 .0
88 .checked_mul(val.1)
89 .ok_or(AvifError::UnknownError("".into()))?;
90 self.1 = self
91 .1
92 .checked_mul(val.1)
93 .ok_or(AvifError::UnknownError("".into()))?;
94 val.0 = val
95 .0
96 .checked_mul(self_d)
97 .ok_or(AvifError::UnknownError("".into()))?;
98 val.1 = val
99 .1
100 .checked_mul(self_d)
101 .ok_or(AvifError::UnknownError("".into()))?;
102 Ok(())
103 }
104
add(&mut self, val: &IFraction) -> AvifResult<()>105 pub(crate) fn add(&mut self, val: &IFraction) -> AvifResult<()> {
106 let mut val = *val;
107 val.simplify();
108 self.common_denominator(&mut val)?;
109 self.0 = self
110 .0
111 .checked_add(val.0)
112 .ok_or(AvifError::UnknownError("".into()))?;
113 self.simplify();
114 Ok(())
115 }
116
sub(&mut self, val: &IFraction) -> AvifResult<()>117 pub(crate) fn sub(&mut self, val: &IFraction) -> AvifResult<()> {
118 let mut val = *val;
119 val.simplify();
120 self.common_denominator(&mut val)?;
121 self.0 = self
122 .0
123 .checked_sub(val.0)
124 .ok_or(AvifError::UnknownError("".into()))?;
125 self.simplify();
126 Ok(())
127 }
128 }
129
130 macro_rules! conversion_function {
131 ($func:ident, $to: ident, $from:ty) => {
132 pub(crate) fn $func(value: $from) -> AvifResult<$to> {
133 $to::try_from(value).or(Err(AvifError::BmffParseFailed("".into())))
134 }
135 };
136 }
137
138 conversion_function!(usize_from_u64, usize, u64);
139 conversion_function!(usize_from_u32, usize, u32);
140 conversion_function!(usize_from_u16, usize, u16);
141 #[cfg(feature = "android_mediacodec")]
142 conversion_function!(usize_from_isize, usize, isize);
143 conversion_function!(u64_from_usize, u64, usize);
144 conversion_function!(u32_from_usize, u32, usize);
145 conversion_function!(u32_from_u64, u32, u64);
146 conversion_function!(u32_from_i32, u32, i32);
147 conversion_function!(i32_from_u32, i32, u32);
148 #[cfg(feature = "android_mediacodec")]
149 conversion_function!(isize_from_i32, isize, i32);
150 #[cfg(any(feature = "capi", feature = "android_mediacodec"))]
151 conversion_function!(isize_from_u32, isize, u32);
152 conversion_function!(isize_from_usize, isize, usize);
153 #[cfg(feature = "android_mediacodec")]
154 conversion_function!(i32_from_usize, i32, usize);
155
156 macro_rules! clamp_function {
157 ($func:ident, $type:ty) => {
158 pub(crate) fn $func(value: $type, low: $type, high: $type) -> $type {
159 if value < low {
160 low
161 } else if value > high {
162 high
163 } else {
164 value
165 }
166 }
167 };
168 }
169
170 clamp_function!(clamp_u16, u16);
171 clamp_function!(clamp_f32, f32);
172 clamp_function!(clamp_i32, i32);
173
174 macro_rules! round2_function {
175 ($func:ident, $type:ty) => {
176 pub(crate) fn $func(value: $type) -> $type {
177 if value % 2 == 0 {
178 value
179 } else {
180 value + 1
181 }
182 }
183 };
184 }
185
186 #[cfg(feature = "capi")]
187 round2_function!(round2_u32, u32);
188 round2_function!(round2_usize, usize);
189
190 macro_rules! find_property {
191 ($properties:expr, $property_name:ident) => {
192 $properties.iter().find_map(|p| match p {
193 ItemProperty::$property_name(value) => Some(value.clone()),
194 _ => None,
195 })
196 };
197 }
198
199 // Returns the colr nclx property. Returns an error if there are multiple ones.
find_nclx(properties: &[ItemProperty]) -> AvifResult<Option<&Nclx>>200 pub(crate) fn find_nclx(properties: &[ItemProperty]) -> AvifResult<Option<&Nclx>> {
201 let mut single_nclx: Option<&Nclx> = None;
202 for property in properties {
203 if let ItemProperty::ColorInformation(ColorInformation::Nclx(nclx)) = property {
204 if single_nclx.is_some() {
205 return Err(AvifError::BmffParseFailed(
206 "multiple nclx were found".into(),
207 ));
208 }
209 single_nclx = Some(nclx);
210 }
211 }
212 Ok(single_nclx)
213 }
214
215 // Returns the colr icc property. Returns an error if there are multiple ones.
find_icc(properties: &[ItemProperty]) -> AvifResult<Option<&Vec<u8>>>216 pub(crate) fn find_icc(properties: &[ItemProperty]) -> AvifResult<Option<&Vec<u8>>> {
217 let mut single_icc: Option<&Vec<u8>> = None;
218 for property in properties {
219 if let ItemProperty::ColorInformation(ColorInformation::Icc(icc)) = property {
220 if single_icc.is_some() {
221 return Err(AvifError::BmffParseFailed("multiple icc were found".into()));
222 }
223 single_icc = Some(icc);
224 }
225 }
226 Ok(single_icc)
227 }
228
check_limits( width: u32, height: u32, size_limit: Option<NonZero<u32>>, dimension_limit: Option<NonZero<u32>>, ) -> bool229 pub(crate) fn check_limits(
230 width: u32,
231 height: u32,
232 size_limit: Option<NonZero<u32>>,
233 dimension_limit: Option<NonZero<u32>>,
234 ) -> bool {
235 if height == 0 {
236 return false;
237 }
238 if let Some(limit) = size_limit {
239 if width > limit.get() / height {
240 return false;
241 }
242 }
243 if let Some(limit) = dimension_limit {
244 if width > limit.get() || height > limit.get() {
245 return false;
246 }
247 }
248 true
249 }
250
limited_to_full(min: i32, max: i32, full: i32, v: u16) -> u16251 fn limited_to_full(min: i32, max: i32, full: i32, v: u16) -> u16 {
252 let v = v as i32;
253 clamp_i32(
254 (((v - min) * full) + ((max - min) / 2)) / (max - min),
255 0,
256 full,
257 ) as u16
258 }
259
limited_to_full_y(depth: u8, v: u16) -> u16260 pub(crate) fn limited_to_full_y(depth: u8, v: u16) -> u16 {
261 match depth {
262 8 => limited_to_full(16, 235, 255, v),
263 10 => limited_to_full(64, 940, 1023, v),
264 12 => limited_to_full(256, 3760, 4095, v),
265 _ => 0,
266 }
267 }
268
create_vec_exact<T>(size: usize) -> AvifResult<Vec<T>>269 pub(crate) fn create_vec_exact<T>(size: usize) -> AvifResult<Vec<T>> {
270 let mut v = Vec::<T>::new();
271 let allocation_size = size
272 .checked_mul(std::mem::size_of::<T>())
273 .ok_or(AvifError::OutOfMemory)?;
274 // TODO: b/342251590 - Do not request allocations of more than what is allowed in Chromium's
275 // partition allocator. This is the allowed limit in the chromium fuzzers. The value comes
276 // from:
277 // https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/src/partition_alloc/partition_alloc_constants.h;l=433-440;drc=c0265133106c7647e90f9aaa4377d28190b1a6a9.
278 // Requesting an allocation larger than this value will cause the fuzzers to crash instead of
279 // returning null. Remove this check once that behavior is fixed.
280 if u64_from_usize(allocation_size)? >= 2_145_386_496 {
281 return Err(AvifError::OutOfMemory);
282 }
283 if v.try_reserve_exact(size).is_err() {
284 return Err(AvifError::OutOfMemory);
285 }
286 Ok(v)
287 }
288
289 #[cfg(test)]
assert_eq_f32_array(a: &[f32], b: &[f32])290 pub(crate) fn assert_eq_f32_array(a: &[f32], b: &[f32]) {
291 assert_eq!(a.len(), b.len());
292 for i in 0..a.len() {
293 assert!((a[i] - b[i]).abs() <= std::f32::EPSILON);
294 }
295 }
296
check_slice_range(len: usize, range: &Range<usize>) -> AvifResult<()>297 pub(crate) fn check_slice_range(len: usize, range: &Range<usize>) -> AvifResult<()> {
298 if range.start >= len || range.end > len {
299 return Err(AvifError::NoContent);
300 }
301 Ok(())
302 }
303
is_auxiliary_type_alpha(aux_type: &str) -> bool304 pub(crate) fn is_auxiliary_type_alpha(aux_type: &str) -> bool {
305 aux_type == "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"
306 || aux_type == "urn:mpeg:hevc:2015:auxid:1"
307 }
308
validate_grid_image_dimensions(image: &Image, grid: &Grid) -> AvifResult<()>309 pub(crate) fn validate_grid_image_dimensions(image: &Image, grid: &Grid) -> AvifResult<()> {
310 if checked_mul!(image.width, grid.columns)? < grid.width
311 || checked_mul!(image.height, grid.rows)? < grid.height
312 {
313 return Err(AvifError::InvalidImageGrid(
314 "Grid image tiles do not completely cover the image (HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1)".into(),
315 ));
316 }
317 if checked_mul!(image.width, grid.columns)? < grid.width
318 || checked_mul!(image.height, grid.rows)? < grid.height
319 {
320 return Err(AvifError::InvalidImageGrid(
321 "Grid image tiles do not completely cover the image (HEIF (ISO/IEC 23008-12:2017), \
322 Section 6.6.2.3.1)"
323 .into(),
324 ));
325 }
326 if checked_mul!(image.width, grid.columns - 1)? >= grid.width
327 || checked_mul!(image.height, grid.rows - 1)? >= grid.height
328 {
329 return Err(AvifError::InvalidImageGrid(
330 "Grid image tiles in the rightmost column and bottommost row do not overlap the \
331 reconstructed image grid canvas. See MIAF (ISO/IEC 23000-22:2019), Section \
332 7.3.11.4.2, Figure 2"
333 .into(),
334 ));
335 }
336 // ISO/IEC 23000-22:2019, Section 7.3.11.4.2:
337 // - the tile_width shall be greater than or equal to 64, and should be a multiple of 64
338 // - the tile_height shall be greater than or equal to 64, and should be a multiple of 64
339 // The "should" part is ignored here.
340 if image.width < 64 || image.height < 64 {
341 return Err(AvifError::InvalidImageGrid(format!(
342 "Grid image tile width ({}) or height ({}) cannot be smaller than 64. See MIAF \
343 (ISO/IEC 23000-22:2019), Section 7.3.11.4.2",
344 image.width, image.height
345 )));
346 }
347 // ISO/IEC 23000-22:2019, Section 7.3.11.4.2:
348 // - when the images are in the 4:2:2 chroma sampling format the horizontal tile offsets
349 // and widths, and the output width, shall be even numbers;
350 // - when the images are in the 4:2:0 chroma sampling format both the horizontal and
351 // vertical tile offsets and widths, and the output width and height, shall be even
352 // numbers.
353 if ((image.yuv_format == PixelFormat::Yuv420 || image.yuv_format == PixelFormat::Yuv422)
354 && (grid.width % 2 != 0 || image.width % 2 != 0))
355 || (image.yuv_format == PixelFormat::Yuv420
356 && (grid.height % 2 != 0 || image.height % 2 != 0))
357 {
358 return Err(AvifError::InvalidImageGrid(format!(
359 "Grid image width ({}) or height ({}) or tile width ({}) or height ({}) shall be \
360 even if chroma is subsampled in that dimension. See MIAF \
361 (ISO/IEC 23000-22:2019), Section 7.3.11.4.2",
362 grid.width, grid.height, image.width, image.height
363 )));
364 }
365 Ok(())
366 }
367