/src/libavif/src/reformat_libyuv.c
Line | Count | Source |
1 | | // Copyright 2019 Joe Drago. All rights reserved. |
2 | | // SPDX-License-Identifier: BSD-2-Clause |
3 | | |
4 | | #include "avif/internal.h" |
5 | | |
6 | | #if !defined(AVIF_LIBYUV_ENABLED) |
7 | | |
8 | | // No libyuv! |
9 | | avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb) |
10 | 0 | { |
11 | 0 | (void)image; |
12 | 0 | (void)rgb; |
13 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
14 | 0 | } |
15 | | avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifBool reformatAlpha, avifBool * alphaReformattedWithLibYUV) |
16 | 10.7k | { |
17 | 10.7k | (void)image; |
18 | 10.7k | (void)rgb; |
19 | 10.7k | (void)reformatAlpha; |
20 | 10.7k | *alphaReformattedWithLibYUV = AVIF_FALSE; |
21 | 10.7k | return AVIF_RESULT_NOT_IMPLEMENTED; |
22 | 10.7k | } |
23 | | avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb) |
24 | 0 | { |
25 | 0 | (void)rgb; |
26 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
27 | 0 | } |
28 | | avifResult avifRGBImageUnpremultiplyAlphaLibYUV(avifRGBImage * rgb) |
29 | 0 | { |
30 | 0 | (void)rgb; |
31 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
32 | 0 | } |
33 | | avifResult avifRGBImageToF16LibYUV(avifRGBImage * rgb) |
34 | 0 | { |
35 | 0 | (void)rgb; |
36 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
37 | 0 | } |
38 | | unsigned int avifLibYUVVersion(void) |
39 | 0 | { |
40 | 0 | return 0; |
41 | 0 | } |
42 | | |
43 | | #else |
44 | | |
45 | | #include <assert.h> |
46 | | #include <limits.h> |
47 | | #include <string.h> |
48 | | |
49 | | #if defined(__clang__) |
50 | | #pragma clang diagnostic push |
51 | | #pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype" |
52 | | // The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored |
53 | | // in version 1813: |
54 | | // https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182 |
55 | | // https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834 |
56 | | #pragma clang diagnostic ignored "-Wnewline-eof" // "no newline at end of file" |
57 | | #endif |
58 | | #include <libyuv.h> |
59 | | #if defined(__clang__) |
60 | | #pragma clang diagnostic pop |
61 | | #endif |
62 | | |
63 | | // libyuv is a C++ library and defines custom types (struct, enum, etc) in the libyuv namespace when the libyuv header files are |
64 | | // included by C++ code. When accessed from a C library like libavif, via a function pointer, this leads to signature mismatches |
65 | | // in the CFI sanitizers since libyuv itself, compiled as C++ code, has the types within the namespace and the C code has the |
66 | | // types without the namespace. The same thing happens with clang's undefined behavior sanitizer as well when invoked with |
67 | | // -fsanitize=function. So we suppress both of these sanitizers in functions that call libyuv functions via a pointer. |
68 | | // For a simpler example of this bug, please see: https://github.com/vigneshvg/cpp_c_potential_cfi_bug. |
69 | | // For more details on clang's CFI see: https://clang.llvm.org/docs/ControlFlowIntegrity.html. |
70 | | // For more details on clang's UBSan see: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html |
71 | | #if defined(__clang__) |
72 | | #define IGNORE_CFI_ICALL __attribute__((no_sanitize("cfi-icall", "function"))) |
73 | | #else |
74 | | #define IGNORE_CFI_ICALL |
75 | | #endif |
76 | | |
77 | | //-------------------------------------------------------------------------------------------------- |
78 | | // libyuv API availability management |
79 | | |
80 | | // These defines are used to create a NULL reference to libyuv functions that |
81 | | // did not exist prior to a particular version of libyuv. |
82 | | // Versions prior to 1755 are considered too old and not used (see CMakeLists.txt). |
83 | | #if LIBYUV_VERSION < 1844 |
84 | | // I444ToRGB24Matrix() and I422ToRGB24MatrixFilter() were added in libyuv version 1844. |
85 | | // |
86 | | // Note: Between the following two commits, libyuv version jumped from 1841 to 1844, down to 1843, |
87 | | // and back to 1844. See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3906082 and |
88 | | // https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3906091. |
89 | | #define I444ToRGB24Matrix NULL |
90 | | #define I422ToRGB24MatrixFilter NULL |
91 | | #endif |
92 | | #if LIBYUV_VERSION < 1841 |
93 | | // I420ToRGB24MatrixFilter() was added in libyuv version 1841. |
94 | | // See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3900298. |
95 | | #define I420ToRGB24MatrixFilter NULL |
96 | | #endif |
97 | | #if LIBYUV_VERSION < 1840 |
98 | | #define ABGRToJ400 NULL |
99 | | #endif |
100 | | #if LIBYUV_VERSION < 1838 |
101 | | #define I422ToRGB565Matrix NULL |
102 | | #endif |
103 | | #if LIBYUV_VERSION < 1813 |
104 | | #define I422ToARGBMatrixFilter NULL |
105 | | #define I420ToARGBMatrixFilter NULL |
106 | | #define I210ToARGBMatrixFilter NULL |
107 | | #define I010ToARGBMatrixFilter NULL |
108 | | #define I420AlphaToARGBMatrixFilter NULL |
109 | | #define I422AlphaToARGBMatrixFilter NULL |
110 | | #define I010AlphaToARGBMatrixFilter NULL |
111 | | #define I210AlphaToARGBMatrixFilter NULL |
112 | | #endif |
113 | | #if LIBYUV_VERSION < 1782 |
114 | | #define RAWToJ420 NULL |
115 | | #endif |
116 | | #if LIBYUV_VERSION < 1781 |
117 | | #define I012ToARGBMatrix NULL |
118 | | #endif |
119 | | #if LIBYUV_VERSION < 1780 |
120 | | #define I410ToARGBMatrix NULL |
121 | | #define I410AlphaToARGBMatrix NULL |
122 | | #define I210AlphaToARGBMatrix NULL |
123 | | #define I010AlphaToARGBMatrix NULL |
124 | | #endif |
125 | | #if LIBYUV_VERSION < 1771 |
126 | | #define I422AlphaToARGBMatrix NULL |
127 | | #define I444AlphaToARGBMatrix NULL |
128 | | #endif |
129 | | #if LIBYUV_VERSION < 1756 |
130 | | #define I400ToARGBMatrix NULL |
131 | | #endif |
132 | | |
133 | | // Two-step replacement for the conversions to 8-bit BT.601 YUV which are missing from libyuv. |
134 | | static int avifReorderARGBThenConvertToYUV(int (*ReorderARGB)(const uint8_t *, int, uint8_t *, int, int, int), |
135 | | int (*ConvertToYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int), |
136 | | const uint8_t * src_abgr, |
137 | | int src_stride_abgr, |
138 | | uint8_t * dst_y, |
139 | | int dst_stride_y, |
140 | | uint8_t * dst_u, |
141 | | int dst_stride_u, |
142 | | uint8_t * dst_v, |
143 | | int dst_stride_v, |
144 | | avifPixelFormat dst_format, |
145 | | int width, |
146 | | int height) |
147 | | { |
148 | | // Only the vertically subsampled formats need to be processed by luma row pairs. |
149 | | avifPixelFormatInfo format_info; |
150 | | avifGetPixelFormatInfo(dst_format, &format_info); |
151 | | const int min_num_rows = (format_info.chromaShiftY == 1) ? 2 : 1; |
152 | | |
153 | | // A temporary buffer is needed to call ReorderARGB(). |
154 | | uint8_t * src_argb; |
155 | | const int src_stride_argb = width * 4; |
156 | | const int soft_allocation_limit = 16384; // Arbitrarily chosen trade-off between CPU and memory footprints. |
157 | | int num_allocated_rows; |
158 | | if ((height == 1) || ((int64_t)src_stride_argb * height <= soft_allocation_limit)) { |
159 | | // Process the whole buffer in one go. |
160 | | num_allocated_rows = height; |
161 | | } else { |
162 | | if ((int64_t)src_stride_argb * min_num_rows > INT_MAX) { |
163 | | return -1; |
164 | | } |
165 | | // The last row of an odd number of RGB rows to be converted to vertically subsampled YUV is treated |
166 | | // differently by libyuv, so make sure all steps but the last one process a multiple of min_num_rows rows. |
167 | | // Try to process the highest multiple of min_num_rows rows possible in a single step without |
168 | | // allocating more than soft_allocation_limit, unless min_num_rows rows need more than that. |
169 | | num_allocated_rows = AVIF_MAX(1, soft_allocation_limit / (src_stride_argb * min_num_rows)) * min_num_rows; |
170 | | } |
171 | | src_argb = (uint8_t *)avifAlloc(num_allocated_rows * src_stride_argb); |
172 | | if (!src_argb) { |
173 | | return -1; |
174 | | } |
175 | | |
176 | | for (int y = 0; y < height; y += num_allocated_rows) { |
177 | | const int num_rows = AVIF_MIN(num_allocated_rows, height - y); |
178 | | if (ReorderARGB(src_abgr, src_stride_abgr, src_argb, src_stride_argb, width, num_rows) || |
179 | | ConvertToYUV(src_argb, src_stride_argb, dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, num_rows)) { |
180 | | avifFree(src_argb); |
181 | | return -1; |
182 | | } |
183 | | src_abgr += (size_t)num_rows * src_stride_abgr; |
184 | | dst_y += (size_t)num_rows * dst_stride_y; |
185 | | // Either chroma is not vertically subsampled, num_rows is even, or this is the last iteration. |
186 | | dst_u += (size_t)(num_rows >> format_info.chromaShiftY) * dst_stride_u; |
187 | | dst_v += (size_t)(num_rows >> format_info.chromaShiftY) * dst_stride_v; |
188 | | } |
189 | | avifFree(src_argb); |
190 | | return 0; |
191 | | } |
192 | | |
193 | | #define AVIF_DEFINE_CONVERSION(NAME, REORDER_ARGB, CONVERT_TO_YUV, YUV_FORMAT) \ |
194 | | static int NAME(const uint8_t * src_abgr, \ |
195 | | int src_stride_abgr, \ |
196 | | uint8_t * dst_y, \ |
197 | | int dst_stride_y, \ |
198 | | uint8_t * dst_u, \ |
199 | | int dst_stride_u, \ |
200 | | uint8_t * dst_v, \ |
201 | | int dst_stride_v, \ |
202 | | int width, \ |
203 | | int height) \ |
204 | | { \ |
205 | | return avifReorderARGBThenConvertToYUV(REORDER_ARGB, \ |
206 | | CONVERT_TO_YUV, \ |
207 | | src_abgr, \ |
208 | | src_stride_abgr, \ |
209 | | dst_y, \ |
210 | | dst_stride_y, \ |
211 | | dst_u, \ |
212 | | dst_stride_u, \ |
213 | | dst_v, \ |
214 | | dst_stride_v, \ |
215 | | YUV_FORMAT, \ |
216 | | width, \ |
217 | | height); \ |
218 | | } |
219 | | |
220 | | #if LIBYUV_VERSION < 1840 |
221 | | // AVIF_RGB_FORMAT_RGBA |
222 | | AVIF_DEFINE_CONVERSION(ABGRToJ422, ABGRToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) |
223 | | AVIF_DEFINE_CONVERSION(ABGRToJ420, ABGRToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) |
224 | | #endif |
225 | | |
226 | | // These are not yet implemented in libyuv so they cannot be guarded by a version check. |
227 | | // The "avif" prefix avoids any redefinition if they are available in libyuv one day. |
228 | | // AVIF_RGB_FORMAT_RGB |
229 | | AVIF_DEFINE_CONVERSION(avifRAWToI444, RAWToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) |
230 | | AVIF_DEFINE_CONVERSION(avifRAWToI422, RAWToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) |
231 | | AVIF_DEFINE_CONVERSION(avifRAWToJ422, RAWToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) |
232 | | // AVIF_RGB_FORMAT_RGBA |
233 | | AVIF_DEFINE_CONVERSION(avifABGRToI444, ABGRToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) |
234 | | AVIF_DEFINE_CONVERSION(avifABGRToI422, ABGRToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) |
235 | | // AVIF_RGB_FORMAT_ARGB |
236 | | AVIF_DEFINE_CONVERSION(avifBGRAToI444, BGRAToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) |
237 | | AVIF_DEFINE_CONVERSION(avifBGRAToI422, BGRAToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) |
238 | | AVIF_DEFINE_CONVERSION(avifBGRAToJ422, BGRAToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) |
239 | | AVIF_DEFINE_CONVERSION(avifBGRAToJ420, BGRAToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) |
240 | | // AVIF_RGB_FORMAT_BGR |
241 | | AVIF_DEFINE_CONVERSION(avifRGB24ToI444, RGB24ToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) |
242 | | AVIF_DEFINE_CONVERSION(avifRGB24ToI422, RGB24ToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) |
243 | | AVIF_DEFINE_CONVERSION(avifRGB24ToJ422, RGB24ToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) |
244 | | // AVIF_RGB_FORMAT_ABGR |
245 | | AVIF_DEFINE_CONVERSION(avifRGBAToI444, RGBAToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) |
246 | | AVIF_DEFINE_CONVERSION(avifRGBAToI422, RGBAToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) |
247 | | AVIF_DEFINE_CONVERSION(avifRGBAToJ422, RGBAToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) |
248 | | AVIF_DEFINE_CONVERSION(avifRGBAToJ420, RGBAToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) |
249 | | |
250 | | //-------------------------------------------------------------------------------------------------- |
251 | | // RGB to YUV |
252 | | |
253 | | static avifResult avifImageRGBToYUVLibYUV8bpc(avifImage * image, const avifRGBImage * rgb); |
254 | | |
255 | | avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb) |
256 | | { |
257 | | // The width, height, and stride parameters of libyuv functions are all of the int type. |
258 | | if (image->width > INT_MAX || image->height > INT_MAX || image->yuvRowBytes[AVIF_CHAN_Y] > INT_MAX || rgb->rowBytes > INT_MAX) { |
259 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
260 | | } |
261 | | |
262 | | if ((image->depth == 8) && (rgb->depth == 8)) { |
263 | | return avifImageRGBToYUVLibYUV8bpc(image, rgb); |
264 | | } |
265 | | |
266 | | // This function didn't do anything; use the built-in conversion. |
267 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
268 | | } |
269 | | |
270 | | avifResult avifImageRGBToYUVLibYUV8bpc(avifImage * image, const avifRGBImage * rgb) |
271 | | { |
272 | | assert((image->depth == 8) && (rgb->depth == 8)); |
273 | | // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, |
274 | | // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. |
275 | | |
276 | | // libyuv only handles BT.601 for RGB to YUV, and not all range/order/subsampling combinations. |
277 | | // BT.470BG has the same coefficients as BT.601. |
278 | | if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT470BG) || (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT601)) { |
279 | | if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { |
280 | | // Lookup table for RGB To Y (monochrome). |
281 | | typedef int (*RGBtoY)(const uint8_t *, int, uint8_t *, int, int, int); |
282 | | // First dimension is for avifRange. |
283 | | RGBtoY lutRgbToY[2][AVIF_RGB_FORMAT_COUNT] = { // AVIF_RANGE_LIMITED |
284 | | { |
285 | | // // AVIF_RGB_FORMAT_ |
286 | | NULL, // RGB |
287 | | NULL, // RGBA |
288 | | NULL, // ARGB |
289 | | NULL, // BGR |
290 | | ARGBToI400, // BGRA |
291 | | NULL, // ABGR |
292 | | NULL, // RGB_565 |
293 | | }, |
294 | | // AVIF_RANGE_FULL |
295 | | { |
296 | | // // AVIF_RGB_FORMAT_ |
297 | | RAWToJ400, // RGB |
298 | | ABGRToJ400, // RGBA |
299 | | NULL, // ARGB |
300 | | RGB24ToJ400, // BGR |
301 | | ARGBToJ400, // BGRA |
302 | | RGBAToJ400, // ABGR |
303 | | NULL // RGB_565 |
304 | | } |
305 | | }; |
306 | | RGBtoY rgbToY = lutRgbToY[image->yuvRange][rgb->format]; |
307 | | if (rgbToY != NULL) { |
308 | | if (rgbToY(rgb->pixels, |
309 | | rgb->rowBytes, |
310 | | image->yuvPlanes[AVIF_CHAN_Y], |
311 | | image->yuvRowBytes[AVIF_CHAN_Y], |
312 | | image->width, |
313 | | image->height) != 0) { |
314 | | return AVIF_RESULT_REFORMAT_FAILED; |
315 | | } |
316 | | return AVIF_RESULT_OK; |
317 | | } |
318 | | } else { |
319 | | // Lookup table for RGB To YUV Matrix (average filter). |
320 | | typedef int (*RGBtoYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int); |
321 | | // First dimension is for avifRange. |
322 | | RGBtoYUV lutRgbToYuv[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
323 | | // AVIF_RANGE_LIMITED |
324 | | { |
325 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
326 | | { NULL, avifRAWToI444, avifRAWToI422, RAWToI420, NULL }, // RGB |
327 | | { NULL, avifABGRToI444, avifABGRToI422, ABGRToI420, NULL }, // RGBA |
328 | | { NULL, avifBGRAToI444, avifBGRAToI422, BGRAToI420, NULL }, // ARGB |
329 | | { NULL, avifRGB24ToI444, avifRGB24ToI422, RGB24ToI420, NULL }, // BGR |
330 | | { NULL, ARGBToI444, ARGBToI422, ARGBToI420, NULL }, // BGRA |
331 | | { NULL, avifRGBAToI444, avifRGBAToI422, RGBAToI420, NULL }, // ABGR |
332 | | { NULL, NULL, NULL, NULL, NULL } // RGB_565 |
333 | | }, |
334 | | // AVIF_RANGE_FULL |
335 | | { |
336 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
337 | | { NULL, NULL, avifRAWToJ422, RAWToJ420, NULL }, // RGB |
338 | | { NULL, NULL, ABGRToJ422, ABGRToJ420, NULL }, // RGBA |
339 | | { NULL, NULL, avifBGRAToJ422, avifBGRAToJ420, NULL }, // ARGB |
340 | | { NULL, NULL, avifRGB24ToJ422, RGB24ToJ420, NULL }, // BGR |
341 | | { NULL, NULL, ARGBToJ422, ARGBToJ420, NULL }, // BGRA |
342 | | { NULL, NULL, avifRGBAToJ422, avifRGBAToJ420, NULL }, // ABGR |
343 | | { NULL, NULL, NULL, NULL, NULL } // RGB_565 |
344 | | } |
345 | | }; |
346 | | RGBtoYUV rgbToYuv = lutRgbToYuv[image->yuvRange][rgb->format][image->yuvFormat]; |
347 | | if (rgbToYuv != NULL) { |
348 | | if (rgbToYuv(rgb->pixels, |
349 | | rgb->rowBytes, |
350 | | image->yuvPlanes[AVIF_CHAN_Y], |
351 | | image->yuvRowBytes[AVIF_CHAN_Y], |
352 | | image->yuvPlanes[AVIF_CHAN_U], |
353 | | image->yuvRowBytes[AVIF_CHAN_U], |
354 | | image->yuvPlanes[AVIF_CHAN_V], |
355 | | image->yuvRowBytes[AVIF_CHAN_V], |
356 | | image->width, |
357 | | image->height) != 0) { |
358 | | return AVIF_RESULT_REFORMAT_FAILED; |
359 | | } |
360 | | return AVIF_RESULT_OK; |
361 | | } |
362 | | } |
363 | | } |
364 | | // TODO: Use SplitRGBPlane() for AVIF_MATRIX_COEFFICIENTS_IDENTITY if faster than the built-in implementation |
365 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
366 | | } |
367 | | |
368 | | //-------------------------------------------------------------------------------------------------- |
369 | | // YUV to RGB |
370 | | |
371 | | // Note about the libyuv look up tables used for YUV-to-RGB conversion: |
372 | | // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, similar to PNG. libyuv |
373 | | // orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. In addition, swapping U and V in any of the |
374 | | // calls, along with using the Yvu matrix instead of Yuv matrix, swaps B and R in these orderings as well. |
375 | | // |
376 | | // libavif format libyuv Func UV matrix (and UV argument ordering) |
377 | | // -------------------- ------------- ------------------------------------ |
378 | | // For 8-bit YUV: |
379 | | // AVIF_RGB_FORMAT_RGB *ToRGB24Matrix matrixYVU |
380 | | // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU |
381 | | // AVIF_RGB_FORMAT_ARGB *ToRGBAMatrix matrixYVU |
382 | | // AVIF_RGB_FORMAT_BGR *ToRGB24Matrix matrixYUV |
383 | | // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV |
384 | | // AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV |
385 | | // AVIF_RGB_FORMAT_RGB_565 *ToRGB565Matrix matrixYUV |
386 | | // |
387 | | // For 10-bit and 12-bit YUV: |
388 | | // AVIF_RGB_FORMAT_RGB n/a n/a |
389 | | // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU |
390 | | // AVIF_RGB_FORMAT_ARGB n/a n/a |
391 | | // AVIF_RGB_FORMAT_BGR n/a n/a |
392 | | // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV |
393 | | // AVIF_RGB_FORMAT_ABGR n/a n/a |
394 | | // AVIF_RGB_FORMAT_RGB_565 n/a n/a |
395 | | |
396 | | // Lookup table for isYVU. If the entry in this table is AVIF_TRUE, then it |
397 | | // means that we are using a libyuv function with R and B channels swapped, |
398 | | // which requires U and V planes also be swapped. |
399 | | static const avifBool lutIsYVU[AVIF_RGB_FORMAT_COUNT] = { |
400 | | // // AVIF_RGB_FORMAT_ |
401 | | AVIF_TRUE, // RGB |
402 | | AVIF_TRUE, // RGBA |
403 | | AVIF_TRUE, // ARGB |
404 | | AVIF_FALSE, // BGR |
405 | | AVIF_FALSE, // BGRA |
406 | | AVIF_FALSE, // ABGR |
407 | | AVIF_FALSE, // RGB_565 |
408 | | }; |
409 | | |
410 | | typedef int (*YUV400ToRGBMatrix)(const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int); |
411 | | typedef int (*YUVToRGBMatrixFilter)(const uint8_t *, |
412 | | int, |
413 | | const uint8_t *, |
414 | | int, |
415 | | const uint8_t *, |
416 | | int, |
417 | | uint8_t *, |
418 | | int, |
419 | | const struct YuvConstants *, |
420 | | int, |
421 | | int, |
422 | | enum FilterMode); |
423 | | typedef int (*YUVAToRGBMatrixFilter)(const uint8_t *, |
424 | | int, |
425 | | const uint8_t *, |
426 | | int, |
427 | | const uint8_t *, |
428 | | int, |
429 | | const uint8_t *, |
430 | | int, |
431 | | uint8_t *, |
432 | | int, |
433 | | const struct YuvConstants *, |
434 | | int, |
435 | | int, |
436 | | int, |
437 | | enum FilterMode); |
438 | | typedef int (*YUVToRGBMatrix)(const uint8_t *, int, const uint8_t *, int, const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int); |
439 | | typedef int (*YUVAToRGBMatrix)(const uint8_t *, |
440 | | int, |
441 | | const uint8_t *, |
442 | | int, |
443 | | const uint8_t *, |
444 | | int, |
445 | | const uint8_t *, |
446 | | int, |
447 | | uint8_t *, |
448 | | int, |
449 | | const struct YuvConstants *, |
450 | | int, |
451 | | int, |
452 | | int); |
453 | | typedef int (*YUVToRGBMatrixFilterHighBitDepth)(const uint16_t *, |
454 | | int, |
455 | | const uint16_t *, |
456 | | int, |
457 | | const uint16_t *, |
458 | | int, |
459 | | uint8_t *, |
460 | | int, |
461 | | const struct YuvConstants *, |
462 | | int, |
463 | | int, |
464 | | enum FilterMode); |
465 | | typedef int (*YUVAToRGBMatrixFilterHighBitDepth)(const uint16_t *, |
466 | | int, |
467 | | const uint16_t *, |
468 | | int, |
469 | | const uint16_t *, |
470 | | int, |
471 | | const uint16_t *, |
472 | | int, |
473 | | uint8_t *, |
474 | | int, |
475 | | const struct YuvConstants *, |
476 | | int, |
477 | | int, |
478 | | int, |
479 | | enum FilterMode); |
480 | | typedef int (*YUVToRGBMatrixHighBitDepth)(const uint16_t *, |
481 | | int, |
482 | | const uint16_t *, |
483 | | int, |
484 | | const uint16_t *, |
485 | | int, |
486 | | uint8_t *, |
487 | | int, |
488 | | const struct YuvConstants *, |
489 | | int, |
490 | | int); |
491 | | typedef int (*YUVAToRGBMatrixHighBitDepth)(const uint16_t *, |
492 | | int, |
493 | | const uint16_t *, |
494 | | int, |
495 | | const uint16_t *, |
496 | | int, |
497 | | const uint16_t *, |
498 | | int, |
499 | | uint8_t *, |
500 | | int, |
501 | | const struct YuvConstants *, |
502 | | int, |
503 | | int, |
504 | | int); |
505 | | |
506 | | // At most one pointer in this struct will be not-NULL. |
507 | | typedef struct |
508 | | { |
509 | | YUV400ToRGBMatrix yuv400ToRgbMatrix; |
510 | | YUVToRGBMatrixFilter yuvToRgbMatrixFilter; |
511 | | YUVAToRGBMatrixFilter yuvaToRgbMatrixFilter; |
512 | | YUVToRGBMatrix yuvToRgbMatrix; |
513 | | YUVAToRGBMatrix yuvaToRgbMatrix; |
514 | | YUVToRGBMatrixFilterHighBitDepth yuvToRgbMatrixFilterHighBitDepth; |
515 | | YUVAToRGBMatrixFilterHighBitDepth yuvaToRgbMatrixFilterHighBitDepth; |
516 | | YUVToRGBMatrixHighBitDepth yuvToRgbMatrixHighBitDepth; |
517 | | YUVAToRGBMatrixHighBitDepth yuvaToRgbMatrixHighBitDepth; |
518 | | } LibyuvConversionFunction; |
519 | | |
520 | | // Only allow nearest-neighbor filter if explicitly specified or left as default. |
521 | | static avifBool nearestNeighborFilterAllowed(int chromaUpsampling) |
522 | | { |
523 | | return chromaUpsampling != AVIF_CHROMA_UPSAMPLING_BILINEAR && chromaUpsampling != AVIF_CHROMA_UPSAMPLING_BEST_QUALITY; |
524 | | } |
525 | | |
526 | | // Returns AVIF_TRUE if the given yuvFormat and yuvDepth can be converted to 8-bit RGB using libyuv, AVIF_FALSE otherwise. When |
527 | | // AVIF_TRUE is returned, exactly one function pointers will be populated with the appropriate conversion function. If |
528 | | // alphaPreferred is set to AVIF_TRUE, then a function that can also copy the alpha channel will be preferred if available. |
529 | | static avifBool getLibYUVConversionFunction(avifPixelFormat yuvFormat, |
530 | | int yuvDepth, |
531 | | avifRGBImage * rgb, |
532 | | avifBool alphaPreferred, |
533 | | LibyuvConversionFunction * lcf) |
534 | | { |
535 | | // Lookup table for 8-bit YUV400 to 8-bit RGB Matrix. |
536 | | static const YUV400ToRGBMatrix lutYuv400ToRgbMatrix[AVIF_RGB_FORMAT_COUNT] = { |
537 | | // // AVIF_RGB_FORMAT_ |
538 | | NULL, // RGB |
539 | | I400ToARGBMatrix, // RGBA |
540 | | NULL, // ARGB |
541 | | NULL, // BGR |
542 | | I400ToARGBMatrix, // BGRA |
543 | | NULL, // ABGR |
544 | | NULL, // RGB_565 |
545 | | }; |
546 | | |
547 | | // Lookup table for 8-bit YUV To 8-bit RGB Matrix (with filter). |
548 | | static const YUVToRGBMatrixFilter lutYuvToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
549 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
550 | | { NULL, NULL, I422ToRGB24MatrixFilter, I420ToRGB24MatrixFilter, NULL }, // RGB |
551 | | { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // RGBA |
552 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
553 | | { NULL, NULL, I422ToRGB24MatrixFilter, I420ToRGB24MatrixFilter, NULL }, // BGR |
554 | | { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // BGRA |
555 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
556 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
557 | | }; |
558 | | |
559 | | // Lookup table for 8-bit YUVA To 8-bit RGB Matrix (with filter). |
560 | | static const YUVAToRGBMatrixFilter lutYuvaToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
561 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
562 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
563 | | { NULL, NULL, I422AlphaToARGBMatrixFilter, I420AlphaToARGBMatrixFilter, NULL }, // RGBA |
564 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
565 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
566 | | { NULL, NULL, I422AlphaToARGBMatrixFilter, I420AlphaToARGBMatrixFilter, NULL }, // BGRA |
567 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
568 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
569 | | }; |
570 | | |
571 | | // Lookup table for 8-bit YUV To 8-bit RGB Matrix (4:4:4 or nearest-neighbor filter). |
572 | | static const YUVToRGBMatrix lutYuvToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
573 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
574 | | { NULL, I444ToRGB24Matrix, NULL, I420ToRGB24Matrix, NULL }, // RGB |
575 | | { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // RGBA |
576 | | { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ARGB |
577 | | { NULL, I444ToRGB24Matrix, NULL, I420ToRGB24Matrix, NULL }, // BGR |
578 | | { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // BGRA |
579 | | { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ABGR |
580 | | { NULL, NULL, I422ToRGB565Matrix, I420ToRGB565Matrix, NULL }, // RGB_565 |
581 | | }; |
582 | | |
583 | | // Lookup table for 8-bit YUVA To 8-bit RGB Matrix (4:4:4 or nearest-neighbor filter). |
584 | | static const YUVAToRGBMatrix lutYuvaToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
585 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
586 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
587 | | { NULL, I444AlphaToARGBMatrix, I422AlphaToARGBMatrix, I420AlphaToARGBMatrix, NULL }, // RGBA |
588 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
589 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
590 | | { NULL, I444AlphaToARGBMatrix, I422AlphaToARGBMatrix, I420AlphaToARGBMatrix, NULL }, // BGRA |
591 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
592 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
593 | | }; |
594 | | |
595 | | // Lookup table for YUV To RGB Matrix (with filter). First dimension is for the YUV bit depth. |
596 | | static const YUVToRGBMatrixFilterHighBitDepth lutYuvToRgbMatrixFilterHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
597 | | // 10bpc |
598 | | { |
599 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
600 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
601 | | { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // RGBA |
602 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
603 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
604 | | { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // BGRA |
605 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
606 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
607 | | }, |
608 | | // 12bpc |
609 | | { |
610 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
611 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
612 | | { NULL, NULL, NULL, NULL, NULL }, // RGBA |
613 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
614 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
615 | | { NULL, NULL, NULL, NULL, NULL }, // BGRA |
616 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
617 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
618 | | }, |
619 | | }; |
620 | | |
621 | | // Lookup table for YUVA To RGB Matrix (with filter). First dimension is for the YUV bit depth. |
622 | | static const YUVAToRGBMatrixFilterHighBitDepth lutYuvaToRgbMatrixFilterHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
623 | | // 10bpc |
624 | | { |
625 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
626 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
627 | | { NULL, NULL, I210AlphaToARGBMatrixFilter, I010AlphaToARGBMatrixFilter, NULL }, // RGBA |
628 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
629 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
630 | | { NULL, NULL, I210AlphaToARGBMatrixFilter, I010AlphaToARGBMatrixFilter, NULL }, // BGRA |
631 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
632 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
633 | | }, |
634 | | // 12bpc |
635 | | { |
636 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
637 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
638 | | { NULL, NULL, NULL, NULL, NULL }, // RGBA |
639 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
640 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
641 | | { NULL, NULL, NULL, NULL, NULL }, // BGRA |
642 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
643 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
644 | | }, |
645 | | }; |
646 | | |
647 | | // Lookup table for YUV To RGB Matrix (4:4:4 or nearest-neighbor filter). First dimension is for the YUV bit depth. |
648 | | static const YUVToRGBMatrixHighBitDepth lutYuvToRgbMatrixHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
649 | | // 10bpc |
650 | | { |
651 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
652 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
653 | | { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // RGBA |
654 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
655 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
656 | | { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // BGRA |
657 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
658 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
659 | | }, |
660 | | // 12bpc |
661 | | { |
662 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
663 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
664 | | { NULL, NULL, NULL, I012ToARGBMatrix, NULL }, // RGBA |
665 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
666 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
667 | | { NULL, NULL, NULL, I012ToARGBMatrix, NULL }, // BGRA |
668 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
669 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
670 | | }, |
671 | | }; |
672 | | |
673 | | // Lookup table for YUVA To RGB Matrix (4:4:4 or nearest-neighbor filter). First dimension is for the YUV bit depth. |
674 | | static const YUVAToRGBMatrixHighBitDepth lutYuvaToRgbMatrixHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { |
675 | | // 10bpc |
676 | | { |
677 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
678 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
679 | | { NULL, I410AlphaToARGBMatrix, I210AlphaToARGBMatrix, I010AlphaToARGBMatrix, NULL }, // RGBA |
680 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
681 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
682 | | { NULL, I410AlphaToARGBMatrix, I210AlphaToARGBMatrix, I010AlphaToARGBMatrix, NULL }, // BGRA |
683 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
684 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
685 | | }, |
686 | | // 12bpc |
687 | | { |
688 | | // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ |
689 | | { NULL, NULL, NULL, NULL, NULL }, // RGB |
690 | | { NULL, NULL, NULL, NULL, NULL }, // RGBA |
691 | | { NULL, NULL, NULL, NULL, NULL }, // ARGB |
692 | | { NULL, NULL, NULL, NULL, NULL }, // BGR |
693 | | { NULL, NULL, NULL, NULL, NULL }, // BGRA |
694 | | { NULL, NULL, NULL, NULL, NULL }, // ABGR |
695 | | { NULL, NULL, NULL, NULL, NULL }, // RGB_565 |
696 | | }, |
697 | | }; |
698 | | |
699 | | memset(lcf, 0, sizeof(*lcf)); |
700 | | assert(rgb->depth == 8); |
701 | | if (yuvDepth > 8) { |
702 | | assert(yuvDepth == 10 || yuvDepth == 12); |
703 | | int depthIndex = (yuvDepth == 10) ? 0 : 1; |
704 | | if (yuvFormat != AVIF_PIXEL_FORMAT_YUV444) { |
705 | | if (alphaPreferred) { |
706 | | lcf->yuvaToRgbMatrixFilterHighBitDepth = lutYuvaToRgbMatrixFilterHighBitDepth[depthIndex][rgb->format][yuvFormat]; |
707 | | if (lcf->yuvaToRgbMatrixFilterHighBitDepth != NULL) { |
708 | | return AVIF_TRUE; |
709 | | } |
710 | | } |
711 | | lcf->yuvToRgbMatrixFilterHighBitDepth = lutYuvToRgbMatrixFilterHighBitDepth[depthIndex][rgb->format][yuvFormat]; |
712 | | if (lcf->yuvToRgbMatrixFilterHighBitDepth != NULL) { |
713 | | return AVIF_TRUE; |
714 | | } |
715 | | } |
716 | | if (yuvFormat == AVIF_PIXEL_FORMAT_YUV444 || nearestNeighborFilterAllowed(rgb->chromaUpsampling)) { |
717 | | if (alphaPreferred) { |
718 | | lcf->yuvaToRgbMatrixHighBitDepth = lutYuvaToRgbMatrixHighBitDepth[depthIndex][rgb->format][yuvFormat]; |
719 | | if (lcf->yuvaToRgbMatrixHighBitDepth != NULL) { |
720 | | return AVIF_TRUE; |
721 | | } |
722 | | } |
723 | | lcf->yuvToRgbMatrixHighBitDepth = lutYuvToRgbMatrixHighBitDepth[depthIndex][rgb->format][yuvFormat]; |
724 | | if (lcf->yuvToRgbMatrixHighBitDepth != NULL) { |
725 | | return AVIF_TRUE; |
726 | | } |
727 | | } |
728 | | // Fallthrough is intentional. No high bitdepth libyuv function was found. Check if there is an 8-bit libyuv function which |
729 | | // can used with a downshift. |
730 | | } |
731 | | if (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { |
732 | | lcf->yuv400ToRgbMatrix = lutYuv400ToRgbMatrix[rgb->format]; |
733 | | return lcf->yuv400ToRgbMatrix != NULL; |
734 | | } |
735 | | if (yuvFormat != AVIF_PIXEL_FORMAT_YUV444) { |
736 | | if (alphaPreferred) { |
737 | | lcf->yuvaToRgbMatrixFilter = lutYuvaToRgbMatrixFilter[rgb->format][yuvFormat]; |
738 | | if (lcf->yuvaToRgbMatrixFilter != NULL) { |
739 | | return AVIF_TRUE; |
740 | | } |
741 | | } |
742 | | lcf->yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[rgb->format][yuvFormat]; |
743 | | if (lcf->yuvToRgbMatrixFilter != NULL) { |
744 | | return AVIF_TRUE; |
745 | | } |
746 | | if (!nearestNeighborFilterAllowed(rgb->chromaUpsampling)) { |
747 | | return AVIF_FALSE; |
748 | | } |
749 | | } |
750 | | if (alphaPreferred) { |
751 | | lcf->yuvaToRgbMatrix = lutYuvaToRgbMatrix[rgb->format][yuvFormat]; |
752 | | if (lcf->yuvaToRgbMatrix != NULL) { |
753 | | return AVIF_TRUE; |
754 | | } |
755 | | } |
756 | | lcf->yuvToRgbMatrix = lutYuvToRgbMatrix[rgb->format][yuvFormat]; |
757 | | return lcf->yuvToRgbMatrix != NULL; |
758 | | } |
759 | | |
760 | | static void getLibYUVConstants(const avifImage * image, const struct YuvConstants ** matrixYUV, const struct YuvConstants ** matrixYVU) |
761 | | { |
762 | | // Allow the identity matrix to be used with YUV 4:0:0. Replace the identity matrix with |
763 | | // MatrixCoefficients 6 (BT.601). |
764 | | const avifBool yuv400WithIdentityMatrix = (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) && |
765 | | (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY); |
766 | | const avifMatrixCoefficients matrixCoefficients = yuv400WithIdentityMatrix ? AVIF_MATRIX_COEFFICIENTS_BT601 : image->matrixCoefficients; |
767 | | if (image->yuvRange == AVIF_RANGE_FULL) { |
768 | | switch (matrixCoefficients) { |
769 | | // BT.709 full range YuvConstants were added in libyuv version 1772. |
770 | | // See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/2646472. |
771 | | case AVIF_MATRIX_COEFFICIENTS_BT709: |
772 | | #if LIBYUV_VERSION >= 1772 |
773 | | *matrixYUV = &kYuvF709Constants; |
774 | | *matrixYVU = &kYvuF709Constants; |
775 | | #endif |
776 | | break; |
777 | | case AVIF_MATRIX_COEFFICIENTS_BT470BG: |
778 | | case AVIF_MATRIX_COEFFICIENTS_BT601: |
779 | | case AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED: |
780 | | *matrixYUV = &kYuvJPEGConstants; |
781 | | *matrixYVU = &kYvuJPEGConstants; |
782 | | break; |
783 | | // BT.2020 full range YuvConstants were added in libyuv version 1775. |
784 | | // See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/2678859. |
785 | | case AVIF_MATRIX_COEFFICIENTS_BT2020_NCL: |
786 | | #if LIBYUV_VERSION >= 1775 |
787 | | *matrixYUV = &kYuvV2020Constants; |
788 | | *matrixYVU = &kYvuV2020Constants; |
789 | | #endif |
790 | | break; |
791 | | case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: |
792 | | switch (image->colorPrimaries) { |
793 | | case AVIF_COLOR_PRIMARIES_BT709: |
794 | | case AVIF_COLOR_PRIMARIES_UNSPECIFIED: |
795 | | #if LIBYUV_VERSION >= 1772 |
796 | | *matrixYUV = &kYuvF709Constants; |
797 | | *matrixYVU = &kYvuF709Constants; |
798 | | #endif |
799 | | break; |
800 | | case AVIF_COLOR_PRIMARIES_BT470BG: |
801 | | case AVIF_COLOR_PRIMARIES_BT601: |
802 | | *matrixYUV = &kYuvJPEGConstants; |
803 | | *matrixYVU = &kYvuJPEGConstants; |
804 | | break; |
805 | | case AVIF_COLOR_PRIMARIES_BT2020: |
806 | | #if LIBYUV_VERSION >= 1775 |
807 | | *matrixYUV = &kYuvV2020Constants; |
808 | | *matrixYVU = &kYvuV2020Constants; |
809 | | #endif |
810 | | break; |
811 | | |
812 | | case AVIF_COLOR_PRIMARIES_UNKNOWN: |
813 | | case AVIF_COLOR_PRIMARIES_BT470M: |
814 | | case AVIF_COLOR_PRIMARIES_SMPTE240: |
815 | | case AVIF_COLOR_PRIMARIES_GENERIC_FILM: |
816 | | case AVIF_COLOR_PRIMARIES_XYZ: |
817 | | case AVIF_COLOR_PRIMARIES_SMPTE431: |
818 | | case AVIF_COLOR_PRIMARIES_SMPTE432: |
819 | | case AVIF_COLOR_PRIMARIES_EBU3213: |
820 | | break; |
821 | | } |
822 | | break; |
823 | | |
824 | | case AVIF_MATRIX_COEFFICIENTS_IDENTITY: |
825 | | case AVIF_MATRIX_COEFFICIENTS_FCC: |
826 | | case AVIF_MATRIX_COEFFICIENTS_SMPTE240: |
827 | | case AVIF_MATRIX_COEFFICIENTS_YCGCO: |
828 | | case AVIF_MATRIX_COEFFICIENTS_BT2020_CL: |
829 | | case AVIF_MATRIX_COEFFICIENTS_SMPTE2085: |
830 | | case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL: |
831 | | case AVIF_MATRIX_COEFFICIENTS_ICTCP: |
832 | | break; |
833 | | } |
834 | | } else { // image->yuvRange == AVIF_RANGE_LIMITED |
835 | | switch (matrixCoefficients) { |
836 | | case AVIF_MATRIX_COEFFICIENTS_BT709: |
837 | | *matrixYUV = &kYuvH709Constants; |
838 | | *matrixYVU = &kYvuH709Constants; |
839 | | break; |
840 | | case AVIF_MATRIX_COEFFICIENTS_BT470BG: |
841 | | case AVIF_MATRIX_COEFFICIENTS_BT601: |
842 | | case AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED: |
843 | | *matrixYUV = &kYuvI601Constants; |
844 | | *matrixYVU = &kYvuI601Constants; |
845 | | break; |
846 | | case AVIF_MATRIX_COEFFICIENTS_BT2020_NCL: |
847 | | *matrixYUV = &kYuv2020Constants; |
848 | | *matrixYVU = &kYvu2020Constants; |
849 | | break; |
850 | | case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: |
851 | | switch (image->colorPrimaries) { |
852 | | case AVIF_COLOR_PRIMARIES_BT709: |
853 | | case AVIF_COLOR_PRIMARIES_UNSPECIFIED: |
854 | | *matrixYUV = &kYuvH709Constants; |
855 | | *matrixYVU = &kYvuH709Constants; |
856 | | break; |
857 | | case AVIF_COLOR_PRIMARIES_BT470BG: |
858 | | case AVIF_COLOR_PRIMARIES_BT601: |
859 | | *matrixYUV = &kYuvI601Constants; |
860 | | *matrixYVU = &kYvuI601Constants; |
861 | | break; |
862 | | case AVIF_COLOR_PRIMARIES_BT2020: |
863 | | *matrixYUV = &kYuv2020Constants; |
864 | | *matrixYVU = &kYvu2020Constants; |
865 | | break; |
866 | | |
867 | | case AVIF_COLOR_PRIMARIES_UNKNOWN: |
868 | | case AVIF_COLOR_PRIMARIES_BT470M: |
869 | | case AVIF_COLOR_PRIMARIES_SMPTE240: |
870 | | case AVIF_COLOR_PRIMARIES_GENERIC_FILM: |
871 | | case AVIF_COLOR_PRIMARIES_XYZ: |
872 | | case AVIF_COLOR_PRIMARIES_SMPTE431: |
873 | | case AVIF_COLOR_PRIMARIES_SMPTE432: |
874 | | case AVIF_COLOR_PRIMARIES_EBU3213: |
875 | | break; |
876 | | } |
877 | | break; |
878 | | case AVIF_MATRIX_COEFFICIENTS_IDENTITY: |
879 | | case AVIF_MATRIX_COEFFICIENTS_FCC: |
880 | | case AVIF_MATRIX_COEFFICIENTS_SMPTE240: |
881 | | case AVIF_MATRIX_COEFFICIENTS_YCGCO: |
882 | | case AVIF_MATRIX_COEFFICIENTS_BT2020_CL: |
883 | | case AVIF_MATRIX_COEFFICIENTS_SMPTE2085: |
884 | | case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL: |
885 | | case AVIF_MATRIX_COEFFICIENTS_ICTCP: |
886 | | break; |
887 | | } |
888 | | } |
889 | | } |
890 | | |
891 | | static avifResult avifImageDownshiftTo8bpc(const avifImage * image, avifImage * image8, avifBool downshiftAlpha) |
892 | | { |
893 | | avifImageSetDefaults(image8); |
894 | | avifImageCopyNoAlloc(image8, image); |
895 | | image8->depth = 8; |
896 | | // downshiftAlpha will be true only if the image has an alpha plane. So it is safe to pass AVIF_PLANES_ALL here in that case. |
897 | | assert(!downshiftAlpha || image->alphaPlane); |
898 | | AVIF_CHECKRES(avifImageAllocatePlanes(image8, downshiftAlpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV)); |
899 | | // 16384 for 10-bit and 4096 for 12-bit. |
900 | | const int scale = 1 << (24 - image->depth); |
901 | | for (int plane = AVIF_CHAN_Y; plane <= (downshiftAlpha ? AVIF_CHAN_A : AVIF_CHAN_V); ++plane) { |
902 | | const uint32_t planeWidth = avifImagePlaneWidth(image, plane); |
903 | | if (planeWidth == 0) { |
904 | | continue; |
905 | | } |
906 | | Convert16To8Plane((const uint16_t *)avifImagePlane(image, plane), |
907 | | avifImagePlaneRowBytes(image, plane) / 2, |
908 | | avifImagePlane(image8, plane), |
909 | | avifImagePlaneRowBytes(image8, plane), |
910 | | scale, |
911 | | planeWidth, |
912 | | avifImagePlaneHeight(image, plane)); |
913 | | } |
914 | | return AVIF_RESULT_OK; |
915 | | } |
916 | | |
917 | | IGNORE_CFI_ICALL avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifBool reformatAlpha, avifBool * alphaReformattedWithLibYUV) |
918 | | { |
919 | | *alphaReformattedWithLibYUV = AVIF_FALSE; |
920 | | // The width, height, and stride parameters of libyuv functions are all of the int type. |
921 | | if (image->width > INT_MAX || image->height > INT_MAX || image->yuvRowBytes[AVIF_CHAN_Y] > INT_MAX || rgb->rowBytes > INT_MAX) { |
922 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
923 | | } |
924 | | if (rgb->depth != 8 || (image->depth != 8 && image->depth != 10 && image->depth != 12)) { |
925 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
926 | | } |
927 | | // Find the correct libyuv YuvConstants, based on range and CP/MC |
928 | | const struct YuvConstants * matrixYUV = NULL; |
929 | | const struct YuvConstants * matrixYVU = NULL; |
930 | | getLibYUVConstants(image, &matrixYUV, &matrixYVU); |
931 | | if (!matrixYVU) { |
932 | | // No YuvConstants exist for the current image; use the built-in YUV conversion |
933 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
934 | | } |
935 | | |
936 | | LibyuvConversionFunction lcf; |
937 | | const avifBool alphaPreferred = reformatAlpha && image->alphaPlane && image->alphaRowBytes; |
938 | | if (!getLibYUVConversionFunction(image->yuvFormat, image->depth, rgb, alphaPreferred, &lcf)) { |
939 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
940 | | } |
941 | | if (!image->alphaPlane || !image->alphaRowBytes) { |
942 | | // If the image does not have an alpha plane, then libyuv always prefills the output RGB image with opaque alpha values. |
943 | | *alphaReformattedWithLibYUV = AVIF_TRUE; |
944 | | } |
945 | | avifBool isYVU = lutIsYVU[rgb->format]; |
946 | | const struct YuvConstants * matrix = isYVU ? matrixYVU : matrixYUV; |
947 | | int libyuvResult = -1; |
948 | | int uPlaneIndex = isYVU ? AVIF_CHAN_V : AVIF_CHAN_U; |
949 | | int vPlaneIndex = isYVU ? AVIF_CHAN_U : AVIF_CHAN_V; |
950 | | const enum FilterMode filter = |
951 | | ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST)) |
952 | | ? kFilterNone |
953 | | : kFilterBilinear; |
954 | | if (lcf.yuvToRgbMatrixFilterHighBitDepth != NULL) { |
955 | | libyuvResult = lcf.yuvToRgbMatrixFilterHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], |
956 | | image->yuvRowBytes[AVIF_CHAN_Y] / 2, |
957 | | (const uint16_t *)image->yuvPlanes[uPlaneIndex], |
958 | | image->yuvRowBytes[uPlaneIndex] / 2, |
959 | | (const uint16_t *)image->yuvPlanes[vPlaneIndex], |
960 | | image->yuvRowBytes[vPlaneIndex] / 2, |
961 | | rgb->pixels, |
962 | | rgb->rowBytes, |
963 | | matrix, |
964 | | image->width, |
965 | | image->height, |
966 | | filter); |
967 | | } else if (lcf.yuvaToRgbMatrixFilterHighBitDepth != NULL) { |
968 | | libyuvResult = lcf.yuvaToRgbMatrixFilterHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], |
969 | | image->yuvRowBytes[AVIF_CHAN_Y] / 2, |
970 | | (const uint16_t *)image->yuvPlanes[uPlaneIndex], |
971 | | image->yuvRowBytes[uPlaneIndex] / 2, |
972 | | (const uint16_t *)image->yuvPlanes[vPlaneIndex], |
973 | | image->yuvRowBytes[vPlaneIndex] / 2, |
974 | | (const uint16_t *)image->alphaPlane, |
975 | | image->alphaRowBytes / 2, |
976 | | rgb->pixels, |
977 | | rgb->rowBytes, |
978 | | matrix, |
979 | | image->width, |
980 | | image->height, |
981 | | /*attenuate=*/0, |
982 | | filter); |
983 | | *alphaReformattedWithLibYUV = AVIF_TRUE; |
984 | | } else if (lcf.yuvToRgbMatrixHighBitDepth != NULL) { |
985 | | libyuvResult = lcf.yuvToRgbMatrixHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], |
986 | | image->yuvRowBytes[AVIF_CHAN_Y] / 2, |
987 | | (const uint16_t *)image->yuvPlanes[uPlaneIndex], |
988 | | image->yuvRowBytes[uPlaneIndex] / 2, |
989 | | (const uint16_t *)image->yuvPlanes[vPlaneIndex], |
990 | | image->yuvRowBytes[vPlaneIndex] / 2, |
991 | | rgb->pixels, |
992 | | rgb->rowBytes, |
993 | | matrix, |
994 | | image->width, |
995 | | image->height); |
996 | | } else if (lcf.yuvaToRgbMatrixHighBitDepth != NULL) { |
997 | | libyuvResult = lcf.yuvaToRgbMatrixHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], |
998 | | image->yuvRowBytes[AVIF_CHAN_Y] / 2, |
999 | | (const uint16_t *)image->yuvPlanes[uPlaneIndex], |
1000 | | image->yuvRowBytes[uPlaneIndex] / 2, |
1001 | | (const uint16_t *)image->yuvPlanes[vPlaneIndex], |
1002 | | image->yuvRowBytes[vPlaneIndex] / 2, |
1003 | | (const uint16_t *)image->alphaPlane, |
1004 | | image->alphaRowBytes / 2, |
1005 | | rgb->pixels, |
1006 | | rgb->rowBytes, |
1007 | | matrix, |
1008 | | image->width, |
1009 | | image->height, |
1010 | | /*attentuate=*/0); |
1011 | | *alphaReformattedWithLibYUV = AVIF_TRUE; |
1012 | | } else { |
1013 | | avifImage image8; |
1014 | | avifBool inputIsHighBitDepth = image->depth > 8; |
1015 | | if (inputIsHighBitDepth) { |
1016 | | const avifBool downshiftAlpha = (lcf.yuvaToRgbMatrixFilter != NULL || lcf.yuvaToRgbMatrix != NULL); |
1017 | | AVIF_CHECKRES(avifImageDownshiftTo8bpc(image, &image8, downshiftAlpha)); |
1018 | | image = &image8; |
1019 | | } |
1020 | | if (lcf.yuv400ToRgbMatrix != NULL) { |
1021 | | libyuvResult = lcf.yuv400ToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], |
1022 | | image->yuvRowBytes[AVIF_CHAN_Y], |
1023 | | rgb->pixels, |
1024 | | rgb->rowBytes, |
1025 | | matrix, |
1026 | | image->width, |
1027 | | image->height); |
1028 | | } else if (lcf.yuvToRgbMatrixFilter != NULL) { |
1029 | | libyuvResult = lcf.yuvToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y], |
1030 | | image->yuvRowBytes[AVIF_CHAN_Y], |
1031 | | image->yuvPlanes[uPlaneIndex], |
1032 | | image->yuvRowBytes[uPlaneIndex], |
1033 | | image->yuvPlanes[vPlaneIndex], |
1034 | | image->yuvRowBytes[vPlaneIndex], |
1035 | | rgb->pixels, |
1036 | | rgb->rowBytes, |
1037 | | matrix, |
1038 | | image->width, |
1039 | | image->height, |
1040 | | filter); |
1041 | | } else if (lcf.yuvaToRgbMatrixFilter != NULL) { |
1042 | | libyuvResult = lcf.yuvaToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y], |
1043 | | image->yuvRowBytes[AVIF_CHAN_Y], |
1044 | | image->yuvPlanes[uPlaneIndex], |
1045 | | image->yuvRowBytes[uPlaneIndex], |
1046 | | image->yuvPlanes[vPlaneIndex], |
1047 | | image->yuvRowBytes[vPlaneIndex], |
1048 | | image->alphaPlane, |
1049 | | image->alphaRowBytes, |
1050 | | rgb->pixels, |
1051 | | rgb->rowBytes, |
1052 | | matrix, |
1053 | | image->width, |
1054 | | image->height, |
1055 | | /*attenuate=*/0, |
1056 | | filter); |
1057 | | *alphaReformattedWithLibYUV = AVIF_TRUE; |
1058 | | } else if (lcf.yuvToRgbMatrix != NULL) { |
1059 | | libyuvResult = lcf.yuvToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], |
1060 | | image->yuvRowBytes[AVIF_CHAN_Y], |
1061 | | image->yuvPlanes[uPlaneIndex], |
1062 | | image->yuvRowBytes[uPlaneIndex], |
1063 | | image->yuvPlanes[vPlaneIndex], |
1064 | | image->yuvRowBytes[vPlaneIndex], |
1065 | | rgb->pixels, |
1066 | | rgb->rowBytes, |
1067 | | matrix, |
1068 | | image->width, |
1069 | | image->height); |
1070 | | } else if (lcf.yuvaToRgbMatrix != NULL) { |
1071 | | libyuvResult = lcf.yuvaToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], |
1072 | | image->yuvRowBytes[AVIF_CHAN_Y], |
1073 | | image->yuvPlanes[uPlaneIndex], |
1074 | | image->yuvRowBytes[uPlaneIndex], |
1075 | | image->yuvPlanes[vPlaneIndex], |
1076 | | image->yuvRowBytes[vPlaneIndex], |
1077 | | image->alphaPlane, |
1078 | | image->alphaRowBytes, |
1079 | | rgb->pixels, |
1080 | | rgb->rowBytes, |
1081 | | matrix, |
1082 | | image->width, |
1083 | | image->height, |
1084 | | /*attenuate=*/0); |
1085 | | *alphaReformattedWithLibYUV = AVIF_TRUE; |
1086 | | } |
1087 | | if (inputIsHighBitDepth) { |
1088 | | avifImageFreePlanes(&image8, AVIF_PLANES_ALL); |
1089 | | image = NULL; |
1090 | | } |
1091 | | } |
1092 | | return (libyuvResult != 0) ? AVIF_RESULT_REFORMAT_FAILED : AVIF_RESULT_OK; |
1093 | | } |
1094 | | |
1095 | | //-------------------------------------------------------------------------------------------------- |
1096 | | |
1097 | | avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb) |
1098 | | { |
1099 | | // See if the current settings can be accomplished with libyuv, and use it (if possible). |
1100 | | |
1101 | | // The width, height, and stride parameters of libyuv functions are all of the int type. |
1102 | | if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { |
1103 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1104 | | } |
1105 | | if (rgb->depth != 8) { |
1106 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1107 | | } |
1108 | | |
1109 | | // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, |
1110 | | // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. |
1111 | | |
1112 | | // Order of RGB doesn't matter here. |
1113 | | if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { |
1114 | | if (ARGBAttenuate(rgb->pixels, rgb->rowBytes, rgb->pixels, rgb->rowBytes, rgb->width, rgb->height) != 0) { |
1115 | | return AVIF_RESULT_REFORMAT_FAILED; |
1116 | | } |
1117 | | return AVIF_RESULT_OK; |
1118 | | } |
1119 | | |
1120 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1121 | | } |
1122 | | |
1123 | | avifResult avifRGBImageUnpremultiplyAlphaLibYUV(avifRGBImage * rgb) |
1124 | | { |
1125 | | // See if the current settings can be accomplished with libyuv, and use it (if possible). |
1126 | | |
1127 | | // The width, height, and stride parameters of libyuv functions are all of the int type. |
1128 | | if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { |
1129 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1130 | | } |
1131 | | if (rgb->depth != 8) { |
1132 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1133 | | } |
1134 | | |
1135 | | // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, |
1136 | | // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. |
1137 | | |
1138 | | if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { |
1139 | | if (ARGBUnattenuate(rgb->pixels, rgb->rowBytes, rgb->pixels, rgb->rowBytes, rgb->width, rgb->height) != 0) { |
1140 | | return AVIF_RESULT_REFORMAT_FAILED; |
1141 | | } |
1142 | | return AVIF_RESULT_OK; |
1143 | | } |
1144 | | |
1145 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1146 | | } |
1147 | | |
1148 | | avifResult avifRGBImageToF16LibYUV(avifRGBImage * rgb) |
1149 | | { |
1150 | | // The width, height, and stride parameters of libyuv functions are all of the int type. |
1151 | | if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { |
1152 | | return AVIF_RESULT_NOT_IMPLEMENTED; |
1153 | | } |
1154 | | const float scale = 1.0f / ((1 << rgb->depth) - 1); |
1155 | | // Note: HalfFloatPlane requires the stride to be in bytes. |
1156 | | const int result = HalfFloatPlane((const uint16_t *)rgb->pixels, |
1157 | | rgb->rowBytes, |
1158 | | (uint16_t *)rgb->pixels, |
1159 | | rgb->rowBytes, |
1160 | | scale, |
1161 | | rgb->width * avifRGBFormatChannelCount(rgb->format), |
1162 | | rgb->height); |
1163 | | return (result == 0) ? AVIF_RESULT_OK : AVIF_RESULT_INVALID_ARGUMENT; |
1164 | | } |
1165 | | |
1166 | | unsigned int avifLibYUVVersion(void) |
1167 | | { |
1168 | | return (unsigned int)LIBYUV_VERSION; |
1169 | | } |
1170 | | |
1171 | | #endif |