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 use super::gainmap::*;
16 use super::io::*;
17 use super::types::*;
18 
19 use crate::image::*;
20 use crate::internal_utils::*;
21 use crate::parser::mp4box::*;
22 use crate::utils::clap::*;
23 use crate::*;
24 
25 use std::os::raw::c_int;
26 use std::os::raw::c_void;
27 
28 pub type avifPixelAspectRatioBox = PixelAspectRatio;
29 
30 /// cbindgen:rename-all=CamelCase
31 #[derive(Clone, Copy, Debug, Default)]
32 #[repr(C)]
33 pub struct avifCleanApertureBox {
34     // The u32 members below are actually i32 values, see
35     // https://github.com/AOMediaCodec/libavif/pull/1749#discussion_r1391583768.
36     pub width_n: u32,
37     pub width_d: u32,
38     pub height_n: u32,
39     pub height_d: u32,
40     pub horiz_off_n: u32,
41     pub horiz_off_d: u32,
42     pub vert_off_n: u32,
43     pub vert_off_d: u32,
44 }
45 
46 impl From<&Option<CleanAperture>> for avifCleanApertureBox {
from(clap_op: &Option<CleanAperture>) -> Self47     fn from(clap_op: &Option<CleanAperture>) -> Self {
48         match clap_op {
49             Some(clap) => Self {
50                 width_n: clap.width.0,
51                 width_d: clap.width.1,
52                 height_n: clap.height.0,
53                 height_d: clap.height.1,
54                 horiz_off_n: clap.horiz_off.0,
55                 horiz_off_d: clap.horiz_off.1,
56                 vert_off_n: clap.vert_off.0,
57                 vert_off_d: clap.vert_off.1,
58             },
59             None => Self::default(),
60         }
61     }
62 }
63 
64 impl From<&avifCleanApertureBox> for CleanAperture {
from(clap: &avifCleanApertureBox) -> Self65     fn from(clap: &avifCleanApertureBox) -> Self {
66         Self {
67             width: UFraction(clap.width_n, clap.width_d),
68             height: UFraction(clap.height_n, clap.height_d),
69             horiz_off: UFraction(clap.horiz_off_n, clap.horiz_off_d),
70             vert_off: UFraction(clap.vert_off_n, clap.vert_off_d),
71         }
72     }
73 }
74 
75 #[derive(Clone, Copy, Debug, Default)]
76 #[repr(C)]
77 pub struct avifImageRotation {
78     pub angle: u8,
79 }
80 
81 #[derive(Clone, Copy, Debug, Default)]
82 #[repr(C)]
83 pub struct avifImageMirror {
84     pub axis: u8,
85 }
86 
87 #[derive(Clone, Debug)]
88 #[repr(C)]
89 pub struct avifImage {
90     pub width: u32,
91     pub height: u32,
92     pub depth: u32,
93 
94     pub yuvFormat: PixelFormat,
95     pub yuvRange: YuvRange,
96     pub yuvChromaSamplePosition: ChromaSamplePosition,
97     pub yuvPlanes: [*mut u8; AVIF_PLANE_COUNT_YUV],
98     pub yuvRowBytes: [u32; AVIF_PLANE_COUNT_YUV],
99     pub imageOwnsYUVPlanes: avifBool,
100 
101     pub alphaPlane: *mut u8,
102     pub alphaRowBytes: u32,
103     pub imageOwnsAlphaPlane: avifBool,
104     pub alphaPremultiplied: avifBool,
105 
106     pub icc: avifRWData,
107     pub colorPrimaries: ColorPrimaries,
108     pub transferCharacteristics: TransferCharacteristics,
109     pub matrixCoefficients: MatrixCoefficients,
110 
111     pub clli: avifContentLightLevelInformationBox,
112     pub transformFlags: avifTransformFlags,
113     pub pasp: avifPixelAspectRatioBox,
114     pub clap: avifCleanApertureBox,
115     pub irot: avifImageRotation,
116     pub imir: avifImageMirror,
117 
118     pub exif: avifRWData,
119     pub xmp: avifRWData,
120     pub gainMap: *mut avifGainMap,
121 }
122 
123 impl Default for avifImage {
default() -> Self124     fn default() -> Self {
125         avifImage {
126             width: 0,
127             height: 0,
128             depth: 0,
129             yuvFormat: Default::default(),
130             yuvRange: YuvRange::Full,
131             yuvChromaSamplePosition: Default::default(),
132             yuvPlanes: [std::ptr::null_mut(); 3],
133             yuvRowBytes: [0; 3],
134             imageOwnsYUVPlanes: AVIF_FALSE,
135             alphaPlane: std::ptr::null_mut(),
136             alphaRowBytes: 0,
137             imageOwnsAlphaPlane: AVIF_FALSE,
138             alphaPremultiplied: AVIF_FALSE,
139             icc: Default::default(),
140             colorPrimaries: Default::default(),
141             transferCharacteristics: Default::default(),
142             matrixCoefficients: Default::default(),
143             clli: Default::default(),
144             transformFlags: AVIF_TRANSFORM_NONE,
145             pasp: Default::default(),
146             clap: Default::default(),
147             irot: Default::default(),
148             imir: Default::default(),
149             exif: Default::default(),
150             xmp: Default::default(),
151             gainMap: std::ptr::null_mut(),
152         }
153     }
154 }
155 
156 impl From<&Image> for avifImage {
from(image: &Image) -> Self157     fn from(image: &Image) -> Self {
158         let mut dst_image: avifImage = avifImage {
159             width: image.width,
160             height: image.height,
161             depth: image.depth as u32,
162             yuvFormat: image.yuv_format,
163             yuvRange: image.yuv_range,
164             yuvChromaSamplePosition: image.chroma_sample_position,
165             alphaPremultiplied: image.alpha_premultiplied as avifBool,
166             icc: (&image.icc).into(),
167             colorPrimaries: image.color_primaries,
168             transferCharacteristics: image.transfer_characteristics,
169             matrixCoefficients: image.matrix_coefficients,
170             clli: image.clli.unwrap_or_default(),
171             transformFlags: {
172                 let mut flags = 0;
173                 if image.pasp.is_some() {
174                     flags |= AVIF_TRANSFORM_PASP;
175                 }
176                 if image.clap.is_some() {
177                     flags |= AVIF_TRANSFORM_CLAP;
178                 }
179                 if image.irot_angle.is_some() {
180                     flags |= AVIF_TRANSFORM_IROT;
181                 }
182                 if image.imir_axis.is_some() {
183                     flags |= AVIF_TRANSFORM_IMIR;
184                 }
185                 flags
186             },
187             pasp: image.pasp.unwrap_or_default(),
188             clap: (&image.clap).into(),
189             irot: avifImageRotation {
190                 angle: image.irot_angle.unwrap_or_default(),
191             },
192             imir: avifImageMirror {
193                 axis: image.imir_axis.unwrap_or_default(),
194             },
195             exif: (&image.exif).into(),
196             xmp: (&image.xmp).into(),
197             ..Self::default()
198         };
199         for i in 0usize..3 {
200             if !image.has_plane(i.into()) {
201                 continue;
202             }
203             dst_image.yuvPlanes[i] = if image.depth > 8 {
204                 image.planes[i].unwrap_ref().ptr16() as *mut u8
205             } else {
206                 image.planes[i].unwrap_ref().ptr() as *mut u8
207             };
208             dst_image.yuvRowBytes[i] = image.row_bytes[i];
209         }
210         if image.has_plane(Plane::A) {
211             dst_image.alphaPlane = if image.depth > 8 {
212                 image.planes[3].unwrap_ref().ptr16() as *mut u8
213             } else {
214                 image.planes[3].unwrap_ref().ptr() as *mut u8
215             };
216             dst_image.alphaRowBytes = image.row_bytes[3];
217         }
218         dst_image
219     }
220 }
221 
222 #[no_mangle]
crabby_avifImageCreateEmpty() -> *mut avifImage223 pub unsafe extern "C" fn crabby_avifImageCreateEmpty() -> *mut avifImage {
224     Box::into_raw(Box::<avifImage>::default())
225 }
226 
227 #[no_mangle]
crabby_avifImageCreate( width: u32, height: u32, depth: u32, yuvFormat: PixelFormat, ) -> *mut avifImage228 pub unsafe extern "C" fn crabby_avifImageCreate(
229     width: u32,
230     height: u32,
231     depth: u32,
232     yuvFormat: PixelFormat,
233 ) -> *mut avifImage {
234     Box::into_raw(Box::new(avifImage {
235         width,
236         height,
237         depth,
238         yuvFormat,
239         ..avifImage::default()
240     }))
241 }
242 
243 #[no_mangle]
244 #[allow(unused)]
crabby_avifImageCopy( dstImage: *mut avifImage, srcImage: *const avifImage, planes: avifPlanesFlags, ) -> avifResult245 pub unsafe extern "C" fn crabby_avifImageCopy(
246     dstImage: *mut avifImage,
247     srcImage: *const avifImage,
248     planes: avifPlanesFlags,
249 ) -> avifResult {
250     unsafe {
251         crabby_avifImageFreePlanes(dstImage, avifPlanesFlag::AvifPlanesAll as u32);
252     }
253     let dst = unsafe { &mut (*dstImage) };
254     let src = unsafe { &(*srcImage) };
255     dst.width = src.width;
256     dst.height = src.height;
257     dst.depth = src.depth;
258     dst.yuvFormat = src.yuvFormat;
259     dst.yuvRange = src.yuvRange;
260     dst.yuvChromaSamplePosition = src.yuvChromaSamplePosition;
261     dst.alphaPremultiplied = src.alphaPremultiplied;
262     dst.colorPrimaries = src.colorPrimaries;
263     dst.transferCharacteristics = src.transferCharacteristics;
264     dst.matrixCoefficients = src.matrixCoefficients;
265     dst.clli = src.clli;
266     dst.transformFlags = src.transformFlags;
267     dst.pasp = src.pasp;
268     dst.clap = src.clap;
269     dst.irot = src.irot;
270     dst.imir = src.imir;
271     let res = unsafe { crabby_avifRWDataSet(&mut dst.icc, src.icc.data, src.icc.size) };
272     if res != avifResult::Ok {
273         return res;
274     }
275     let res = unsafe { crabby_avifRWDataSet(&mut dst.exif, src.exif.data, src.exif.size) };
276     if res != avifResult::Ok {
277         return res;
278     }
279     let res = unsafe { crabby_avifRWDataSet(&mut dst.xmp, src.xmp.data, src.xmp.size) };
280     if res != avifResult::Ok {
281         return res;
282     }
283     if (planes & 1) != 0 {
284         for plane in 0usize..3 {
285             if src.yuvPlanes[plane].is_null() || src.yuvRowBytes[plane] == 0 {
286                 continue;
287             }
288             let plane_height = unsafe { crabby_avifImagePlaneHeight(srcImage, plane as i32) };
289             let plane_size = match usize_from_u32(src.yuvRowBytes[plane] * plane_height) {
290                 Ok(size) => size,
291                 Err(_) => return avifResult::UnknownError,
292             };
293             dst.yuvPlanes[plane] = unsafe { crabby_avifAlloc(plane_size) } as *mut _;
294             unsafe {
295                 std::ptr::copy_nonoverlapping(
296                     src.yuvPlanes[plane],
297                     dst.yuvPlanes[plane],
298                     plane_size,
299                 );
300             }
301             dst.yuvRowBytes[plane] = src.yuvRowBytes[plane];
302             dst.imageOwnsYUVPlanes = AVIF_TRUE;
303         }
304     }
305     if (planes & 2) != 0 && !src.alphaPlane.is_null() && src.alphaRowBytes != 0 {
306         let plane_size = match usize_from_u32(src.alphaRowBytes * src.height) {
307             Ok(size) => size,
308             Err(_) => return avifResult::UnknownError,
309         };
310         dst.alphaPlane = unsafe { crabby_avifAlloc(plane_size) } as *mut _;
311         unsafe {
312             std::ptr::copy_nonoverlapping(src.alphaPlane, dst.alphaPlane, plane_size);
313         }
314         dst.alphaRowBytes = src.alphaRowBytes;
315         dst.imageOwnsAlphaPlane = AVIF_TRUE;
316     }
317     avifResult::Ok
318 }
319 
avif_image_allocate_planes_helper( image: &mut avifImage, planes: avifPlanesFlags, ) -> AvifResult<()>320 fn avif_image_allocate_planes_helper(
321     image: &mut avifImage,
322     planes: avifPlanesFlags,
323 ) -> AvifResult<()> {
324     if image.width == 0 || image.height == 0 {
325         return Err(AvifError::InvalidArgument);
326     }
327     let channel_size = if image.depth == 8 { 1 } else { 2 };
328     let y_row_bytes = usize_from_u32(image.width * channel_size)?;
329     let y_size = y_row_bytes
330         .checked_mul(usize_from_u32(image.height)?)
331         .ok_or(avifResult::InvalidArgument)?;
332     if (planes & 1) != 0 && image.yuvFormat != PixelFormat::None {
333         image.imageOwnsYUVPlanes = AVIF_TRUE;
334         if image.yuvPlanes[0].is_null() {
335             image.yuvRowBytes[0] = u32_from_usize(y_row_bytes)?;
336             image.yuvPlanes[0] = unsafe { crabby_avifAlloc(y_size) as *mut u8 };
337         }
338         if !image.yuvFormat.is_monochrome() {
339             let csx0 = image.yuvFormat.chroma_shift_x().0 as u64;
340             let csx1 = image.yuvFormat.chroma_shift_x().1 as u64;
341             let width = (((image.width as u64) + csx0) >> csx0) << csx1;
342             let csy = image.yuvFormat.chroma_shift_y() as u64;
343             let height = ((image.height as u64) + csy) >> csy;
344             let uv_row_bytes = usize_from_u64(width * channel_size as u64)?;
345             let uv_size = usize_from_u64(uv_row_bytes as u64 * height)?;
346             for plane in 1usize..=2 {
347                 if !image.yuvPlanes[plane].is_null() {
348                     continue;
349                 }
350                 image.yuvRowBytes[plane] = u32_from_usize(uv_row_bytes)?;
351                 image.yuvPlanes[plane] = unsafe { crabby_avifAlloc(uv_size) as *mut u8 };
352             }
353         }
354     }
355     if (planes & 2) != 0 {
356         image.imageOwnsAlphaPlane = AVIF_TRUE;
357         image.alphaRowBytes = u32_from_usize(y_row_bytes)?;
358         image.alphaPlane = unsafe { crabby_avifAlloc(y_size) as *mut u8 };
359     }
360     Ok(())
361 }
362 
363 #[no_mangle]
crabby_avifImageAllocatePlanes( image: *mut avifImage, planes: avifPlanesFlags, ) -> avifResult364 pub unsafe extern "C" fn crabby_avifImageAllocatePlanes(
365     image: *mut avifImage,
366     planes: avifPlanesFlags,
367 ) -> avifResult {
368     let image = unsafe { &mut (*image) };
369     to_avifResult(&avif_image_allocate_planes_helper(image, planes))
370 }
371 
372 #[no_mangle]
crabby_avifImageFreePlanes( image: *mut avifImage, planes: avifPlanesFlags, )373 pub unsafe extern "C" fn crabby_avifImageFreePlanes(
374     image: *mut avifImage,
375     planes: avifPlanesFlags,
376 ) {
377     let image = unsafe { &mut (*image) };
378     if (planes & 1) != 0 {
379         for plane in 0usize..3 {
380             if image.imageOwnsYUVPlanes == AVIF_TRUE {
381                 unsafe {
382                     crabby_avifFree(image.yuvPlanes[plane] as *mut c_void);
383                 }
384             }
385             image.yuvPlanes[plane] = std::ptr::null_mut();
386             image.yuvRowBytes[plane] = 0;
387         }
388         image.imageOwnsYUVPlanes = AVIF_FALSE;
389     }
390     if (planes & 2) != 0 {
391         if image.imageOwnsAlphaPlane == AVIF_TRUE {
392             unsafe {
393                 crabby_avifFree(image.alphaPlane as *mut c_void);
394             }
395         }
396         image.alphaPlane = std::ptr::null_mut();
397         image.alphaRowBytes = 0;
398         image.imageOwnsAlphaPlane = AVIF_FALSE;
399     }
400 }
401 
402 #[no_mangle]
crabby_avifImageDestroy(image: *mut avifImage)403 pub unsafe extern "C" fn crabby_avifImageDestroy(image: *mut avifImage) {
404     unsafe {
405         crabby_avifImageFreePlanes(image, avifPlanesFlag::AvifPlanesAll as u32);
406         let _ = Box::from_raw(image);
407     }
408 }
409 
410 #[no_mangle]
crabby_avifImageUsesU16(image: *const avifImage) -> avifBool411 pub unsafe extern "C" fn crabby_avifImageUsesU16(image: *const avifImage) -> avifBool {
412     unsafe { to_avifBool(!image.is_null() && (*image).depth > 8) }
413 }
414 
415 #[no_mangle]
crabby_avifImageIsOpaque(image: *const avifImage) -> avifBool416 pub unsafe extern "C" fn crabby_avifImageIsOpaque(image: *const avifImage) -> avifBool {
417     unsafe {
418         // TODO: Check for pixel level opacity as well.
419         to_avifBool(!image.is_null() && !(*image).alphaPlane.is_null())
420     }
421 }
422 
423 #[no_mangle]
crabby_avifImagePlane(image: *const avifImage, channel: c_int) -> *mut u8424 pub unsafe extern "C" fn crabby_avifImagePlane(image: *const avifImage, channel: c_int) -> *mut u8 {
425     if image.is_null() {
426         return std::ptr::null_mut();
427     }
428     unsafe {
429         match channel {
430             0..=2 => (*image).yuvPlanes[channel as usize],
431             3 => (*image).alphaPlane,
432             _ => std::ptr::null_mut(),
433         }
434     }
435 }
436 
437 #[no_mangle]
crabby_avifImagePlaneRowBytes( image: *const avifImage, channel: c_int, ) -> u32438 pub unsafe extern "C" fn crabby_avifImagePlaneRowBytes(
439     image: *const avifImage,
440     channel: c_int,
441 ) -> u32 {
442     if image.is_null() {
443         return 0;
444     }
445     unsafe {
446         match channel {
447             0..=2 => (*image).yuvRowBytes[channel as usize],
448             3 => (*image).alphaRowBytes,
449             _ => 0,
450         }
451     }
452 }
453 
454 #[no_mangle]
crabby_avifImagePlaneWidth( image: *const avifImage, channel: c_int, ) -> u32455 pub unsafe extern "C" fn crabby_avifImagePlaneWidth(
456     image: *const avifImage,
457     channel: c_int,
458 ) -> u32 {
459     if image.is_null() {
460         return 0;
461     }
462     unsafe {
463         match channel {
464             0 => (*image).width,
465             1 | 2 => {
466                 if (*image).yuvFormat.is_monochrome() {
467                     0
468                 } else {
469                     let shift_x = (*image).yuvFormat.chroma_shift_x();
470                     (((*image).width + shift_x.0) >> shift_x.0) << shift_x.1
471                 }
472             }
473             3 => {
474                 if !(*image).alphaPlane.is_null() {
475                     (*image).width
476                 } else {
477                     0
478                 }
479             }
480             _ => 0,
481         }
482     }
483 }
484 
485 #[no_mangle]
crabby_avifImagePlaneHeight( image: *const avifImage, channel: c_int, ) -> u32486 pub unsafe extern "C" fn crabby_avifImagePlaneHeight(
487     image: *const avifImage,
488     channel: c_int,
489 ) -> u32 {
490     if image.is_null() {
491         return 0;
492     }
493     unsafe {
494         match channel {
495             0 => (*image).height,
496             1 | 2 => {
497                 if (*image).yuvFormat.is_monochrome() {
498                     0
499                 } else {
500                     let shift_y = (*image).yuvFormat.chroma_shift_y();
501                     ((*image).height + shift_y) >> shift_y
502                 }
503             }
504             3 => {
505                 if !(*image).alphaPlane.is_null() {
506                     (*image).height
507                 } else {
508                     0
509                 }
510             }
511             _ => 0,
512         }
513     }
514 }
515 
516 #[no_mangle]
crabby_avifImageSetViewRect( dstImage: *mut avifImage, srcImage: *const avifImage, rect: *const avifCropRect, ) -> avifResult517 pub unsafe extern "C" fn crabby_avifImageSetViewRect(
518     dstImage: *mut avifImage,
519     srcImage: *const avifImage,
520     rect: *const avifCropRect,
521 ) -> avifResult {
522     let dst = unsafe { &mut (*dstImage) };
523     let src = unsafe { &(*srcImage) };
524     let rect = unsafe { &(*rect) };
525     if rect.width > src.width
526         || rect.height > src.height
527         || rect.x > (src.width - rect.width)
528         || rect.y > (src.height - rect.height)
529     {
530         return avifResult::InvalidArgument;
531     }
532     if !src.yuvFormat.is_monochrome()
533         && ((rect.x & src.yuvFormat.chroma_shift_x().0) != 0
534             || (rect.y & src.yuvFormat.chroma_shift_y()) != 0)
535     {
536         return avifResult::InvalidArgument;
537     }
538     *dst = avifImage {
539         width: src.width,
540         height: src.height,
541         depth: src.depth,
542         yuvFormat: src.yuvFormat,
543         yuvRange: src.yuvRange,
544         yuvChromaSamplePosition: src.yuvChromaSamplePosition,
545         alphaPremultiplied: src.alphaPremultiplied,
546         colorPrimaries: src.colorPrimaries,
547         transferCharacteristics: src.transferCharacteristics,
548         matrixCoefficients: src.matrixCoefficients,
549         clli: src.clli,
550         transformFlags: src.transformFlags,
551         pasp: src.pasp,
552         clap: src.clap,
553         irot: src.irot,
554         imir: src.imir,
555         ..avifImage::default()
556     };
557     dst.width = rect.width;
558     dst.height = rect.height;
559     let pixel_size: u32 = if src.depth == 8 { 1 } else { 2 };
560     for plane in 0usize..3 {
561         if src.yuvPlanes[plane].is_null() {
562             continue;
563         }
564         let chroma_shift = src.yuvFormat.chroma_shift_x();
565         let x = if plane == 0 { rect.x } else { (rect.x >> chroma_shift.0) << chroma_shift.1 };
566         let y = if plane == 0 { rect.y } else { rect.y >> src.yuvFormat.chroma_shift_y() };
567         let offset = match isize_from_u32(y * src.yuvRowBytes[plane] + x * pixel_size) {
568             Ok(x) => x,
569             _ => return avifResult::InvalidArgument,
570         };
571         dst.yuvPlanes[plane] = unsafe { src.yuvPlanes[plane].offset(offset) };
572         dst.yuvRowBytes[plane] = src.yuvRowBytes[plane];
573     }
574     if !src.alphaPlane.is_null() {
575         let offset = match isize_from_u32(rect.y * src.alphaRowBytes + rect.x * pixel_size) {
576             Ok(x) => x,
577             _ => return avifResult::InvalidArgument,
578         };
579         dst.alphaPlane = unsafe { src.alphaPlane.offset(offset) };
580         dst.alphaRowBytes = src.alphaRowBytes;
581     }
582     avifResult::Ok
583 }
584