Coverage Report

Created: 2026-04-01 07:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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