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