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