Refactor libyuv yuv->rgb conversion with function pointers
Use function pointers and look up tables based on RGB and
YUV image formats to call the appropriate libyuv function.
This eliminates a lot of repeated code (which can be error prone
when copy-pasting, refactoring, etc). It also makes adding new
libyuv functions simpler.
Tested with several AVIF images and the existing yuv and rgb tests
continue to pass.
diff --git a/apps/shared/y4m.c b/apps/shared/y4m.c
index e924600..e6519f6 100644
--- a/apps/shared/y4m.c
+++ b/apps/shared/y4m.c
@@ -457,7 +457,8 @@
y4mHeaderFormat = "Cmono XYSCSS=400";
break;
case AVIF_PIXEL_FORMAT_NONE:
- // will error later; this case is here for warning's sake
+ case AVIF_PIXEL_FORMAT_COUNT:
+ // will error later; these cases are here for warning's sake
break;
}
break;
@@ -476,7 +477,8 @@
y4mHeaderFormat = "Cmono10 XYSCSS=400";
break;
case AVIF_PIXEL_FORMAT_NONE:
- // will error later; this case is here for warning's sake
+ case AVIF_PIXEL_FORMAT_COUNT:
+ // will error later; these cases are here for warning's sake
break;
}
break;
@@ -495,7 +497,8 @@
y4mHeaderFormat = "Cmono12 XYSCSS=400";
break;
case AVIF_PIXEL_FORMAT_NONE:
- // will error later; this case is here for warning's sake
+ case AVIF_PIXEL_FORMAT_COUNT:
+ // will error later; these cases are here for warning's sake
break;
}
break;
diff --git a/include/avif/avif.h b/include/avif/avif.h
index 089a7e2..089c6fa 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -180,7 +180,11 @@
// ---------------------------------------------------------------------------
// avifPixelFormat
-
+//
+// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV
+// rely on the ordering of this enum values for their correctness. So changing
+// the values in this enum will require auditing avifImageYUVToRGBLibYUV for
+// correctness.
typedef enum avifPixelFormat
{
// No YUV pixels are present. Alpha plane can still be present.
@@ -189,7 +193,8 @@
AVIF_PIXEL_FORMAT_YUV444,
AVIF_PIXEL_FORMAT_YUV422,
AVIF_PIXEL_FORMAT_YUV420,
- AVIF_PIXEL_FORMAT_YUV400
+ AVIF_PIXEL_FORMAT_YUV400,
+ AVIF_PIXEL_FORMAT_COUNT
} avifPixelFormat;
AVIF_API const char * avifPixelFormatToString(avifPixelFormat format);
@@ -544,6 +549,10 @@
// * BGR to YUV420 /[5|6]/[Full|Limited]
// * [RGBA|ARGB|ABGR] to YUV420 /[5|6]/ Limited
+// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV
+// rely on the ordering of this enum values for their correctness. So changing
+// the values in this enum will require auditing avifImageYUVToRGBLibYUV for
+// correctness.
typedef enum avifRGBFormat
{
AVIF_RGB_FORMAT_RGB = 0,
@@ -561,7 +570,8 @@
// b4 and b0 are the MSB and LSB of the blue component respectively.
// This format is only supported for YUV -> RGB conversion and when
// avifRGBImage.depth is set to 8.
- AVIF_RGB_FORMAT_RGB_565
+ AVIF_RGB_FORMAT_RGB_565,
+ AVIF_RGB_FORMAT_COUNT
} avifRGBFormat;
AVIF_API uint32_t avifRGBFormatChannelCount(avifRGBFormat format);
AVIF_API avifBool avifRGBFormatHasAlpha(avifRGBFormat format);
diff --git a/src/avif.c b/src/avif.c
index 99b1acb..6ced063 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -28,6 +28,7 @@
case AVIF_PIXEL_FORMAT_YUV400:
return "YUV400";
case AVIF_PIXEL_FORMAT_NONE:
+ case AVIF_PIXEL_FORMAT_COUNT:
default:
break;
}
@@ -61,6 +62,7 @@
break;
case AVIF_PIXEL_FORMAT_NONE:
+ case AVIF_PIXEL_FORMAT_COUNT:
default:
break;
}
diff --git a/src/codec_aom.c b/src/codec_aom.c
index 44deab9..a3fba99 100644
--- a/src/codec_aom.c
+++ b/src/codec_aom.c
@@ -272,6 +272,7 @@
fmt = AOM_IMG_FMT_I420;
break;
case AVIF_PIXEL_FORMAT_NONE:
+ case AVIF_PIXEL_FORMAT_COUNT:
default:
return AOM_IMG_FMT_NONE;
}
@@ -649,6 +650,7 @@
seqProfile = 0;
break;
case AVIF_PIXEL_FORMAT_NONE:
+ case AVIF_PIXEL_FORMAT_COUNT:
default:
break;
}
diff --git a/src/reformat.c b/src/reformat.c
index 88faf2a..1489c38 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -28,6 +28,10 @@
if (rgb->format == AVIF_RGB_FORMAT_RGB_565 && rgb->depth != 8) {
return AVIF_FALSE;
}
+ if (image->yuvFormat <= AVIF_PIXEL_FORMAT_NONE || image->yuvFormat >= AVIF_PIXEL_FORMAT_COUNT ||
+ rgb->format < AVIF_RGB_FORMAT_RGB || rgb->format >= AVIF_RGB_FORMAT_COUNT) {
+ return AVIF_FALSE;
+ }
// These matrix coefficients values are currently unsupported. Revise this list as more support is added.
//
@@ -46,10 +50,6 @@
return AVIF_FALSE;
}
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
- return AVIF_FALSE;
- }
-
avifGetPixelFormatInfo(image->yuvFormat, &state->formatInfo);
avifCalcYUVCoefficients(image, &state->kr, &state->kg, &state->kb);
state->mode = AVIF_REFORMAT_MODE_YUV_COEFFICIENTS;
@@ -119,7 +119,7 @@
state->rgbOffsetBytesA = 0;
break;
- default:
+ case AVIF_RGB_FORMAT_COUNT:
return AVIF_FALSE;
}
diff --git a/src/reformat_libyuv.c b/src/reformat_libyuv.c
index 37d8f12..8c0b895 100644
--- a/src/reformat_libyuv.c
+++ b/src/reformat_libyuv.c
@@ -341,6 +341,37 @@
return AVIF_RESULT_NOT_IMPLEMENTED;
}
+// These defines are used to create a NULL reference to libyuv functions that
+// did not exist prior to a particular version of libyuv.
+#if LIBYUV_VERSION < 1838
+#define I422ToRGB565Matrix NULL
+#endif
+#if LIBYUV_VERSION < 1813
+#define I422ToARGBMatrixFilter NULL
+#define I420ToARGBMatrixFilter NULL
+#define I210ToARGBMatrixFilter NULL
+#define I010ToARGBMatrixFilter NULL
+#endif
+#if LIBYUV_VERSION < 1780
+#define I410ToARGBMatrix NULL
+#endif
+#if LIBYUV_VERSION < 1756
+#define I400ToARGBMatrix NULL
+#endif
+
+// Lookup table for isYVU. If the entry in this table is AVIF_TRUE, then it
+// means that we are using a libyuv function with R and B channels swapped,
+// which requires U and V planes also be swapped.
+static const avifBool lutIsYVU[AVIF_RGB_FORMAT_COUNT] = {
+ AVIF_TRUE, // RGB
+ AVIF_TRUE, // RGBA
+ AVIF_TRUE, // ARGB
+ AVIF_FALSE, // BGR
+ AVIF_FALSE, // BGRA
+ AVIF_FALSE, // ABGR
+ AVIF_FALSE, // RGB_565
+};
+
avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
avifRGBImage * rgb,
const struct YuvConstants * matrixYUV,
@@ -350,428 +381,122 @@
assert((image->depth == 8) && (rgb->depth == 8));
-#if LIBYUV_VERSION >= 1813
- enum FilterMode filter = kFilterBilinear;
- if (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) {
- // 'None' (Nearest neighbor) filter is faster than bilinear.
- filter = kFilterNone;
- }
-#endif
-
- // This following section might be a bit complicated to audit without a bit of explanation:
- //
// 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.
// In addition, swapping U and V in any of these calls, along with using the Yvu matrix instead of Yuv matrix,
- // swaps B and R in these orderings as well. This table summarizes this block's intent:
+ // swaps B and R in these orderings as well. This table summarizes the lookup tables that follow:
//
// libavif format libyuv Func UV matrix (and UV argument ordering)
// -------------------- ------------- ------------------------------------
// AVIF_RGB_FORMAT_RGB *ToRGB24Matrix matrixYVU
- // AVIF_RGB_FORMAT_BGR *ToRGB24Matrix matrixYUV
- // AVIF_RGB_FORMAT_RGB_565 *ToRGB565Matrix matrixYUV
- // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV
// AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU
- // AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV
// AVIF_RGB_FORMAT_ARGB *ToRGBAMatrix matrixYVU
+ // AVIF_RGB_FORMAT_BGR *ToRGB24Matrix matrixYUV
+ // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV
+ // AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV
+ // AVIF_RGB_FORMAT_RGB_565 *ToRGB565Matrix matrixYUV
- if (rgb->format == AVIF_RGB_FORMAT_RGB) {
- // AVIF_RGB_FORMAT_RGB *ToRGB24Matrix matrixYVU
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (I420ToRGB24Matrix(image->yuvPlanes[AVIF_CHAN_Y],
+ avifBool isYVU = lutIsYVU[rgb->format];
+ const struct YuvConstants * matrix = isYVU ? matrixYVU : matrixYUV;
+ if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
+ // Lookup table for YUV400 to RGB Matrix.
+ typedef int (*YUV400ToRGBMatrix)(const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int);
+ YUV400ToRGBMatrix lutYuv400ToRgbMatrix[AVIF_RGB_FORMAT_COUNT] = {
+ NULL, // RGB
+ I400ToARGBMatrix, // RGBA
+ NULL, // ARGB
+ NULL, // BGR
+ I400ToARGBMatrix, // BGRA
+ NULL, // ABGR
+ NULL, // RGB_565
+ };
+ YUV400ToRGBMatrix yuv400ToRgbMatrix = lutYuv400ToRgbMatrix[rgb->format];
+ if (yuv400ToRgbMatrix != NULL) {
+ if (yuv400ToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y],
image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
rgb->pixels,
rgb->rowBytes,
- matrixYVU,
+ matrix,
image->width,
image->height) != 0) {
return AVIF_RESULT_REFORMAT_FAILED;
}
return AVIF_RESULT_OK;
}
- } else if (rgb->format == AVIF_RGB_FORMAT_BGR) {
- // AVIF_RGB_FORMAT_BGR *ToRGB24Matrix matrixYUV
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (I420ToRGB24Matrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
+ } else {
+ int uPlaneIndex = isYVU ? AVIF_CHAN_V : AVIF_CHAN_U;
+ int vPlaneIndex = isYVU ? AVIF_CHAN_U : AVIF_CHAN_V;
+ // Lookup table for YUV To RGB Matrix (with filter).
+ typedef int (*YUVToRGBMatrixFilter)(const uint8_t *,
+ int,
+ const uint8_t *,
+ int,
+ const uint8_t *,
+ int,
+ uint8_t *,
+ int,
+ const struct YuvConstants *,
+ int,
+ int,
+ enum FilterMode);
+ YUVToRGBMatrixFilter lutYuvToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = {
+ { NULL, NULL, NULL, NULL, NULL }, // RGB
+ { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // RGBA
+ { NULL, NULL, NULL, NULL, NULL }, // ARGB
+ { NULL, NULL, NULL, NULL, NULL }, // BGR
+ { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // BGRA
+ { NULL, NULL, NULL, NULL, NULL }, // ABGR
+ { NULL, NULL, NULL, NULL, NULL }, // RGB_565
+ };
+ YUVToRGBMatrixFilter yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[rgb->format][image->yuvFormat];
+ if (yuvToRgbMatrixFilter != NULL) {
+ // 'None' (Nearest neighbor) filter is faster than bilinear.
+ enum FilterMode filter = (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) ? kFilterNone : kFilterBilinear;
+ if (yuvToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
+ image->yuvRowBytes[AVIF_CHAN_Y],
+ image->yuvPlanes[uPlaneIndex],
+ image->yuvRowBytes[uPlaneIndex],
+ image->yuvPlanes[vPlaneIndex],
+ image->yuvRowBytes[vPlaneIndex],
+ rgb->pixels,
+ rgb->rowBytes,
+ matrix,
+ image->width,
+ image->height,
+ filter) != 0) {
return AVIF_RESULT_REFORMAT_FAILED;
}
return AVIF_RESULT_OK;
}
- } else if (rgb->format == AVIF_RGB_FORMAT_RGB_565) {
- // AVIF_RGB_FORMAT_BGR *ToRGB565Matrix matrixYUV
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
-#if LIBYUV_VERSION >= 1838
- if (I422ToRGB565Matrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
+ // Lookup table for YUV To RGB Matrix (without filter).
+ typedef int (
+ *YUVToRGBMatrix)(const uint8_t *, int, const uint8_t *, int, const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int);
+ YUVToRGBMatrix lutYuvToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = {
+ { NULL, NULL, NULL, I420ToRGB24Matrix, NULL }, // RGB
+ { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // RGBA
+ { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ARGB
+ { NULL, NULL, NULL, I420ToRGB24Matrix, NULL }, // BGR
+ { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // BGRA
+ { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ABGR
+ { NULL, NULL, I422ToRGB565Matrix, I420ToRGB565Matrix, NULL }, // RGB_565
+ };
+ YUVToRGBMatrix yuvToRgbMatrix = lutYuvToRgbMatrix[rgb->format][image->yuvFormat];
+ if (yuvToRgbMatrix != NULL) {
+ if (yuvToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y],
+ image->yuvRowBytes[AVIF_CHAN_Y],
+ image->yuvPlanes[uPlaneIndex],
+ image->yuvRowBytes[uPlaneIndex],
+ image->yuvPlanes[vPlaneIndex],
+ image->yuvRowBytes[vPlaneIndex],
+ rgb->pixels,
+ rgb->rowBytes,
+ matrix,
+ image->width,
+ image->height) != 0) {
return AVIF_RESULT_REFORMAT_FAILED;
}
return AVIF_RESULT_OK;
-#endif
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (I420ToRGB565Matrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_BGRA) {
- // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
- if (I444ToARGBMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
-#if LIBYUV_VERSION >= 1813
- if (I422ToARGBMatrixFilter(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I422ToARGBMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
-#if LIBYUV_VERSION >= 1813
- if (I420ToARGBMatrixFilter(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I420ToARGBMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
-#if LIBYUV_VERSION >= 1756
- if (I400ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_RGBA) {
- // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
- if (I444ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
-#if LIBYUV_VERSION >= 1813
- if (I422ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I422ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
-#if LIBYUV_VERSION >= 1813
- if (I420ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I420ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
-#if LIBYUV_VERSION >= 1756
- if (I400ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_ABGR) {
- // AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
- // This doesn't currently exist in libyuv
-#if 0
- if (I444ToRGBAMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
- if (I422ToRGBAMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (I420ToRGBAMatrix(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],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
- // This doesn't currently exist in libyuv
-#if 0
- if (I400ToRGBAMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- }
- } else if (rgb->format == AVIF_RGB_FORMAT_ARGB) {
- // AVIF_RGB_FORMAT_ARGB *ToRGBAMatrix matrixYVU
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
- // This doesn't currently exist in libyuv
-#if 0
- if (I444ToRGBAMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
- if (I422ToRGBAMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
- if (I420ToRGBAMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V],
- image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
- // This doesn't currently exist in libyuv
-#if 0
- if (I400ToRGBAMatrix(image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y],
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
}
}
@@ -788,207 +513,99 @@
assert((image->depth == 10) && (rgb->depth == 8));
-#if LIBYUV_VERSION >= 1813
- enum FilterMode filter = kFilterBilinear;
- if (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) {
- // 'None' (Nearest neighbor) filter is faster than bilinear.
- filter = kFilterNone;
- }
-#endif
-
- // This following section might be a bit complicated to audit without a bit of explanation:
- //
// 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.
// In addition, swapping U and V in any of these calls, along with using the Yvu matrix instead of Yuv matrix,
- // swaps B and R in these orderings as well. This table summarizes this block's intent:
+ // swaps B and R in these orderings as well. This table summarizes the lookup tables that follow:
//
// libavif format libyuv Func UV matrix (and UV argument ordering)
// -------------------- ------------- ------------------------------------
// AVIF_RGB_FORMAT_RGB n/a n/a
+ // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU
+ // AVIF_RGB_FORMAT_ARGB n/a n/a
// AVIF_RGB_FORMAT_BGR n/a n/a
// AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV
- // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU
- // AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV
- // AVIF_RGB_FORMAT_ARGB *ToRGBAMatrix matrixYVU
+ // AVIF_RGB_FORMAT_ABGR n/a n/a
+ // AVIF_RGB_FORMAT_565 n/a n/a
- if (rgb->format == AVIF_RGB_FORMAT_BGRA) {
- // AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV
+ avifBool isYVU = lutIsYVU[rgb->format];
+ const struct YuvConstants * matrix = isYVU ? matrixYVU : matrixYUV;
+ int uPlaneIndex = isYVU ? AVIF_CHAN_V : AVIF_CHAN_U;
+ int vPlaneIndex = isYVU ? AVIF_CHAN_U : AVIF_CHAN_V;
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
-#if LIBYUV_VERSION >= 1780
- if (I410ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
+ // Lookup table for YUV To RGB Matrix (with filter).
+ typedef int (*YUVToRGBMatrixFilter)(const uint16_t *,
+ int,
+ const uint16_t *,
+ int,
+ const uint16_t *,
+ int,
+ uint8_t *,
+ int,
+ const struct YuvConstants *,
+ int,
+ int,
+ enum FilterMode);
+ YUVToRGBMatrixFilter lutYuvToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = {
+ { NULL, NULL, NULL, NULL, NULL }, // RGB
+ { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // RGBA
+ { NULL, NULL, NULL, NULL, NULL }, // ARGB
+ { NULL, NULL, NULL, NULL, NULL }, // BGR
+ { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // BGRA
+ { NULL, NULL, NULL, NULL, NULL }, // ABGR
+ { NULL, NULL, NULL, NULL, NULL }, // RGB_565
+ };
+ YUVToRGBMatrixFilter yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[rgb->format][image->yuvFormat];
+ if (yuvToRgbMatrixFilter != NULL) {
+ // 'None' (Nearest neighbor) filter is faster than bilinear.
+ enum FilterMode filter = (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) ? kFilterNone : kFilterBilinear;
+ if (yuvToRgbMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
+ (const uint16_t *)image->yuvPlanes[uPlaneIndex],
+ image->yuvRowBytes[uPlaneIndex] / 2,
+ (const uint16_t *)image->yuvPlanes[vPlaneIndex],
+ image->yuvRowBytes[vPlaneIndex] / 2,
rgb->pixels,
rgb->rowBytes,
- matrixYUV,
+ matrix,
image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
-#if LIBYUV_VERSION >= 1813
- if (I210ToARGBMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I210ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
-#if LIBYUV_VERSION >= 1813
- if (I010ToARGBMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I010ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYUV,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
- // This doesn't currently exist in libyuv
+ image->height,
+ filter) != 0) {
+ return AVIF_RESULT_REFORMAT_FAILED;
}
- } else if (rgb->format == AVIF_RGB_FORMAT_RGBA) {
- // AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU
-
- if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) {
-#if LIBYUV_VERSION >= 1780
- if (I410ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
- return AVIF_RESULT_OK;
-#endif
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
-#if LIBYUV_VERSION >= 1813
- if (I210ToARGBMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I210ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
-#if LIBYUV_VERSION >= 1813
- if (I010ToARGBMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height,
- filter) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#else
- if (I010ToARGBMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
- image->yuvRowBytes[AVIF_CHAN_Y] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_V],
- image->yuvRowBytes[AVIF_CHAN_V] / 2,
- (const uint16_t *)image->yuvPlanes[AVIF_CHAN_U],
- image->yuvRowBytes[AVIF_CHAN_U] / 2,
- rgb->pixels,
- rgb->rowBytes,
- matrixYVU,
- image->width,
- image->height) != 0) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-#endif
- return AVIF_RESULT_OK;
- } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
- // This doesn't currently exist in libyuv
- }
+ return AVIF_RESULT_OK;
}
- // This function didn't do anything; use the built-in YUV conversion
+ // Lookup table for YUV To RGB Matrix (without filter).
+ typedef int (
+ *YUVToRGBMatrix)(const uint16_t *, int, const uint16_t *, int, const uint16_t *, int, uint8_t *, int, const struct YuvConstants *, int, int);
+ YUVToRGBMatrix lutYuvToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = {
+ { NULL, NULL, NULL, NULL, NULL }, // RGB
+ { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // RGBA
+ { NULL, NULL, NULL, NULL, NULL }, // ARGB
+ { NULL, NULL, NULL, NULL, NULL }, // BGR
+ { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // BGRA
+ { NULL, NULL, NULL, NULL, NULL }, // ABGR
+ { NULL, NULL, NULL, NULL, NULL }, // RGB_565
+ };
+ YUVToRGBMatrix yuvToRgbMatrix = lutYuvToRgbMatrix[rgb->format][image->yuvFormat];
+ if (yuvToRgbMatrix != NULL) {
+ if (yuvToRgbMatrix((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
+ image->yuvRowBytes[AVIF_CHAN_Y] / 2,
+ (const uint16_t *)image->yuvPlanes[uPlaneIndex],
+ image->yuvRowBytes[uPlaneIndex] / 2,
+ (const uint16_t *)image->yuvPlanes[vPlaneIndex],
+ image->yuvRowBytes[vPlaneIndex] / 2,
+ rgb->pixels,
+ rgb->rowBytes,
+ matrix,
+ image->width,
+ image->height) != 0) {
+ return AVIF_RESULT_REFORMAT_FAILED;
+ }
+ return AVIF_RESULT_OK;
+ }
+
return AVIF_RESULT_NOT_IMPLEMENTED;
}
diff --git a/tests/avifyuv.c b/tests/avifyuv.c
index 0c1a53a..b7e2df1 100644
--- a/tests/avifyuv.c
+++ b/tests/avifyuv.c
@@ -41,6 +41,8 @@
return "ABGR";
case AVIF_RGB_FORMAT_RGB_565:
return "RGB_565";
+ case AVIF_RGB_FORMAT_COUNT:
+ break;
}
return "Unknown";
}