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::rgb;
16 use super::rgb::*;
17
18 use crate::image::*;
19 use crate::internal_utils::*;
20 use crate::*;
21
22 use libyuv_sys::bindings::*;
23
24 use std::os::raw::c_int;
25
find_constants(image: &image::Image) -> Option<(&YuvConstants, &YuvConstants)>26 fn find_constants(image: &image::Image) -> Option<(&YuvConstants, &YuvConstants)> {
27 let matrix_coefficients = if image.yuv_format == PixelFormat::Yuv400
28 && image.matrix_coefficients == MatrixCoefficients::Identity
29 {
30 MatrixCoefficients::Bt601
31 } else {
32 image.matrix_coefficients
33 };
34 // Android MediaCodec always uses Yuv420. So use Bt601 instead of Identity in that case.
35 #[cfg(feature = "android_mediacodec")]
36 let matrix_coefficients = if matrix_coefficients == MatrixCoefficients::Identity {
37 MatrixCoefficients::Bt601
38 } else {
39 matrix_coefficients
40 };
41 unsafe {
42 match image.yuv_range {
43 YuvRange::Full => match matrix_coefficients {
44 MatrixCoefficients::Bt709 => Some((&kYuvF709Constants, &kYvuF709Constants)),
45 MatrixCoefficients::Bt470bg
46 | MatrixCoefficients::Bt601
47 | MatrixCoefficients::Unspecified => Some((&kYuvJPEGConstants, &kYvuJPEGConstants)),
48 MatrixCoefficients::Bt2020Ncl => Some((&kYuvV2020Constants, &kYvuV2020Constants)),
49 MatrixCoefficients::ChromaDerivedNcl => match image.color_primaries {
50 ColorPrimaries::Srgb | ColorPrimaries::Unspecified => {
51 Some((&kYuvF709Constants, &kYvuF709Constants))
52 }
53 ColorPrimaries::Bt470bg | ColorPrimaries::Bt601 => {
54 Some((&kYuvJPEGConstants, &kYvuJPEGConstants))
55 }
56 ColorPrimaries::Bt2020 => Some((&kYuvV2020Constants, &kYvuV2020Constants)),
57 _ => None,
58 },
59 _ => None,
60 },
61 YuvRange::Limited => match matrix_coefficients {
62 MatrixCoefficients::Bt709 => Some((&kYuvH709Constants, &kYvuH709Constants)),
63 MatrixCoefficients::Bt470bg
64 | MatrixCoefficients::Bt601
65 | MatrixCoefficients::Unspecified => Some((&kYuvI601Constants, &kYvuI601Constants)),
66 MatrixCoefficients::Bt2020Ncl => Some((&kYuv2020Constants, &kYvu2020Constants)),
67 MatrixCoefficients::ChromaDerivedNcl => match image.color_primaries {
68 ColorPrimaries::Srgb | ColorPrimaries::Unspecified => {
69 Some((&kYuvH709Constants, &kYvuH709Constants))
70 }
71 ColorPrimaries::Bt470bg | ColorPrimaries::Bt601 => {
72 Some((&kYuvI601Constants, &kYvuI601Constants))
73 }
74 ColorPrimaries::Bt2020 => Some((&kYuv2020Constants, &kYvu2020Constants)),
75 _ => None,
76 },
77 _ => None,
78 },
79 }
80 }
81 }
82
83 #[rustfmt::skip]
84 type YUV400ToRGBMatrix = unsafe extern "C" fn(
85 *const u8, c_int, *mut u8, c_int, *const YuvConstants, c_int, c_int) -> c_int;
86 #[rustfmt::skip]
87 type YUVToRGBMatrixFilter = unsafe extern "C" fn(
88 *const u8, c_int, *const u8, c_int, *const u8, c_int, *mut u8, c_int, *const YuvConstants,
89 c_int, c_int, FilterMode) -> c_int;
90 #[rustfmt::skip]
91 type YUVAToRGBMatrixFilter = unsafe extern "C" fn(
92 *const u8, c_int, *const u8, c_int, *const u8, c_int, *const u8, c_int, *mut u8, c_int,
93 *const YuvConstants, c_int, c_int, c_int, FilterMode) -> c_int;
94 #[rustfmt::skip]
95 type YUVToRGBMatrix = unsafe extern "C" fn(
96 *const u8, c_int, *const u8, c_int, *const u8, c_int, *mut u8, c_int, *const YuvConstants,
97 c_int, c_int) -> c_int;
98 #[rustfmt::skip]
99 type YUVAToRGBMatrix = unsafe extern "C" fn(
100 *const u8, c_int, *const u8, c_int, *const u8, c_int, *const u8, c_int, *mut u8, c_int,
101 *const YuvConstants, c_int, c_int, c_int) -> c_int;
102 #[rustfmt::skip]
103 type YUVToRGBMatrixFilterHighBitDepth = unsafe extern "C" fn(
104 *const u16, c_int, *const u16, c_int, *const u16, c_int, *mut u8, c_int, *const YuvConstants,
105 c_int, c_int, FilterMode) -> c_int;
106 #[rustfmt::skip]
107 type YUVAToRGBMatrixFilterHighBitDepth = unsafe extern "C" fn(
108 *const u16, c_int, *const u16, c_int, *const u16, c_int, *const u16, c_int, *mut u8, c_int,
109 *const YuvConstants, c_int, c_int, c_int, FilterMode) -> c_int;
110 #[rustfmt::skip]
111 type YUVToRGBMatrixHighBitDepth = unsafe extern "C" fn(
112 *const u16, c_int, *const u16, c_int, *const u16, c_int, *mut u8, c_int, *const YuvConstants,
113 c_int, c_int) -> c_int;
114 #[rustfmt::skip]
115 type YUVAToRGBMatrixHighBitDepth = unsafe extern "C" fn(
116 *const u16, c_int, *const u16, c_int, *const u16, c_int, *const u16, c_int, *mut u8, c_int,
117 *const YuvConstants, c_int, c_int, c_int) -> c_int;
118 #[rustfmt::skip]
119 type P010ToRGBMatrix = unsafe extern "C" fn(
120 *const u16, c_int, *const u16, c_int, *mut u8, c_int, *const YuvConstants, c_int,
121 c_int) -> c_int;
122 #[rustfmt::skip]
123 type ARGBToABGR = unsafe extern "C" fn(
124 *const u8, c_int, *mut u8, c_int, c_int, c_int) -> c_int;
125 #[rustfmt::skip]
126 type NVToARGBMatrix = unsafe extern "C" fn(
127 *const u8, c_int, *const u8, c_int, *mut u8, c_int, *const YuvConstants, c_int,
128 c_int) -> c_int;
129
130 #[derive(Debug)]
131 enum ConversionFunction {
132 YUV400ToRGBMatrix(YUV400ToRGBMatrix),
133 YUVToRGBMatrixFilter(YUVToRGBMatrixFilter),
134 YUVAToRGBMatrixFilter(YUVAToRGBMatrixFilter),
135 YUVToRGBMatrix(YUVToRGBMatrix),
136 YUVAToRGBMatrix(YUVAToRGBMatrix),
137 YUVToRGBMatrixFilterHighBitDepth(YUVToRGBMatrixFilterHighBitDepth),
138 YUVAToRGBMatrixFilterHighBitDepth(YUVAToRGBMatrixFilterHighBitDepth),
139 YUVToRGBMatrixHighBitDepth(YUVToRGBMatrixHighBitDepth),
140 YUVAToRGBMatrixHighBitDepth(YUVAToRGBMatrixHighBitDepth),
141 P010ToRGBMatrix(P010ToRGBMatrix, ARGBToABGR),
142 YUVToAB30Matrix(YUVToRGBMatrixHighBitDepth, ARGBToABGR),
143 NVToARGBMatrix(NVToARGBMatrix),
144 }
145
146 impl ConversionFunction {
is_yuva(&self) -> bool147 fn is_yuva(&self) -> bool {
148 matches!(
149 self,
150 ConversionFunction::YUVAToRGBMatrixFilter(_)
151 | ConversionFunction::YUVAToRGBMatrix(_)
152 | ConversionFunction::YUVAToRGBMatrixFilterHighBitDepth(_)
153 | ConversionFunction::YUVAToRGBMatrixHighBitDepth(_)
154 )
155 }
156 }
157
find_conversion_function( yuv_format: PixelFormat, yuv_depth: u8, rgb: &rgb::Image, alpha_preferred: bool, ) -> Option<ConversionFunction>158 fn find_conversion_function(
159 yuv_format: PixelFormat,
160 yuv_depth: u8,
161 rgb: &rgb::Image,
162 alpha_preferred: bool,
163 ) -> Option<ConversionFunction> {
164 match (alpha_preferred, yuv_depth, rgb.format, yuv_format) {
165 (_, 8, Format::Rgba, PixelFormat::AndroidNv12) => {
166 // What Android considers to be NV12 is actually NV21 in libyuv.
167 Some(ConversionFunction::NVToARGBMatrix(NV21ToARGBMatrix))
168 }
169 (_, 8, Format::Rgba, PixelFormat::AndroidNv21) => {
170 // What Android considers to be NV21 is actually NV12 in libyuv.
171 Some(ConversionFunction::NVToARGBMatrix(NV12ToARGBMatrix))
172 }
173 (_, 8, Format::Rgb565, PixelFormat::AndroidNv12) => {
174 Some(ConversionFunction::NVToARGBMatrix(NV12ToRGB565Matrix))
175 }
176 (_, 16, Format::Rgba1010102, PixelFormat::AndroidP010) => Some(
177 ConversionFunction::P010ToRGBMatrix(P010ToAR30Matrix, AR30ToAB30),
178 ),
179 (_, 10, Format::Rgba1010102, PixelFormat::Yuv420) => Some(
180 ConversionFunction::YUVToAB30Matrix(I010ToAR30Matrix, AR30ToAB30),
181 ),
182 (_, 16, Format::Rgba, PixelFormat::AndroidP010) => Some(
183 ConversionFunction::P010ToRGBMatrix(P010ToARGBMatrix, ARGBToABGR),
184 ),
185 (true, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
186 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
187 {
188 Some(ConversionFunction::YUVAToRGBMatrixFilterHighBitDepth(
189 I210AlphaToARGBMatrixFilter,
190 ))
191 }
192 (true, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
193 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
194 {
195 Some(ConversionFunction::YUVAToRGBMatrixFilterHighBitDepth(
196 I010AlphaToARGBMatrixFilter,
197 ))
198 }
199 (_, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
200 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
201 {
202 Some(ConversionFunction::YUVToRGBMatrixFilterHighBitDepth(
203 I210ToARGBMatrixFilter,
204 ))
205 }
206 (_, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
207 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
208 {
209 Some(ConversionFunction::YUVToRGBMatrixFilterHighBitDepth(
210 I010ToARGBMatrixFilter,
211 ))
212 }
213
214 (true, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv444) => Some(
215 ConversionFunction::YUVAToRGBMatrixHighBitDepth(I410AlphaToARGBMatrix),
216 ),
217 (true, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
218 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
219 {
220 Some(ConversionFunction::YUVAToRGBMatrixHighBitDepth(
221 I210AlphaToARGBMatrix,
222 ))
223 }
224 (true, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
225 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
226 {
227 Some(ConversionFunction::YUVAToRGBMatrixHighBitDepth(
228 I010AlphaToARGBMatrix,
229 ))
230 }
231 (_, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv444) => Some(
232 ConversionFunction::YUVToRGBMatrixHighBitDepth(I410ToARGBMatrix),
233 ),
234 (_, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
235 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
236 {
237 Some(ConversionFunction::YUVToRGBMatrixHighBitDepth(
238 I210ToARGBMatrix,
239 ))
240 }
241 (_, 10, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
242 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
243 {
244 Some(ConversionFunction::YUVToRGBMatrixHighBitDepth(
245 I010ToARGBMatrix,
246 ))
247 }
248 (_, 12, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
249 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
250 {
251 Some(ConversionFunction::YUVToRGBMatrixHighBitDepth(
252 I012ToARGBMatrix,
253 ))
254 }
255
256 // The fall through here is intentional. If a high bitdepth function was not found, try to
257 // see if we can use a low bitdepth function with a downshift.
258 //
259 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv400) => {
260 Some(ConversionFunction::YUV400ToRGBMatrix(I400ToARGBMatrix))
261 }
262
263 (true, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
264 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
265 {
266 Some(ConversionFunction::YUVAToRGBMatrixFilter(
267 I422AlphaToARGBMatrixFilter,
268 ))
269 }
270 (true, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
271 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
272 {
273 Some(ConversionFunction::YUVAToRGBMatrixFilter(
274 I420AlphaToARGBMatrixFilter,
275 ))
276 }
277
278 (_, _, Format::Rgb | Format::Bgr, PixelFormat::Yuv422)
279 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
280 {
281 Some(ConversionFunction::YUVToRGBMatrixFilter(
282 I422ToRGB24MatrixFilter,
283 ))
284 }
285 (_, _, Format::Rgb | Format::Bgr, PixelFormat::Yuv420)
286 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
287 {
288 Some(ConversionFunction::YUVToRGBMatrixFilter(
289 I420ToRGB24MatrixFilter,
290 ))
291 }
292 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
293 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
294 {
295 Some(ConversionFunction::YUVToRGBMatrixFilter(
296 I422ToARGBMatrixFilter,
297 ))
298 }
299 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
300 if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() =>
301 {
302 Some(ConversionFunction::YUVToRGBMatrixFilter(
303 I420ToARGBMatrixFilter,
304 ))
305 }
306
307 (true, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv444) => {
308 Some(ConversionFunction::YUVAToRGBMatrix(I444AlphaToARGBMatrix))
309 }
310 (true, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
311 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
312 {
313 Some(ConversionFunction::YUVAToRGBMatrix(I422AlphaToARGBMatrix))
314 }
315 (true, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
316 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
317 {
318 Some(ConversionFunction::YUVAToRGBMatrix(I420AlphaToARGBMatrix))
319 }
320
321 (_, _, Format::Rgb | Format::Bgr, PixelFormat::Yuv444) => {
322 Some(ConversionFunction::YUVToRGBMatrix(I444ToRGB24Matrix))
323 }
324 (_, _, Format::Rgb | Format::Bgr, PixelFormat::Yuv420)
325 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
326 {
327 Some(ConversionFunction::YUVToRGBMatrix(I420ToRGB24Matrix))
328 }
329
330 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv444) => {
331 Some(ConversionFunction::YUVToRGBMatrix(I444ToARGBMatrix))
332 }
333 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv422)
334 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
335 {
336 Some(ConversionFunction::YUVToRGBMatrix(I422ToARGBMatrix))
337 }
338 (_, _, Format::Rgba | Format::Bgra, PixelFormat::Yuv420)
339 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
340 {
341 Some(ConversionFunction::YUVToRGBMatrix(I420ToARGBMatrix))
342 }
343
344 (_, _, Format::Argb | Format::Abgr, PixelFormat::Yuv422)
345 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
346 {
347 Some(ConversionFunction::YUVToRGBMatrix(I422ToRGBAMatrix))
348 }
349 (_, _, Format::Argb | Format::Abgr, PixelFormat::Yuv420)
350 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
351 {
352 Some(ConversionFunction::YUVToRGBMatrix(I420ToRGBAMatrix))
353 }
354
355 (_, _, Format::Rgb565, PixelFormat::Yuv422)
356 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
357 {
358 Some(ConversionFunction::YUVToRGBMatrix(I422ToRGB565Matrix))
359 }
360 (_, _, Format::Rgb565, PixelFormat::Yuv420)
361 if rgb.chroma_upsampling.nearest_neighbor_filter_allowed() =>
362 {
363 Some(ConversionFunction::YUVToRGBMatrix(I420ToRGB565Matrix))
364 }
365
366 _ => None,
367 }
368 }
369
370 #[cfg_attr(feature = "disable_cfi", no_sanitize(cfi))]
yuv_to_rgb(image: &image::Image, rgb: &mut rgb::Image) -> AvifResult<bool>371 pub(crate) fn yuv_to_rgb(image: &image::Image, rgb: &mut rgb::Image) -> AvifResult<bool> {
372 if (rgb.depth != 8 && rgb.depth != 10) || !image.depth_valid() {
373 return Err(AvifError::NotImplemented);
374 }
375 if rgb.depth == 10
376 && (!matches!(
377 image.yuv_format,
378 PixelFormat::AndroidP010 | PixelFormat::Yuv420
379 ) || rgb.format != Format::Rgba1010102)
380 {
381 return Err(AvifError::NotImplemented);
382 }
383
384 let (matrix_yuv, matrix_yvu) = find_constants(image).ok_or(AvifError::NotImplemented)?;
385 let alpha_preferred = rgb.has_alpha() && image.has_alpha();
386 let conversion_function =
387 find_conversion_function(image.yuv_format, image.depth, rgb, alpha_preferred)
388 .ok_or(AvifError::NotImplemented)?;
389 let is_yvu = matches!(rgb.format, Format::Rgb | Format::Rgba | Format::Argb);
390 let matrix = if is_yvu { matrix_yvu } else { matrix_yuv };
391 let u_plane_index: usize = if is_yvu { 2 } else { 1 };
392 let v_plane_index: usize = if is_yvu { 1 } else { 2 };
393 let filter = if rgb.chroma_upsampling.bilinear_or_better_filter_allowed() {
394 FilterMode_kFilterBilinear
395 } else {
396 FilterMode_kFilterNone
397 };
398 let mut plane_u8: [*const u8; 4] = ALL_PLANES
399 .iter()
400 .map(|x| {
401 if image.has_plane(*x) {
402 image.planes[x.as_usize()].unwrap_ref().ptr()
403 } else {
404 std::ptr::null()
405 }
406 })
407 .collect::<Vec<*const u8>>()
408 .try_into()
409 .unwrap();
410 let plane_u16: [*const u16; 4] = ALL_PLANES
411 .iter()
412 .map(|x| {
413 if image.has_plane(*x) {
414 image.planes[x.as_usize()].unwrap_ref().ptr16()
415 } else {
416 std::ptr::null()
417 }
418 })
419 .collect::<Vec<*const u16>>()
420 .try_into()
421 .unwrap();
422 let mut plane_row_bytes: [i32; 4] = ALL_PLANES
423 .iter()
424 .map(|x| {
425 if image.has_plane(*x) {
426 i32_from_u32(image.plane_data(*x).unwrap().row_bytes).unwrap_or_default()
427 } else {
428 0
429 }
430 })
431 .collect::<Vec<i32>>()
432 .try_into()
433 .unwrap();
434 let rgb_row_bytes = i32_from_u32(rgb.row_bytes)?;
435 let width = i32_from_u32(image.width)?;
436 let height = i32_from_u32(image.height)?;
437 let mut result: c_int;
438 unsafe {
439 let mut high_bd_matched = true;
440 // Apply one of the high bitdepth functions if possible.
441 result = match conversion_function {
442 ConversionFunction::P010ToRGBMatrix(func1, func2) => {
443 let result = func1(
444 plane_u16[0],
445 plane_row_bytes[0] / 2,
446 plane_u16[1],
447 plane_row_bytes[1] / 2,
448 rgb.pixels(),
449 rgb_row_bytes,
450 matrix,
451 width,
452 height,
453 );
454 if result == 0 {
455 // It is okay to use the same pointer as source and destination for this
456 // conversion.
457 func2(
458 rgb.pixels(),
459 rgb_row_bytes,
460 rgb.pixels(),
461 rgb_row_bytes,
462 width,
463 height,
464 )
465 } else {
466 result
467 }
468 }
469 ConversionFunction::YUVToAB30Matrix(func1, func2) => {
470 let result = func1(
471 plane_u16[0],
472 plane_row_bytes[0] / 2,
473 plane_u16[1],
474 plane_row_bytes[1] / 2,
475 plane_u16[2],
476 plane_row_bytes[2] / 2,
477 rgb.pixels(),
478 rgb_row_bytes,
479 matrix,
480 width,
481 height,
482 );
483 if result == 0 {
484 // It is okay to use the same pointer as source and destination for this
485 // conversion.
486 func2(
487 rgb.pixels(),
488 rgb_row_bytes,
489 rgb.pixels(),
490 rgb_row_bytes,
491 width,
492 height,
493 )
494 } else {
495 result
496 }
497 }
498 ConversionFunction::YUVToRGBMatrixFilterHighBitDepth(func) => func(
499 plane_u16[0],
500 plane_row_bytes[0] / 2,
501 plane_u16[u_plane_index],
502 plane_row_bytes[u_plane_index] / 2,
503 plane_u16[v_plane_index],
504 plane_row_bytes[v_plane_index] / 2,
505 rgb.pixels(),
506 rgb_row_bytes,
507 matrix,
508 width,
509 height,
510 filter,
511 ),
512 ConversionFunction::YUVAToRGBMatrixFilterHighBitDepth(func) => func(
513 plane_u16[0],
514 plane_row_bytes[0] / 2,
515 plane_u16[u_plane_index],
516 plane_row_bytes[u_plane_index] / 2,
517 plane_u16[v_plane_index],
518 plane_row_bytes[v_plane_index] / 2,
519 plane_u16[3],
520 plane_row_bytes[3] / 2,
521 rgb.pixels(),
522 rgb_row_bytes,
523 matrix,
524 width,
525 height,
526 0, // attenuate
527 filter,
528 ),
529 ConversionFunction::YUVToRGBMatrixHighBitDepth(func) => func(
530 plane_u16[0],
531 plane_row_bytes[0] / 2,
532 plane_u16[u_plane_index],
533 plane_row_bytes[u_plane_index] / 2,
534 plane_u16[v_plane_index],
535 plane_row_bytes[v_plane_index] / 2,
536 rgb.pixels(),
537 rgb_row_bytes,
538 matrix,
539 width,
540 height,
541 ),
542 ConversionFunction::YUVAToRGBMatrixHighBitDepth(func) => func(
543 plane_u16[0],
544 plane_row_bytes[0] / 2,
545 plane_u16[u_plane_index],
546 plane_row_bytes[u_plane_index] / 2,
547 plane_u16[v_plane_index],
548 plane_row_bytes[v_plane_index] / 2,
549 plane_u16[3],
550 plane_row_bytes[3] / 2,
551 rgb.pixels(),
552 rgb_row_bytes,
553 matrix,
554 width,
555 height,
556 0, // attenuate
557 ),
558 _ => {
559 high_bd_matched = false;
560 -1
561 }
562 };
563 if high_bd_matched {
564 return if result == 0 {
565 Ok(!image.has_alpha() || conversion_function.is_yuva())
566 } else {
567 Err(AvifError::ReformatFailed)
568 };
569 }
570 let mut image8 = image::Image::default();
571 if image.depth > 8 {
572 downshift_to_8bit(image, &mut image8, conversion_function.is_yuva())?;
573 plane_u8 = ALL_PLANES
574 .iter()
575 .map(|x| {
576 if image8.has_plane(*x) {
577 image8.planes[x.as_usize()].unwrap_ref().ptr()
578 } else {
579 std::ptr::null()
580 }
581 })
582 .collect::<Vec<*const u8>>()
583 .try_into()
584 .unwrap();
585 plane_row_bytes = ALL_PLANES
586 .iter()
587 .map(|x| {
588 if image8.has_plane(*x) {
589 i32_from_u32(image8.plane_data(*x).unwrap().row_bytes).unwrap_or_default()
590 } else {
591 0
592 }
593 })
594 .collect::<Vec<i32>>()
595 .try_into()
596 .unwrap();
597 }
598 result = match conversion_function {
599 ConversionFunction::NVToARGBMatrix(func) => func(
600 plane_u8[0],
601 plane_row_bytes[0],
602 plane_u8[1],
603 plane_row_bytes[1],
604 rgb.pixels(),
605 rgb_row_bytes,
606 matrix,
607 width,
608 height,
609 ),
610 ConversionFunction::YUV400ToRGBMatrix(func) => func(
611 plane_u8[0],
612 plane_row_bytes[0],
613 rgb.pixels(),
614 rgb_row_bytes,
615 matrix,
616 width,
617 height,
618 ),
619 ConversionFunction::YUVToRGBMatrixFilter(func) => func(
620 plane_u8[0],
621 plane_row_bytes[0],
622 plane_u8[u_plane_index],
623 plane_row_bytes[u_plane_index],
624 plane_u8[v_plane_index],
625 plane_row_bytes[v_plane_index],
626 rgb.pixels(),
627 rgb_row_bytes,
628 matrix,
629 width,
630 height,
631 filter,
632 ),
633 ConversionFunction::YUVAToRGBMatrixFilter(func) => func(
634 plane_u8[0],
635 plane_row_bytes[0],
636 plane_u8[u_plane_index],
637 plane_row_bytes[u_plane_index],
638 plane_u8[v_plane_index],
639 plane_row_bytes[v_plane_index],
640 plane_u8[3],
641 plane_row_bytes[3],
642 rgb.pixels(),
643 rgb_row_bytes,
644 matrix,
645 width,
646 height,
647 0, // attenuate
648 filter,
649 ),
650 ConversionFunction::YUVToRGBMatrix(func) => func(
651 plane_u8[0],
652 plane_row_bytes[0],
653 plane_u8[u_plane_index],
654 plane_row_bytes[u_plane_index],
655 plane_u8[v_plane_index],
656 plane_row_bytes[v_plane_index],
657 rgb.pixels(),
658 rgb_row_bytes,
659 matrix,
660 width,
661 height,
662 ),
663 ConversionFunction::YUVAToRGBMatrix(func) => func(
664 plane_u8[0],
665 plane_row_bytes[0],
666 plane_u8[u_plane_index],
667 plane_row_bytes[u_plane_index],
668 plane_u8[v_plane_index],
669 plane_row_bytes[v_plane_index],
670 plane_u8[3],
671 plane_row_bytes[3],
672 rgb.pixels(),
673 rgb_row_bytes,
674 matrix,
675 width,
676 height,
677 0, // attenuate
678 ),
679 _ => 0,
680 };
681 }
682 if result == 0 {
683 Ok(!image.has_alpha() || conversion_function.is_yuva())
684 } else {
685 Err(AvifError::ReformatFailed)
686 }
687 }
688
downshift_to_8bit( image: &image::Image, image8: &mut image::Image, alpha: bool, ) -> AvifResult<()>689 fn downshift_to_8bit(
690 image: &image::Image,
691 image8: &mut image::Image,
692 alpha: bool,
693 ) -> AvifResult<()> {
694 image8.width = image.width;
695 image8.height = image.height;
696 image8.depth = 8;
697 image8.yuv_format = image.yuv_format;
698 image8.allocate_planes(Category::Color)?;
699 if alpha {
700 image8.allocate_planes(Category::Alpha)?;
701 }
702 let scale = 1 << (24 - image.depth);
703 for plane in ALL_PLANES {
704 if plane == Plane::A && !alpha {
705 continue;
706 }
707 let pd = image.plane_data(plane);
708 if pd.is_none() {
709 continue;
710 }
711 let pd = pd.unwrap();
712 if pd.width == 0 {
713 continue;
714 }
715 let source_ptr = image.planes[plane.as_usize()].unwrap_ref().ptr16();
716 let pd8 = image8.plane_data(plane).unwrap();
717 let dst_ptr = image8.planes[plane.as_usize()].unwrap_mut().ptr_mut();
718 unsafe {
719 Convert16To8Plane(
720 source_ptr,
721 i32_from_u32(pd.row_bytes / 2)?,
722 dst_ptr,
723 i32_from_u32(pd8.row_bytes)?,
724 scale,
725 i32_from_u32(pd.width)?,
726 i32_from_u32(pd.height)?,
727 );
728 }
729 }
730 Ok(())
731 }
732
process_alpha(rgb: &mut rgb::Image, multiply: bool) -> AvifResult<()>733 pub(crate) fn process_alpha(rgb: &mut rgb::Image, multiply: bool) -> AvifResult<()> {
734 if rgb.depth != 8 {
735 return Err(AvifError::NotImplemented);
736 }
737 match rgb.format {
738 Format::Rgba | Format::Bgra => {}
739 _ => return Err(AvifError::NotImplemented),
740 }
741 let result = unsafe {
742 if multiply {
743 ARGBAttenuate(
744 rgb.pixels(),
745 i32_from_u32(rgb.row_bytes)?,
746 rgb.pixels(),
747 i32_from_u32(rgb.row_bytes)?,
748 i32_from_u32(rgb.width)?,
749 i32_from_u32(rgb.height)?,
750 )
751 } else {
752 ARGBUnattenuate(
753 rgb.pixels(),
754 i32_from_u32(rgb.row_bytes)?,
755 rgb.pixels(),
756 i32_from_u32(rgb.row_bytes)?,
757 i32_from_u32(rgb.width)?,
758 i32_from_u32(rgb.height)?,
759 )
760 }
761 };
762 if result == 0 {
763 Ok(())
764 } else {
765 Err(AvifError::ReformatFailed)
766 }
767 }
768
convert_to_half_float(rgb: &mut rgb::Image, scale: f32) -> AvifResult<()>769 pub(crate) fn convert_to_half_float(rgb: &mut rgb::Image, scale: f32) -> AvifResult<()> {
770 let res = unsafe {
771 HalfFloatPlane(
772 rgb.pixels() as *const u16,
773 i32_from_u32(rgb.row_bytes)?,
774 rgb.pixels() as *mut u16,
775 i32_from_u32(rgb.row_bytes)?,
776 scale,
777 i32_from_u32(rgb.width * rgb.channel_count())?,
778 i32_from_u32(rgb.height)?,
779 )
780 };
781 if res == 0 {
782 Ok(())
783 } else {
784 Err(AvifError::InvalidArgument)
785 }
786 }
787