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::*;
21
22 use std::ops::Range;
23
24 // Some HEIF fractional fields can be negative, hence Fraction and UFraction.
25 // The denominator is always unsigned.
26
27 /// cbindgen:field-names=[n,d]
28 #[derive(Clone, Copy, Debug, Default)]
29 #[repr(C)]
30 pub struct Fraction(pub i32, pub u32);
31
32 /// cbindgen:field-names=[n,d]
33 #[derive(Clone, Copy, Debug, Default, PartialEq)]
34 #[repr(C)]
35 pub struct UFraction(pub u32, pub u32);
36
37 // 'clap' fractions do not follow this pattern: both numerators and denominators
38 // are used as i32, but they are signalled as u32 according to the specification
39 // as of 2024. This may be fixed in later versions of the specification, see
40 // https://github.com/AOMediaCodec/libavif/pull/1749#discussion_r1391612932.
41 #[derive(Clone, Copy, Debug, Default)]
42 pub struct IFraction(pub i32, pub i32);
43
44 impl TryFrom<UFraction> for IFraction {
45 type Error = AvifError;
46
try_from(uf: UFraction) -> AvifResult<IFraction>47 fn try_from(uf: UFraction) -> AvifResult<IFraction> {
48 Ok(IFraction(uf.0 as i32, i32_from_u32(uf.1)?))
49 }
50 }
51
52 impl IFraction {
gcd(a: i32, b: i32) -> i3253 fn gcd(a: i32, b: i32) -> i32 {
54 let mut a = if a < 0 { -a as i64 } else { a as i64 };
55 let mut b = if b < 0 { -b as i64 } else { b as i64 };
56 while b != 0 {
57 let r = a % b;
58 a = b;
59 b = r;
60 }
61 a as i32
62 }
63
simplified(n: i32, d: i32) -> Self64 pub fn simplified(n: i32, d: i32) -> Self {
65 let mut fraction = IFraction(n, d);
66 fraction.simplify();
67 fraction
68 }
69
simplify(&mut self)70 pub fn simplify(&mut self) {
71 let gcd = Self::gcd(self.0, self.1);
72 if gcd > 1 {
73 self.0 /= gcd;
74 self.1 /= gcd;
75 }
76 }
77
get_i32(&self) -> i3278 pub fn get_i32(&self) -> i32 {
79 assert!(self.1 != 0);
80 self.0 / self.1
81 }
82
get_u32(&self) -> AvifResult<u32>83 pub fn get_u32(&self) -> AvifResult<u32> {
84 u32_from_i32(self.get_i32())
85 }
86
is_integer(&self) -> bool87 pub fn is_integer(&self) -> bool {
88 self.0 % self.1 == 0
89 }
90
common_denominator(&mut self, val: &mut IFraction) -> AvifResult<()>91 fn common_denominator(&mut self, val: &mut IFraction) -> AvifResult<()> {
92 self.simplify();
93 if self.1 == val.1 {
94 return Ok(());
95 }
96 let self_d = self.1;
97 self.0 = self
98 .0
99 .checked_mul(val.1)
100 .ok_or(AvifError::UnknownError("".into()))?;
101 self.1 = self
102 .1
103 .checked_mul(val.1)
104 .ok_or(AvifError::UnknownError("".into()))?;
105 val.0 = val
106 .0
107 .checked_mul(self_d)
108 .ok_or(AvifError::UnknownError("".into()))?;
109 val.1 = val
110 .1
111 .checked_mul(self_d)
112 .ok_or(AvifError::UnknownError("".into()))?;
113 Ok(())
114 }
115
add(&mut self, val: &IFraction) -> AvifResult<()>116 pub fn add(&mut self, val: &IFraction) -> AvifResult<()> {
117 let mut val = *val;
118 val.simplify();
119 self.common_denominator(&mut val)?;
120 self.0 = self
121 .0
122 .checked_add(val.0)
123 .ok_or(AvifError::UnknownError("".into()))?;
124 self.simplify();
125 Ok(())
126 }
127
sub(&mut self, val: &IFraction) -> AvifResult<()>128 pub fn sub(&mut self, val: &IFraction) -> AvifResult<()> {
129 let mut val = *val;
130 val.simplify();
131 self.common_denominator(&mut val)?;
132 self.0 = self
133 .0
134 .checked_sub(val.0)
135 .ok_or(AvifError::UnknownError("".into()))?;
136 self.simplify();
137 Ok(())
138 }
139 }
140
141 macro_rules! conversion_function {
142 ($func:ident, $to: ident, $from:ty) => {
143 pub fn $func(value: $from) -> AvifResult<$to> {
144 $to::try_from(value).or(Err(AvifError::BmffParseFailed("".into())))
145 }
146 };
147 }
148
149 conversion_function!(usize_from_u64, usize, u64);
150 conversion_function!(usize_from_u32, usize, u32);
151 conversion_function!(usize_from_u16, usize, u16);
152 #[cfg(feature = "android_mediacodec")]
153 conversion_function!(usize_from_isize, usize, isize);
154 conversion_function!(u64_from_usize, u64, usize);
155 conversion_function!(u32_from_usize, u32, usize);
156 conversion_function!(u32_from_u64, u32, u64);
157 conversion_function!(u32_from_i32, u32, i32);
158 conversion_function!(i32_from_u32, i32, u32);
159 #[cfg(feature = "android_mediacodec")]
160 conversion_function!(isize_from_i32, isize, i32);
161 #[cfg(feature = "capi")]
162 conversion_function!(isize_from_u32, isize, u32);
163 conversion_function!(isize_from_usize, isize, usize);
164 #[cfg(feature = "android_mediacodec")]
165 conversion_function!(i32_from_usize, i32, usize);
166
167 macro_rules! clamp_function {
168 ($func:ident, $type:ty) => {
169 pub fn $func(value: $type, low: $type, high: $type) -> $type {
170 if value < low {
171 low
172 } else if value > high {
173 high
174 } else {
175 value
176 }
177 }
178 };
179 }
180
181 clamp_function!(clamp_u16, u16);
182 clamp_function!(clamp_f32, f32);
183 clamp_function!(clamp_i32, i32);
184
185 // Returns the colr nclx property. Returns an error if there are multiple ones.
find_nclx(properties: &[ItemProperty]) -> AvifResult<Option<&Nclx>>186 pub fn find_nclx(properties: &[ItemProperty]) -> AvifResult<Option<&Nclx>> {
187 let mut single_nclx: Option<&Nclx> = None;
188 for property in properties {
189 if let ItemProperty::ColorInformation(ColorInformation::Nclx(nclx)) = property {
190 if single_nclx.is_some() {
191 return Err(AvifError::BmffParseFailed(
192 "multiple nclx were found".into(),
193 ));
194 }
195 single_nclx = Some(nclx);
196 }
197 }
198 Ok(single_nclx)
199 }
200
201 // Returns the colr icc property. Returns an error if there are multiple ones.
find_icc(properties: &[ItemProperty]) -> AvifResult<Option<&Vec<u8>>>202 pub fn find_icc(properties: &[ItemProperty]) -> AvifResult<Option<&Vec<u8>>> {
203 let mut single_icc: Option<&Vec<u8>> = None;
204 for property in properties {
205 if let ItemProperty::ColorInformation(ColorInformation::Icc(icc)) = property {
206 if single_icc.is_some() {
207 return Err(AvifError::BmffParseFailed("multiple icc were found".into()));
208 }
209 single_icc = Some(icc);
210 }
211 }
212 Ok(single_icc)
213 }
214
check_limits(width: u32, height: u32, size_limit: u32, dimension_limit: u32) -> bool215 pub fn check_limits(width: u32, height: u32, size_limit: u32, dimension_limit: u32) -> bool {
216 if height == 0 {
217 return false;
218 }
219 if width > size_limit / height {
220 return false;
221 }
222 if dimension_limit != 0 && (width > dimension_limit || height > dimension_limit) {
223 return false;
224 }
225 true
226 }
227
limited_to_full(min: i32, max: i32, full: i32, v: u16) -> u16228 fn limited_to_full(min: i32, max: i32, full: i32, v: u16) -> u16 {
229 let v = v as i32;
230 clamp_i32(
231 (((v - min) * full) + ((max - min) / 2)) / (max - min),
232 0,
233 full,
234 ) as u16
235 }
236
limited_to_full_y(depth: u8, v: u16) -> u16237 pub fn limited_to_full_y(depth: u8, v: u16) -> u16 {
238 match depth {
239 8 => limited_to_full(16, 235, 255, v),
240 10 => limited_to_full(64, 940, 1023, v),
241 12 => limited_to_full(256, 3760, 4095, v),
242 _ => 0,
243 }
244 }
245
create_vec_exact<T>(size: usize) -> AvifResult<Vec<T>>246 pub fn create_vec_exact<T>(size: usize) -> AvifResult<Vec<T>> {
247 let mut v = Vec::<T>::new();
248 let allocation_size = size
249 .checked_mul(std::mem::size_of::<T>())
250 .ok_or(AvifError::OutOfMemory)?;
251 // TODO: b/342251590 - Do not request allocations of more than what is allowed in Chromium's
252 // partition allocator. This is the allowed limit in the chromium fuzzers. The value comes
253 // from:
254 // https://source.chromium.org/chromium/chromium/src/+/main:base/allocator/partition_allocator/src/partition_alloc/partition_alloc_constants.h;l=433-440;drc=c0265133106c7647e90f9aaa4377d28190b1a6a9.
255 // Requesting an allocation larger than this value will cause the fuzzers to crash instead of
256 // returning null. Remove this check once that behavior is fixed.
257 if u64_from_usize(allocation_size)? >= 2_145_386_496 {
258 return Err(AvifError::OutOfMemory);
259 }
260 if v.try_reserve_exact(size).is_err() {
261 return Err(AvifError::OutOfMemory);
262 }
263 Ok(v)
264 }
265
266 #[cfg(test)]
assert_eq_f32_array(a: &[f32], b: &[f32])267 pub fn assert_eq_f32_array(a: &[f32], b: &[f32]) {
268 assert_eq!(a.len(), b.len());
269 for i in 0..a.len() {
270 assert!((a[i] - b[i]).abs() <= std::f32::EPSILON);
271 }
272 }
273
check_slice_range(len: usize, range: &Range<usize>) -> AvifResult<()>274 pub fn check_slice_range(len: usize, range: &Range<usize>) -> AvifResult<()> {
275 if range.start >= len || range.end > len {
276 return Err(AvifError::NoContent);
277 }
278 Ok(())
279 }
280