• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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