Refactor avifImageRGBToYUVLibYUV8bpc() and avif.h
Remove libyuv tables in avif.h.
Use look-up tables in avifImageRGBToYUVLibYUV8bpc().
Fix libyuv RGB-to-YUV 4:0:0 being used for any avifMatrixCoefficients
and not only for BT.601.
diff --git a/include/avif/avif.h b/include/avif/avif.h
index 71cf54f..08555cb 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -528,30 +528,8 @@
// conversion, if necessary. Pixels in an avifRGBImage buffer are always full range, and conversion
// routines will fail if the width and height don't match the associated avifImage.
-// If libavif is built with libyuv fast paths enabled and the AVIF_RGB_TO_YUV_AVOID_LIBYUV or
-// AVIF_YUV_TO_RGB_AVOID_LIBYUV flag is not set, libavif will use libyuv for conversion between
-// RGB and YUV if the following requirements are met:
-
-// Conversion from YUV to RGB:
-// * YUV depth: 8 or 10
-// * RGB depth: 8
-// * rgb.format: AVIF_RGB_FORMAT_RGBA, AVIF_RGB_FORMAT_BGRA (420/422 support for
-// AVIF_RGB_FORMAT_ABGR, AVIF_RGB_FORMAT_ARGB, AVIF_RGB_FORMAT_RGB_565)
-// * CICP is one of the following combinations (CP/TC/MC/Range):
-// * x/x/[2|5|6]/Full
-// * [5|6]/x/12/Full
-// * x/x/[1|2|5|6|9]/Limited
-// * [1|2|5|6|9]/x/12/Limited
-
-// Conversion from RGB to YUV:
-// * YUV depth: 8
-// * RGB depth: 8
-// * One of the following combinations (avifRGBFormat to avifPixelFormat/MC/Range):
-// * BGRA to YUV400 / x /[Full|Limited]
-// * BGRA to [YUV420|YUV422]/[5|6]/[Full|Limited]
-// * BGRA to YUV444 /[5|6]/ Limited
-// * BGR to YUV420 /[5|6]/[Full|Limited]
-// * [RGBA|ARGB|ABGR] to YUV420 /[5|6]/ Limited
+// If libavif is built with a version of libyuv offering a fast conversion between RGB and YUV for
+// the given inputs, libavif will use it. See reformat_libyuv.c for the details.
// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV
// rely on the ordering of this enum values for their correctness. So changing
diff --git a/src/reformat.c b/src/reformat.c
index 9559fad..099778a 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -32,6 +32,9 @@
rgb->format < AVIF_RGB_FORMAT_RGB || rgb->format >= AVIF_RGB_FORMAT_COUNT) {
return AVIF_FALSE;
}
+ if (image->yuvRange != AVIF_RANGE_LIMITED && image->yuvRange != AVIF_RANGE_FULL) {
+ return AVIF_FALSE;
+ }
// These matrix coefficients values are currently unsupported. Revise this list as more support is added.
//
diff --git a/src/reformat_libyuv.c b/src/reformat_libyuv.c
index 267fb02..1bef108 100644
--- a/src/reformat_libyuv.c
+++ b/src/reformat_libyuv.c
@@ -127,101 +127,67 @@
// libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address,
// similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR.
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
- // Generic mapping from any RGB layout (with or without alpha) to monochrome.
- int (*RGBtoY)(const uint8_t *, int, uint8_t *, int, int, int) = NULL;
-
- if (image->yuvRange == AVIF_RANGE_LIMITED) {
- if (rgb->format == AVIF_RGB_FORMAT_BGRA) {
- RGBtoY = ARGBToI400;
- }
- } else { // image->yuvRange == AVIF_RANGE_FULL
- if (rgb->format == AVIF_RGB_FORMAT_BGRA) {
- RGBtoY = ARGBToJ400;
- }
- }
-
- if (!RGBtoY) {
- return AVIF_RESULT_NOT_IMPLEMENTED;
- }
- if (RGBtoY(rgb->pixels, rgb->rowBytes, image->yuvPlanes[AVIF_CHAN_Y], image->yuvRowBytes[AVIF_CHAN_Y], image->width, image->height)) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- }
-
- // Generic mapping from any RGB layout (with or without alpha) to any YUV layout (subsampled or not).
- int (*RGBtoYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int) = NULL;
-
// libyuv only handles BT.601 for RGB to YUV, and not all range/order/subsampling combinations.
// BT.470BG has the same coefficients as BT.601.
if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT470BG) || (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT601)) {
- if (rgb->format == AVIF_RGB_FORMAT_BGRA) {
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (image->yuvRange == AVIF_RANGE_FULL) {
- RGBtoYUV = ARGBToJ420;
- } else {
- RGBtoYUV = ARGBToI420;
+ if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
+ // Lookup table for RGB To Y (monochrome).
+ typedef int (*RGBtoY)(const uint8_t *, int, uint8_t *, int, int, int);
+ // Second dimension is for avifRange.
+ RGBtoY lutRgbToY[AVIF_RGB_FORMAT_COUNT][2] = {
+ { NULL, NULL }, // RGB
+ { NULL, NULL }, // RGBA
+ { NULL, NULL }, // ARGB
+ { NULL, NULL }, // BGR
+ { ARGBToI400, ARGBToJ400 }, // BGRA
+ { NULL, NULL }, // ABGR
+ { NULL, NULL } // RGB_565
+ };
+ RGBtoY rgbToY = lutRgbToY[rgb->format][image->yuvRange];
+ if (rgbToY != NULL) {
+ if (rgbToY(rgb->pixels,
+ rgb->rowBytes,
+ image->yuvPlanes[AVIF_CHAN_Y],
+ image->yuvRowBytes[AVIF_CHAN_Y],
+ image->width,
+ image->height) != 0) {
+ return AVIF_RESULT_REFORMAT_FAILED;
}
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
- if (image->yuvRange == AVIF_RANGE_FULL) {
- RGBtoYUV = ARGBToJ422;
- } else {
- RGBtoYUV = ARGBToI422;
- }
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
- if (image->yuvRange == AVIF_RANGE_LIMITED) {
- RGBtoYUV = ARGBToI444;
- }
+ return AVIF_RESULT_OK;
}
- } else if (rgb->format == AVIF_RGB_FORMAT_BGR) {
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (image->yuvRange == AVIF_RANGE_FULL) {
- RGBtoYUV = RGB24ToJ420;
- } else {
- RGBtoYUV = RGB24ToI420;
+ } else {
+ // Lookup table for RGB To YUV Matrix (average filter).
+ typedef int (*RGBtoYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int);
+ // Third dimension is for avifRange.
+ RGBtoYUV lutRgbToYuv[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT][2] = {
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } }, // RGB
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { ABGRToI420, avifABGRToJ420 }, { NULL, NULL } }, // RGBA
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { BGRAToI420, NULL }, { NULL, NULL } }, // ARGB
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { RGB24ToI420, RGB24ToJ420 }, { NULL, NULL } }, // BGR
+ { { NULL, NULL }, { ARGBToI444, NULL }, { ARGBToI422, ARGBToJ422 }, { ARGBToI420, ARGBToJ420 }, { NULL, NULL } }, // BGRA
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { RGBAToI420, NULL }, { NULL, NULL } }, // ABGR
+ { { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL }, { NULL, NULL } } // RGB_565
+ };
+ RGBtoYUV rgbToYuv = lutRgbToYuv[rgb->format][image->yuvFormat][image->yuvRange];
+ if (rgbToYuv != NULL) {
+ if (rgbToYuv(rgb->pixels,
+ rgb->rowBytes,
+ image->yuvPlanes[AVIF_CHAN_Y],
+ image->yuvRowBytes[AVIF_CHAN_Y],
+ image->yuvPlanes[AVIF_CHAN_U],
+ image->yuvRowBytes[AVIF_CHAN_U],
+ image->yuvPlanes[AVIF_CHAN_V],
+ image->yuvRowBytes[AVIF_CHAN_V],
+ image->width,
+ image->height) != 0) {
+ return AVIF_RESULT_REFORMAT_FAILED;
}
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_RGBA) {
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (image->yuvRange == AVIF_RANGE_FULL) {
- RGBtoYUV = avifABGRToJ420;
- } else {
- RGBtoYUV = ABGRToI420;
- }
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_ARGB) {
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (image->yuvRange == AVIF_RANGE_LIMITED) {
- RGBtoYUV = BGRAToI420;
- }
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_ABGR) {
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (image->yuvRange == AVIF_RANGE_LIMITED) {
- RGBtoYUV = RGBAToI420;
- }
+ return AVIF_RESULT_OK;
}
}
}
- // TODO: Use SplitRGBPlane() for AVIF_MATRIX_COEFFICIENTS_IDENTITY if faster than the current implementation
-
- if (!RGBtoYUV) {
- return AVIF_RESULT_NOT_IMPLEMENTED;
- }
- if (RGBtoYUV(rgb->pixels,
- rgb->rowBytes,
- image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->width,
- image->height)) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
+ // TODO: Use SplitRGBPlane() for AVIF_MATRIX_COEFFICIENTS_IDENTITY if faster than the built-in implementation
+ return AVIF_RESULT_NOT_IMPLEMENTED;
}
static avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
@@ -309,7 +275,7 @@
case AVIF_MATRIX_COEFFICIENTS_ICTCP:
break;
}
- } else {
+ } else { // image->yuvRange == AVIF_RANGE_LIMITED
switch (image->matrixCoefficients) {
case AVIF_MATRIX_COEFFICIENTS_BT709:
matrixYUV = &kYuvH709Constants;