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