• 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 use super::gainmap::*;
16 use super::io::*;
17 use super::types::*;
18 
19 use crate::image::*;
20 use crate::internal_utils::*;
21 use crate::utils::clap::*;
22 use crate::utils::*;
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 macro_rules! usize_from_u32_or_fail {
244     ($param: expr) => {
245         match usize_from_u32($param) {
246             Ok(value) => value,
247             Err(_) => return avifResult::UnknownError,
248         }
249     };
250 }
251 
copy_plane_helper( mut src_plane_ptr: *const u8, src_row_bytes: u32, mut dst_plane_ptr: *mut u8, dst_row_bytes: u32, mut width: usize, height: usize, pixel_size: usize, )252 fn copy_plane_helper(
253     mut src_plane_ptr: *const u8,
254     src_row_bytes: u32,
255     mut dst_plane_ptr: *mut u8,
256     dst_row_bytes: u32,
257     mut width: usize,
258     height: usize,
259     pixel_size: usize,
260 ) {
261     width *= pixel_size;
262     for _ in 0..height {
263         unsafe {
264             std::ptr::copy_nonoverlapping(src_plane_ptr, dst_plane_ptr, width);
265             src_plane_ptr = src_plane_ptr.offset(src_row_bytes as isize);
266             dst_plane_ptr = dst_plane_ptr.offset(dst_row_bytes as isize);
267         }
268     }
269 }
270 
271 #[no_mangle]
272 #[allow(unused)]
crabby_avifImageCopy( dstImage: *mut avifImage, srcImage: *const avifImage, planes: avifPlanesFlags, ) -> avifResult273 pub unsafe extern "C" fn crabby_avifImageCopy(
274     dstImage: *mut avifImage,
275     srcImage: *const avifImage,
276     planes: avifPlanesFlags,
277 ) -> avifResult {
278     unsafe {
279         crabby_avifImageFreePlanes(dstImage, avifPlanesFlag::AvifPlanesAll as u32);
280     }
281     let dst = unsafe { &mut (*dstImage) };
282     let src = unsafe { &(*srcImage) };
283     dst.width = src.width;
284     dst.height = src.height;
285     dst.depth = src.depth;
286     dst.yuvFormat = src.yuvFormat;
287     dst.yuvRange = src.yuvRange;
288     dst.yuvChromaSamplePosition = src.yuvChromaSamplePosition;
289     dst.alphaPremultiplied = src.alphaPremultiplied;
290     dst.colorPrimaries = src.colorPrimaries;
291     dst.transferCharacteristics = src.transferCharacteristics;
292     dst.matrixCoefficients = src.matrixCoefficients;
293     dst.clli = src.clli;
294     dst.transformFlags = src.transformFlags;
295     dst.pasp = src.pasp;
296     dst.clap = src.clap;
297     dst.irot = src.irot;
298     dst.imir = src.imir;
299     let res = unsafe { crabby_avifRWDataSet(&mut dst.icc, src.icc.data, src.icc.size) };
300     if res != avifResult::Ok {
301         return res;
302     }
303     let res = unsafe { crabby_avifRWDataSet(&mut dst.exif, src.exif.data, src.exif.size) };
304     if res != avifResult::Ok {
305         return res;
306     }
307     let res = unsafe { crabby_avifRWDataSet(&mut dst.xmp, src.xmp.data, src.xmp.size) };
308     if res != avifResult::Ok {
309         return res;
310     }
311     let pixel_size: usize = if src.depth > 8 { 2 } else { 1 };
312     if (planes & 1) != 0 {
313         for plane in 0usize..3 {
314             if src.yuvPlanes[plane].is_null() || src.yuvRowBytes[plane] == 0 {
315                 continue;
316             }
317             let plane_height = usize_from_u32_or_fail!(unsafe {
318                 crabby_avifImagePlaneHeight(srcImage, plane as i32)
319             });
320             let plane_width = usize_from_u32_or_fail!(unsafe {
321                 crabby_avifImagePlaneWidth(srcImage, plane as i32)
322             });
323             let alloc_plane_height = round2_usize(plane_height);
324             let alloc_plane_width = round2_usize(plane_width);
325             let plane_size = alloc_plane_width * alloc_plane_height * pixel_size;
326             dst.yuvPlanes[plane] = unsafe { crabby_avifAlloc(plane_size) } as *mut _;
327             dst.yuvRowBytes[plane] = (pixel_size * alloc_plane_width) as u32;
328             copy_plane_helper(
329                 src.yuvPlanes[plane],
330                 src.yuvRowBytes[plane],
331                 dst.yuvPlanes[plane],
332                 dst.yuvRowBytes[plane],
333                 plane_width,
334                 plane_height,
335                 pixel_size,
336             );
337             dst.imageOwnsYUVPlanes = AVIF_TRUE;
338         }
339     }
340     if (planes & 2) != 0 && !src.alphaPlane.is_null() && src.alphaRowBytes != 0 {
341         let plane_height = usize_from_u32_or_fail!(src.height);
342         let plane_width = usize_from_u32_or_fail!(src.width);
343         let alloc_plane_height = round2_usize(plane_height);
344         let alloc_plane_width = round2_usize(plane_width);
345         let plane_size = alloc_plane_width * alloc_plane_height * pixel_size;
346         dst.alphaPlane = unsafe { crabby_avifAlloc(plane_size) } as *mut _;
347         dst.alphaRowBytes = (pixel_size * alloc_plane_width) as u32;
348         copy_plane_helper(
349             src.alphaPlane,
350             src.alphaRowBytes,
351             dst.alphaPlane,
352             dst.alphaRowBytes,
353             plane_width,
354             plane_height,
355             pixel_size,
356         );
357         dst.imageOwnsAlphaPlane = AVIF_TRUE;
358     }
359     avifResult::Ok
360 }
361 
avif_image_allocate_planes_helper( image: &mut avifImage, planes: avifPlanesFlags, ) -> AvifResult<()>362 fn avif_image_allocate_planes_helper(
363     image: &mut avifImage,
364     planes: avifPlanesFlags,
365 ) -> AvifResult<()> {
366     if image.width == 0 || image.height == 0 {
367         return Err(AvifError::InvalidArgument);
368     }
369     let channel_size = if image.depth == 8 { 1 } else { 2 };
370     let alloc_width = round2_u32(image.width);
371     let y_row_bytes = usize_from_u32(alloc_width * channel_size)?;
372     let alloc_height = round2_u32(image.height);
373     let y_size = y_row_bytes
374         .checked_mul(usize_from_u32(alloc_height)?)
375         .ok_or(avifResult::InvalidArgument)?;
376     if (planes & 1) != 0 && image.yuvFormat != PixelFormat::None {
377         image.imageOwnsYUVPlanes = AVIF_TRUE;
378         if image.yuvPlanes[0].is_null() {
379             image.yuvRowBytes[0] = u32_from_usize(y_row_bytes)?;
380             image.yuvPlanes[0] = unsafe { crabby_avifAlloc(y_size) as *mut u8 };
381         }
382         if !image.yuvFormat.is_monochrome() {
383             let csx0 = image.yuvFormat.chroma_shift_x().0 as u64;
384             let csx1 = image.yuvFormat.chroma_shift_x().1 as u64;
385             let width = (((image.width as u64) + csx0) >> csx0) << csx1;
386             let alloc_width = round2_u32(u32_from_u64(width)?);
387             let csy = image.yuvFormat.chroma_shift_y() as u64;
388             let height = ((image.height as u64) + csy) >> csy;
389             let alloc_height = round2_u32(u32_from_u64(height)?);
390             let uv_row_bytes = usize_from_u32(alloc_width * channel_size)?;
391             let uv_size = usize_from_u32(uv_row_bytes as u32 * alloc_height)?;
392             let plane_end = match image.yuvFormat {
393                 PixelFormat::AndroidP010 | PixelFormat::AndroidNv12 | PixelFormat::AndroidNv21 => 1,
394                 _ => 2,
395             };
396             for plane in 1usize..=plane_end {
397                 if !image.yuvPlanes[plane].is_null() {
398                     continue;
399                 }
400                 image.yuvRowBytes[plane] = u32_from_usize(uv_row_bytes)?;
401                 image.yuvPlanes[plane] = unsafe { crabby_avifAlloc(uv_size) as *mut u8 };
402             }
403         }
404     }
405     if (planes & 2) != 0 {
406         image.imageOwnsAlphaPlane = AVIF_TRUE;
407         image.alphaRowBytes = u32_from_usize(y_row_bytes)?;
408         image.alphaPlane = unsafe { crabby_avifAlloc(y_size) as *mut u8 };
409     }
410     Ok(())
411 }
412 
413 #[no_mangle]
crabby_avifImageAllocatePlanes( image: *mut avifImage, planes: avifPlanesFlags, ) -> avifResult414 pub unsafe extern "C" fn crabby_avifImageAllocatePlanes(
415     image: *mut avifImage,
416     planes: avifPlanesFlags,
417 ) -> avifResult {
418     let image = unsafe { &mut (*image) };
419     to_avifResult(&avif_image_allocate_planes_helper(image, planes))
420 }
421 
422 #[no_mangle]
crabby_avifImageFreePlanes( image: *mut avifImage, planes: avifPlanesFlags, )423 pub unsafe extern "C" fn crabby_avifImageFreePlanes(
424     image: *mut avifImage,
425     planes: avifPlanesFlags,
426 ) {
427     let image = unsafe { &mut (*image) };
428     if (planes & 1) != 0 {
429         for plane in 0usize..3 {
430             if image.imageOwnsYUVPlanes == AVIF_TRUE {
431                 unsafe {
432                     crabby_avifFree(image.yuvPlanes[plane] as *mut c_void);
433                 }
434             }
435             image.yuvPlanes[plane] = std::ptr::null_mut();
436             image.yuvRowBytes[plane] = 0;
437         }
438         image.imageOwnsYUVPlanes = AVIF_FALSE;
439     }
440     if (planes & 2) != 0 {
441         if image.imageOwnsAlphaPlane == AVIF_TRUE {
442             unsafe {
443                 crabby_avifFree(image.alphaPlane as *mut c_void);
444             }
445         }
446         image.alphaPlane = std::ptr::null_mut();
447         image.alphaRowBytes = 0;
448         image.imageOwnsAlphaPlane = AVIF_FALSE;
449     }
450 }
451 
452 #[no_mangle]
crabby_avifImageDestroy(image: *mut avifImage)453 pub unsafe extern "C" fn crabby_avifImageDestroy(image: *mut avifImage) {
454     unsafe {
455         crabby_avifImageFreePlanes(image, avifPlanesFlag::AvifPlanesAll as u32);
456         let _ = Box::from_raw(image);
457     }
458 }
459 
460 #[no_mangle]
crabby_avifImageUsesU16(image: *const avifImage) -> avifBool461 pub unsafe extern "C" fn crabby_avifImageUsesU16(image: *const avifImage) -> avifBool {
462     unsafe { to_avifBool(!image.is_null() && (*image).depth > 8) }
463 }
464 
465 #[no_mangle]
crabby_avifImageIsOpaque(image: *const avifImage) -> avifBool466 pub unsafe extern "C" fn crabby_avifImageIsOpaque(image: *const avifImage) -> avifBool {
467     unsafe {
468         // TODO: Check for pixel level opacity as well.
469         to_avifBool(!image.is_null() && !(*image).alphaPlane.is_null())
470     }
471 }
472 
473 #[no_mangle]
crabby_avifImagePlane(image: *const avifImage, channel: c_int) -> *mut u8474 pub unsafe extern "C" fn crabby_avifImagePlane(image: *const avifImage, channel: c_int) -> *mut u8 {
475     if image.is_null() {
476         return std::ptr::null_mut();
477     }
478     unsafe {
479         match channel {
480             0..=2 => (*image).yuvPlanes[channel as usize],
481             3 => (*image).alphaPlane,
482             _ => std::ptr::null_mut(),
483         }
484     }
485 }
486 
487 #[no_mangle]
crabby_avifImagePlaneRowBytes( image: *const avifImage, channel: c_int, ) -> u32488 pub unsafe extern "C" fn crabby_avifImagePlaneRowBytes(
489     image: *const avifImage,
490     channel: c_int,
491 ) -> u32 {
492     if image.is_null() {
493         return 0;
494     }
495     unsafe {
496         match channel {
497             0..=2 => (*image).yuvRowBytes[channel as usize],
498             3 => (*image).alphaRowBytes,
499             _ => 0,
500         }
501     }
502 }
503 
504 #[no_mangle]
crabby_avifImagePlaneWidth( image: *const avifImage, channel: c_int, ) -> u32505 pub unsafe extern "C" fn crabby_avifImagePlaneWidth(
506     image: *const avifImage,
507     channel: c_int,
508 ) -> u32 {
509     if image.is_null() {
510         return 0;
511     }
512     unsafe {
513         match channel {
514             0 => (*image).width,
515             1 => match (*image).yuvFormat {
516                 PixelFormat::Yuv444
517                 | PixelFormat::AndroidP010
518                 | PixelFormat::AndroidNv12
519                 | PixelFormat::AndroidNv21 => (*image).width,
520                 PixelFormat::Yuv420 | PixelFormat::Yuv422 => ((*image).width).div_ceil(2),
521                 PixelFormat::None | PixelFormat::Yuv400 => 0,
522             },
523             2 => match (*image).yuvFormat {
524                 PixelFormat::Yuv444 => (*image).width,
525                 PixelFormat::Yuv420 | PixelFormat::Yuv422 => ((*image).width).div_ceil(2),
526                 PixelFormat::None
527                 | PixelFormat::Yuv400
528                 | PixelFormat::AndroidP010
529                 | PixelFormat::AndroidNv12
530                 | PixelFormat::AndroidNv21 => 0,
531             },
532             3 => {
533                 if !(*image).alphaPlane.is_null() {
534                     (*image).width
535                 } else {
536                     0
537                 }
538             }
539             _ => 0,
540         }
541     }
542 }
543 
544 #[no_mangle]
crabby_avifImagePlaneHeight( image: *const avifImage, channel: c_int, ) -> u32545 pub unsafe extern "C" fn crabby_avifImagePlaneHeight(
546     image: *const avifImage,
547     channel: c_int,
548 ) -> u32 {
549     if image.is_null() {
550         return 0;
551     }
552     unsafe {
553         match channel {
554             0 => (*image).height,
555             1 | 2 => {
556                 if (*image).yuvFormat.is_monochrome() {
557                     0
558                 } else {
559                     let shift_y = (*image).yuvFormat.chroma_shift_y();
560                     ((*image).height + shift_y) >> shift_y
561                 }
562             }
563             3 => {
564                 if !(*image).alphaPlane.is_null() {
565                     (*image).height
566                 } else {
567                     0
568                 }
569             }
570             _ => 0,
571         }
572     }
573 }
574 
575 #[no_mangle]
crabby_avifImageSetViewRect( dstImage: *mut avifImage, srcImage: *const avifImage, rect: *const avifCropRect, ) -> avifResult576 pub unsafe extern "C" fn crabby_avifImageSetViewRect(
577     dstImage: *mut avifImage,
578     srcImage: *const avifImage,
579     rect: *const avifCropRect,
580 ) -> avifResult {
581     let dst = unsafe { &mut (*dstImage) };
582     let src = unsafe { &(*srcImage) };
583     let rect = unsafe { &(*rect) };
584     if rect.width > src.width
585         || rect.height > src.height
586         || rect.x > (src.width - rect.width)
587         || rect.y > (src.height - rect.height)
588     {
589         return avifResult::InvalidArgument;
590     }
591     if !src.yuvFormat.is_monochrome()
592         && ((rect.x & src.yuvFormat.chroma_shift_x().0) != 0
593             || (rect.y & src.yuvFormat.chroma_shift_y()) != 0)
594     {
595         return avifResult::InvalidArgument;
596     }
597     *dst = avifImage {
598         width: src.width,
599         height: src.height,
600         depth: src.depth,
601         yuvFormat: src.yuvFormat,
602         yuvRange: src.yuvRange,
603         yuvChromaSamplePosition: src.yuvChromaSamplePosition,
604         alphaPremultiplied: src.alphaPremultiplied,
605         colorPrimaries: src.colorPrimaries,
606         transferCharacteristics: src.transferCharacteristics,
607         matrixCoefficients: src.matrixCoefficients,
608         clli: src.clli,
609         transformFlags: src.transformFlags,
610         pasp: src.pasp,
611         clap: src.clap,
612         irot: src.irot,
613         imir: src.imir,
614         ..avifImage::default()
615     };
616     dst.width = rect.width;
617     dst.height = rect.height;
618     let pixel_size: u32 = if src.depth == 8 { 1 } else { 2 };
619     for plane in 0usize..3 {
620         if src.yuvPlanes[plane].is_null() {
621             continue;
622         }
623         let chroma_shift = src.yuvFormat.chroma_shift_x();
624         let x = if plane == 0 { rect.x } else { (rect.x >> chroma_shift.0) << chroma_shift.1 };
625         let y = if plane == 0 { rect.y } else { rect.y >> src.yuvFormat.chroma_shift_y() };
626         let offset = match isize_from_u32(y * src.yuvRowBytes[plane] + x * pixel_size) {
627             Ok(x) => x,
628             _ => return avifResult::InvalidArgument,
629         };
630         dst.yuvPlanes[plane] = unsafe { src.yuvPlanes[plane].offset(offset) };
631         dst.yuvRowBytes[plane] = src.yuvRowBytes[plane];
632     }
633     if !src.alphaPlane.is_null() {
634         let offset = match isize_from_u32(rect.y * src.alphaRowBytes + rect.x * pixel_size) {
635             Ok(x) => x,
636             _ => return avifResult::InvalidArgument,
637         };
638         dst.alphaPlane = unsafe { src.alphaPlane.offset(offset) };
639         dst.alphaRowBytes = src.alphaRowBytes;
640     }
641     avifResult::Ok
642 }
643