Revert avifImageRGBToYUV() API changes
To avoid backward compatibility issues.
Add avoidLibYUV member to avifRGBImage.
chromaUpsampling has no impact on libyuv use.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 14fc6b9..ff86177 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,11 +7,11 @@
## [Unreleased]
There are incompatible ABI changes in this release. The alphaRange member was
-removed from avifImage struct. The chromaDownsampling member was added to the
-avifRGBImage struct. The imageDimensionLimit member was added to the avifDecoder
-struct. avifImageCopy() and avifImageAllocatePlanes() signatures changed. It is
-necessary to recompile your code. Also check the return values of
-avifImageCopy() and avifImageAllocatePlanes().
+removed from avifImage struct. The chromaDownsampling and avoidLibYUV members
+were added to the avifRGBImage struct. The imageDimensionLimit member was added
+to the avifDecoder struct. avifImageCopy() and avifImageAllocatePlanes()
+signatures changed. It is necessary to recompile your code. Also check the
+return values of avifImageCopy() and avifImageAllocatePlanes().
### Changed
* Update aom.cmd: v3.4.0
@@ -23,7 +23,10 @@
void to report invalid parameters or memory allocation failures.
* avifImageRGBToYUV() now uses libyuv fast paths by default. It may slightly
change conversion results. The old behavior can be restored by setting
- avifRGBImage::chromaDownsampling to AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY.
+ avifRGBImage::chromaDownsampling to AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY
+ and avifRGBImage::avoidLibYUV to AVIF_TRUE.
+* avifRGBImage::chromaUpsampling now only applies to conversions that need
+ upsampling chroma from 4:2:0 or 4:2:2 and has no impact on the use of libyuv.
### Removed
* alphaRange field was removed from the avifImage struct. It it presumed that
@@ -34,6 +37,7 @@
* Add chromaDownsampling field to avifRGBImage struct
* Add imageDimensionLimit field to avifDecoder struct
* Add autoTiling field to avifEncoder struct
+* Add AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV value to avifChromaDownsampling enum
* avifdec: Add --dimension-limit, which specifies the image dimension limit
(width or height) that should be tolerated
* avifenc: Add --sharpyuv, which enables "sharp" RGB to YUV420 conversion, which
diff --git a/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc b/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
index 6d0e060..6284870 100644
--- a/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
+++ b/android_jni/avifandroidjni/src/main/jni/libavif_jni.cc
@@ -166,8 +166,7 @@
}
rgb_image.pixels = static_cast<uint8_t*>(bitmap_pixels);
rgb_image.rowBytes = bitmap_info.stride;
- res = avifImageYUVToRGB(decoder.decoder->image, &rgb_image,
- AVIF_YUV_TO_RGB_DEFAULT);
+ res = avifImageYUVToRGB(decoder.decoder->image, &rgb_image);
AndroidBitmap_unlockPixels(env, bitmap);
if (res != AVIF_RESULT_OK) {
LOGE("Failed to convert YUV Pixels to RGB. Status: %d", res);
diff --git a/apps/avifdec.c b/apps/avifdec.c
index 27ab40d..004f681 100644
--- a/apps/avifdec.c
+++ b/apps/avifdec.c
@@ -62,7 +62,7 @@
int pngCompressionLevel = -1; // -1 is a sentinel to avifPNGWrite() to skip calling png_set_compression_level()
avifCodecChoice codecChoice = AVIF_CODEC_CHOICE_AUTO;
avifBool infoOnly = AVIF_FALSE;
- avifYUVToRGBFlags upsamplingFlags = AVIF_YUV_TO_RGB_DEFAULT;
+ avifChromaUpsampling upsamplingFlags = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
avifBool ignoreICC = AVIF_FALSE;
avifBool rawColor = AVIF_FALSE;
avifBool allowProgressive = AVIF_FALSE;
@@ -153,11 +153,11 @@
} else if (!strcmp(arg, "-u") || !strcmp(arg, "--upsampling")) {
NEXTARG();
if (!strcmp(arg, "automatic")) {
- upsamplingFlags = AVIF_YUV_TO_RGB_DEFAULT;
+ upsamplingFlags = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
} else if (!strcmp(arg, "fastest")) {
- upsamplingFlags = AVIF_CHROMA_UPSAMPLING_NEAREST;
+ upsamplingFlags = AVIF_CHROMA_UPSAMPLING_FASTEST;
} else if (!strcmp(arg, "best")) {
- upsamplingFlags = AVIF_YUV_TO_RGB_AVOID_LIBYUV | AVIF_CHROMA_UPSAMPLING_BILINEAR;
+ upsamplingFlags = AVIF_CHROMA_UPSAMPLING_BEST_QUALITY;
} else if (!strcmp(arg, "nearest")) {
upsamplingFlags = AVIF_CHROMA_UPSAMPLING_NEAREST;
} else if (!strcmp(arg, "bilinear")) {
diff --git a/apps/avifenc.c b/apps/avifenc.c
index 199f841..918d521 100644
--- a/apps/avifenc.c
+++ b/apps/avifenc.c
@@ -266,7 +266,7 @@
avifImage * image,
uint32_t * outDepth,
avifAppSourceTiming * sourceTiming,
- avifRGBToYUVFlags flags)
+ avifChromaDownsampling flags)
{
if (sourceTiming) {
// A source timing of all 0s is a sentinel value hinting that the value is unset / should be
@@ -484,7 +484,7 @@
avifColorPrimaries colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED;
avifTransferCharacteristics transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED;
avifMatrixCoefficients matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601;
- avifRGBToYUVFlags flags = AVIF_RGB_TO_YUV_DEFAULT;
+ avifChromaDownsampling flags = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC;
int argIndex = 1;
while (argIndex < argc) {
diff --git a/apps/shared/avifjpeg.c b/apps/shared/avifjpeg.c
index 4cb54ba..2e2bd60 100644
--- a/apps/shared/avifjpeg.c
+++ b/apps/shared/avifjpeg.c
@@ -243,7 +243,7 @@
avifImage * avif,
avifPixelFormat requestedFormat,
uint32_t requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP)
@@ -318,6 +318,7 @@
avif->depth = requestedDepth ? requestedDepth : 8;
avifRGBImageSetDefaults(&rgb, avif);
rgb.format = AVIF_RGB_FORMAT_RGB;
+ rgb.chromaDownsampling = flags;
rgb.depth = 8;
avifRGBImageAllocatePixels(&rgb);
@@ -328,7 +329,7 @@
memcpy(pixelRow, buffer[0], rgb.rowBytes);
++row;
}
- if (avifImageRGBToYUV(avif, &rgb, flags) != AVIF_RESULT_OK) {
+ if (avifImageRGBToYUV(avif, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion to YUV failed: %s\n", inputFilename);
goto cleanup;
}
@@ -381,7 +382,7 @@
return ret;
}
-avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifYUVToRGBFlags conversionFlags)
+avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifChromaUpsampling flags)
{
avifBool ret = AVIF_FALSE;
FILE * f = NULL;
@@ -395,9 +396,10 @@
avifRGBImage rgb;
avifRGBImageSetDefaults(&rgb, avif);
rgb.format = AVIF_RGB_FORMAT_RGB;
+ rgb.chromaUpsampling = flags;
rgb.depth = 8;
avifRGBImageAllocatePixels(&rgb);
- if (avifImageYUVToRGB(avif, &rgb, conversionFlags) != AVIF_RESULT_OK) {
+ if (avifImageYUVToRGB(avif, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion to RGB failed: %s\n", outputFilename);
goto cleanup;
}
diff --git a/apps/shared/avifjpeg.h b/apps/shared/avifjpeg.h
index 214bcca..6611063 100644
--- a/apps/shared/avifjpeg.h
+++ b/apps/shared/avifjpeg.h
@@ -14,11 +14,11 @@
avifImage * avif,
avifPixelFormat requestedFormat,
uint32_t requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP);
-avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifYUVToRGBFlags conversionFlags);
+avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifChromaUpsampling flags);
#ifdef __cplusplus
} // extern "C"
diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c
index 873cb39..d40ab1a 100644
--- a/apps/shared/avifpng.c
+++ b/apps/shared/avifpng.c
@@ -205,7 +205,7 @@
avifImage * avif,
avifPixelFormat requestedFormat,
uint32_t requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP,
@@ -328,6 +328,7 @@
}
avifRGBImageSetDefaults(&rgb, avif);
+ rgb.chromaDownsampling = flags;
rgb.depth = imgBitDepth;
avifRGBImageAllocatePixels(&rgb);
rowPointers = (png_bytep *)malloc(sizeof(png_bytep) * rgb.height);
@@ -335,7 +336,7 @@
rowPointers[y] = &rgb.pixels[y * rgb.rowBytes];
}
png_read_image(png, rowPointers);
- if (avifImageRGBToYUV(avif, &rgb, flags) != AVIF_RESULT_OK) {
+ if (avifImageRGBToYUV(avif, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion to YUV failed: %s\n", inputFilename);
goto cleanup;
}
@@ -367,7 +368,7 @@
return readResult;
}
-avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifYUVToRGBFlags conversionFlags, int compressionLevel)
+avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling flags, int compressionLevel)
{
volatile avifBool writeResult = AVIF_FALSE;
png_structp png = NULL;
@@ -388,6 +389,7 @@
}
avifRGBImageSetDefaults(&rgb, avif);
+ rgb.chromaUpsampling = flags;
rgb.depth = rgbDepth;
int colorType = PNG_COLOR_TYPE_RGBA;
if (!avif->alphaPlane) {
@@ -395,7 +397,7 @@
rgb.format = AVIF_RGB_FORMAT_RGB;
}
avifRGBImageAllocatePixels(&rgb);
- if (avifImageYUVToRGB(avif, &rgb, conversionFlags) != AVIF_RESULT_OK) {
+ if (avifImageYUVToRGB(avif, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion to RGB failed: %s\n", outputFilename);
goto cleanup;
}
diff --git a/apps/shared/avifpng.h b/apps/shared/avifpng.h
index 9597211..cd52e29 100644
--- a/apps/shared/avifpng.h
+++ b/apps/shared/avifpng.h
@@ -15,16 +15,12 @@
avifImage * avif,
avifPixelFormat requestedFormat,
uint32_t requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP,
uint32_t * outPNGDepth);
-avifBool avifPNGWrite(const char * outputFilename,
- const avifImage * avif,
- uint32_t requestedDepth,
- avifYUVToRGBFlags conversionFlags,
- int compressionLevel);
+avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling flags, int compressionLevel);
#ifdef __cplusplus
} // extern "C"
diff --git a/apps/shared/avifutil.c b/apps/shared/avifutil.c
index c0c4470..7d2db88 100644
--- a/apps/shared/avifutil.c
+++ b/apps/shared/avifutil.c
@@ -227,7 +227,7 @@
avifAppFileFormat avifReadImage(const char * filename,
avifPixelFormat requestedFormat,
int requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP,
diff --git a/apps/shared/avifutil.h b/apps/shared/avifutil.h
index c1b77cd..d6da3fe 100644
--- a/apps/shared/avifutil.h
+++ b/apps/shared/avifutil.h
@@ -63,7 +63,7 @@
avifAppFileFormat avifReadImage(const char * filename,
avifPixelFormat requestedFormat,
int requestedDepth,
- avifRGBToYUVFlags flags,
+ avifChromaDownsampling flags,
avifBool ignoreICC,
avifBool ignoreExif,
avifBool ignoreXMP,
diff --git a/contrib/gdk-pixbuf/loader.c b/contrib/gdk-pixbuf/loader.c
index c9a760b..32c6eee 100644
--- a/contrib/gdk-pixbuf/loader.c
+++ b/contrib/gdk-pixbuf/loader.c
@@ -124,7 +124,7 @@
rgb.pixels = gdk_pixbuf_get_pixels(output);
rgb.rowBytes = gdk_pixbuf_get_rowstride(output);
- ret = avifImageYUVToRGB(image, &rgb, AVIF_YUV_TO_RGB_DEFAULT);
+ ret = avifImageYUVToRGB(image, &rgb);
if (ret != AVIF_RESULT_OK) {
g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED,
"Failed to convert YUV to RGB: %s", avifResultToString(ret));
@@ -448,7 +448,7 @@
rgb.format = AVIF_RGB_FORMAT_RGB;
}
- res = avifImageRGBToYUV(avif, &rgb, AVIF_RGB_TO_YUV_DEFAULT);
+ res = avifImageRGBToYUV(avif, &rgb);
if ( res != AVIF_RESULT_OK ) {
g_set_error(error,
GDK_PIXBUF_ERROR,
diff --git a/examples/avif_example_decode_file.c b/examples/avif_example_decode_file.c
index 1f8d1fa..f3fafb9 100644
--- a/examples/avif_example_decode_file.c
+++ b/examples/avif_example_decode_file.c
@@ -53,14 +53,14 @@
// * this frame's sequence timing
avifRGBImageSetDefaults(&rgb, decoder->image);
- // Override YUV(A)->RGB(A) defaults here: depth, format, ignoreAlpha, alphaPremultiplied, etc
+ // Override YUV(A)->RGB(A) defaults here:
+ // depth, format, chromaUpsampling, avoidLibYUV, ignoreAlpha, alphaPremultiplied, etc.
// Alternative: set rgb.pixels and rgb.rowBytes yourself, which should match your chosen rgb.format
// Be sure to use uint16_t* instead of uint8_t* for rgb.pixels/rgb.rowBytes if (rgb.depth > 8)
avifRGBImageAllocatePixels(&rgb);
- // Other flags than AVIF_YUV_TO_RGB_DEFAULT, such as AVIF_CHROMA_UPSAMPLING_NEAREST, can be passed.
- if (avifImageYUVToRGB(decoder->image, &rgb, AVIF_YUV_TO_RGB_DEFAULT) != AVIF_RESULT_OK) {
+ if (avifImageYUVToRGB(decoder->image, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion from YUV failed: %s\n", inputFilename);
goto cleanup;
}
diff --git a/examples/avif_example_decode_memory.c b/examples/avif_example_decode_memory.c
index d5a9aa9..2cf4a0b 100644
--- a/examples/avif_example_decode_memory.c
+++ b/examples/avif_example_decode_memory.c
@@ -75,14 +75,14 @@
// * this frame's sequence timing
avifRGBImageSetDefaults(&rgb, decoder->image);
- // Override YUV(A)->RGB(A) defaults here: depth, format, ignoreAlpha, alphaPremultiplied, etc
+ // Override YUV(A)->RGB(A) defaults here:
+ // depth, format, chromaUpsampling, avoidLibYUV, ignoreAlpha, alphaPremultiplied, etc.
// Alternative: set rgb.pixels and rgb.rowBytes yourself, which should match your chosen rgb.format
// Be sure to use uint16_t* instead of uint8_t* for rgb.pixels/rgb.rowBytes if (rgb.depth > 8)
avifRGBImageAllocatePixels(&rgb);
- // Other flags than AVIF_YUV_TO_RGB_DEFAULT, such as AVIF_CHROMA_UPSAMPLING_NEAREST, can be passed.
- if (avifImageYUVToRGB(decoder->image, &rgb, AVIF_YUV_TO_RGB_DEFAULT) != AVIF_RESULT_OK) {
+ if (avifImageYUVToRGB(decoder->image, &rgb) != AVIF_RESULT_OK) {
fprintf(stderr, "Conversion from YUV failed: %s\n", inputFilename);
goto cleanup;
}
diff --git a/examples/avif_example_encode.c b/examples/avif_example_encode.c
index b95cf0a..34b6cdc 100644
--- a/examples/avif_example_encode.c
+++ b/examples/avif_example_encode.c
@@ -57,7 +57,8 @@
printf("Encoding from converted RGBA\n");
avifRGBImageSetDefaults(&rgb, image);
- // Override RGB(A)->YUV(A) defaults here: depth, format, ignoreAlpha, alphaPremultiplied, etc
+ // Override RGB(A)->YUV(A) defaults here:
+ // depth, format, chromaDownsampling, avoidLibYUV, ignoreAlpha, alphaPremultiplied, etc.
// Alternative: set rgb.pixels and rgb.rowBytes yourself, which should match your chosen rgb.format
// Be sure to use uint16_t* instead of uint8_t* for rgb.pixels/rgb.rowBytes if (rgb.depth > 8)
@@ -66,8 +67,7 @@
// Fill your RGB(A) data here
memset(rgb.pixels, 255, rgb.rowBytes * image->height);
- // Other flags than AVIF_RGB_TO_YUV_DEFAULT, such as AVIF_RGB_TO_YUV_AVOID_LIBYUV, can be passed.
- avifResult convertResult = avifImageRGBToYUV(image, &rgb, AVIF_RGB_TO_YUV_DEFAULT);
+ avifResult convertResult = avifImageRGBToYUV(image, &rgb);
if (convertResult != AVIF_RESULT_OK) {
fprintf(stderr, "Failed to convert to YUV(A): %s\n", avifResultToString(convertResult));
goto cleanup;
diff --git a/include/avif/avif.h b/include/avif/avif.h
index a946d94..9e0203d 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -530,6 +530,9 @@
// 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.
+// libyuv is faster but may have slightly less precision than built-in conversion, so avoidLibYUV
+// should be set to AVIF_TRUE when AVIF_CHROMA_UPSAMPLING_BEST_QUALITY or
+// AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY is used, to get the most precise but slowest results.
// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV
// rely on the ordering of this enum values for their correctness. So changing
@@ -558,14 +561,39 @@
AVIF_API uint32_t avifRGBFormatChannelCount(avifRGBFormat format);
AVIF_API avifBool avifRGBFormatHasAlpha(avifRGBFormat format);
+typedef enum avifChromaUpsampling
+{
+ AVIF_CHROMA_UPSAMPLING_AUTOMATIC = 0, // Chooses best trade off of speed/quality (uses BILINEAR libyuv if available,
+ // or fallbacks to NEAREST libyuv if available, or fallbacks to BILINEAR built-in)
+ AVIF_CHROMA_UPSAMPLING_FASTEST = 1, // Chooses speed over quality (same as NEAREST)
+ AVIF_CHROMA_UPSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling, given settings (same as BILINEAR)
+ AVIF_CHROMA_UPSAMPLING_NEAREST = 3, // Uses nearest-neighbor filter
+ AVIF_CHROMA_UPSAMPLING_BILINEAR = 4 // Uses bilinear filter
+} avifChromaUpsampling;
+
+typedef enum avifChromaDownsampling
+{
+ AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC = 0, // Chooses best trade off of speed/quality (same as AVERAGE)
+ AVIF_CHROMA_DOWNSAMPLING_FASTEST = 1, // Chooses speed over quality (same as AVERAGE)
+ AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling (same as AVERAGE)
+ AVIF_CHROMA_DOWNSAMPLING_AVERAGE = 3, // Uses averaging filter
+ AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV = 4 // Uses sharp yuv filter (libsharpyuv), available for 4:2:0 only, ignored for 4:2:2
+} avifChromaDownsampling;
+
typedef struct avifRGBImage
{
- uint32_t width; // must match associated avifImage
- uint32_t height; // must match associated avifImage
- uint32_t depth; // legal depths [8, 10, 12, 16]. if depth>8, pixels must be uint16_t internally
- avifRGBFormat format; // all channels are always full range
- avifBool ignoreAlpha; // Used for XRGB formats, treats formats containing alpha (such as ARGB) as if they were
- // RGB, treating the alpha bits as if they were all 1.
+ uint32_t width; // must match associated avifImage
+ uint32_t height; // must match associated avifImage
+ uint32_t depth; // legal depths [8, 10, 12, 16]. if depth>8, pixels must be uint16_t internally
+ avifRGBFormat format; // all channels are always full range
+ avifChromaUpsampling chromaUpsampling; // How to upsample from 4:2:0 or 4:2:2 UV when converting to RGB (ignored for 4:4:4 and 4:0:0).
+ // Ignored when converting to YUV. Defaults to AVIF_CHROMA_UPSAMPLING_AUTOMATIC.
+ avifChromaDownsampling chromaDownsampling; // How to downsample to 4:2:0 or 4:2:2 UV when converting from RGB (ignored for 4:4:4 and 4:0:0).
+ // Ignored when converting to RGB. Defaults to AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC.
+ avifBool avoidLibYUV; // If AVIF_FALSE and libyuv conversion between RGB and YUV (including upsampling or downsampling if any)
+ // is available for the avifImage/avifRGBImage combination, then libyuv is used. Default is AVIF_FALSE.
+ avifBool ignoreAlpha; // Used for XRGB formats, treats formats containing alpha (such as ARGB) as if they were RGB, treating
+ // the alpha bits as if they were all 1.
avifBool alphaPremultiplied; // indicates if RGB value is pre-multiplied by alpha. Default: false
avifBool isFloat; // indicates if RGBA values are in half float (f16) format. Valid only when depth == 16. Default: false
@@ -583,44 +611,9 @@
AVIF_API void avifRGBImageAllocatePixels(avifRGBImage * rgb);
AVIF_API void avifRGBImageFreePixels(avifRGBImage * rgb);
-// Conversion options.
-typedef enum avifRGBToYUVFlag
-{
- AVIF_RGB_TO_YUV_DEFAULT = 0, // Uses the first available downsampling filter among:
- // libyuv average, built-in average
-
- // libyuv preference
-
- AVIF_RGB_TO_YUV_AVOID_LIBYUV = (1 << 0), // Only picks built-in paths. The default is to prefer libyuv if available.
-
- // Conversion from RGB to YUV 4:2:2 or YUV 4:2:0 (ignored in all other cases)
-
- // Chroma downsampling filter. Set at most one:
- AVIF_CHROMA_DOWNSAMPLING_AVERAGE = (1 << 10), // only use the averaging filter (libyuv or built-in)
- AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV = (1 << 11), // only use sharp yuv filter (libsharpyuv),
- // available for 4:2:0 only, ignored for 4:2:2
-} avifRGBToYUVFlag;
-typedef uint32_t avifRGBToYUVFlags;
-typedef enum avifYUVToRGBFlag
-{
- AVIF_YUV_TO_RGB_DEFAULT = 0, // Uses the first available upsampling filter among:
- // libyuv bilinear, libyuv nearest-neighbor, built-in bilinear
-
- // libyuv preference
-
- AVIF_YUV_TO_RGB_AVOID_LIBYUV = (1 << 0), // Only picks built-in paths. The default is to prefer libyuv if available.
-
- // Conversion from YUV 4:2:2 or YUV 4:2:0 to RGB (ignored in all other cases)
-
- // Chroma upsampling filter. Set at most one:
- AVIF_CHROMA_UPSAMPLING_NEAREST = (1 << 10), // only use the fast nearest-neighbor filter (libyuv or built-in)
- AVIF_CHROMA_UPSAMPLING_BILINEAR = (1 << 11) // only use the good-quality bilinear filter (libyuv or built-in)
-} avifYUVToRGBFlag;
-typedef uint32_t avifYUVToRGBFlags;
-
-// Conversion functions.
-AVIF_API avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb, avifRGBToYUVFlags flags);
-AVIF_API avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags);
+// The main conversion functions
+AVIF_API avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb);
+AVIF_API avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb);
// Premultiply handling functions.
// (Un)premultiply is automatically done by the main conversion functions above,
diff --git a/include/avif/internal.h b/include/avif/internal.h
index 283502d..5cc5aab 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -149,7 +149,7 @@
// * AVIF_RESULT_OK - Converted successfully with libyuv
// * AVIF_RESULT_NOT_IMPLEMENTED - The fast path for this combination is not implemented with libyuv, use built-in YUV conversion
// * [any other error] - Return error to caller
-avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags);
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb);
// Returns:
// * AVIF_RESULT_OK - Converted successfully with libsharpyuv
diff --git a/src/avif.c b/src/avif.c
index d6e4afb..4ef4780 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -457,6 +457,9 @@
rgb->height = image->height;
rgb->depth = image->depth;
rgb->format = AVIF_RGB_FORMAT_RGBA;
+ rgb->chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
+ rgb->chromaDownsampling = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC;
+ rgb->avoidLibYUV = AVIF_FALSE;
rgb->ignoreAlpha = AVIF_FALSE;
rgb->pixels = NULL;
rgb->rowBytes = 0;
diff --git a/src/reformat.c b/src/reformat.c
index d792a85..951c46b 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -194,7 +194,7 @@
return AVIF_CLAMP(unorm, 0, state->yuvMaxChannel);
}
-avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb, avifRGBToYUVFlags flags)
+avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb)
{
if (!rgb->pixels || rgb->format == AVIF_RGB_FORMAT_RGB_565) {
return AVIF_RESULT_REFORMAT_FAILED;
@@ -227,7 +227,7 @@
avifBool converted = AVIF_FALSE;
// Try converting with libsharpyuv.
- if ((flags & AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV) && image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
+ if ((rgb->chromaDownsampling == AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV) && (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420)) {
const avifResult libSharpYUVResult = avifImageRGBToYUVLibSharpYUV(image, rgb, &state);
if (libSharpYUVResult != AVIF_RESULT_OK) {
// Return the error if sharpyuv was requested but failed for any reason, including libsharpyuv not being available.
@@ -236,7 +236,7 @@
converted = AVIF_TRUE;
}
- if (!converted && !(flags & AVIF_RGB_TO_YUV_AVOID_LIBYUV) && (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP)) {
+ if (!converted && !rgb->avoidLibYUV && (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP)) {
avifResult libyuvResult = avifImageRGBToYUVLibYUV(image, rgb);
if (libyuvResult == AVIF_RESULT_OK) {
converted = AVIF_TRUE;
@@ -479,7 +479,7 @@
}
// Note: This function handles alpha (un)multiply.
-static avifResult avifImageYUVAnyToRGBAnySlow(const avifImage * image, avifRGBImage * rgb, avifReformatState * state, avifYUVToRGBFlags flags)
+static avifResult avifImageYUVAnyToRGBAnySlow(const avifImage * image, avifRGBImage * rgb, avifReformatState * state)
{
// Aliases for some state
const float kr = state->kr;
@@ -647,7 +647,8 @@
}
}
- if (flags & AVIF_CHROMA_UPSAMPLING_NEAREST) {
+ if ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) ||
+ (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST)) {
// Nearest neighbor; ignore all UVs but the closest one
Cb = unormFloatTableUV[unormU[0][0]];
Cr = unormFloatTableUV[unormV[0][0]];
@@ -1163,10 +1164,10 @@
return AVIF_RESULT_OK;
}
-static avifResult avifRGBImageToF16(avifRGBImage * rgb, avifYUVToRGBFlags flags)
+static avifResult avifRGBImageToF16(avifRGBImage * rgb)
{
avifResult libyuvResult = AVIF_RESULT_NOT_IMPLEMENTED;
- if (!(flags & AVIF_YUV_TO_RGB_AVOID_LIBYUV)) {
+ if (!rgb->avoidLibYUV) {
libyuvResult = avifRGBImageToF16LibYUV(rgb);
}
if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) {
@@ -1195,7 +1196,7 @@
return AVIF_RESULT_OK;
}
-avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
+avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb)
{
if (!image->yuvPlanes[AVIF_CHAN_Y]) {
return AVIF_RESULT_REFORMAT_FAILED;
@@ -1206,16 +1207,10 @@
return AVIF_RESULT_REFORMAT_FAILED;
}
- // At most one filter can be specified.
- if ((flags & AVIF_CHROMA_UPSAMPLING_NEAREST) && (flags & AVIF_CHROMA_UPSAMPLING_BILINEAR)) {
- return AVIF_RESULT_REFORMAT_FAILED;
- }
-
avifAlphaMultiplyMode alphaMultiplyMode = state.toRGBAlphaMode;
avifBool convertedWithLibYUV = AVIF_FALSE;
- if (!(flags & AVIF_YUV_TO_RGB_AVOID_LIBYUV) &&
- ((alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP) || avifRGBFormatHasAlpha(rgb->format))) {
- avifResult libyuvResult = avifImageYUVToRGBLibYUV(image, rgb, flags);
+ if (!rgb->avoidLibYUV && ((alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP) || avifRGBFormatHasAlpha(rgb->format))) {
+ avifResult libyuvResult = avifImageYUVToRGBLibYUV(image, rgb);
if (libyuvResult == AVIF_RESULT_OK) {
convertedWithLibYUV = AVIF_TRUE;
} else {
@@ -1263,7 +1258,8 @@
const avifBool hasColor =
(image->yuvRowBytes[AVIF_CHAN_U] && image->yuvRowBytes[AVIF_CHAN_V] && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400));
- if ((!hasColor || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) || (flags & AVIF_CHROMA_UPSAMPLING_NEAREST)) &&
+ if ((!hasColor || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) ||
+ ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST))) &&
(alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP || avifRGBFormatHasAlpha(rgb->format))) {
// Explanations on the above conditional:
// * None of these fast paths currently support bilinear upsampling, so avoid all of them
@@ -1325,7 +1321,7 @@
if (convertResult == AVIF_RESULT_NOT_IMPLEMENTED) {
// If we get here, there is no fast path for this combination. Time to be slow!
- convertResult = avifImageYUVAnyToRGBAnySlow(image, rgb, &state, flags);
+ convertResult = avifImageYUVAnyToRGBAnySlow(image, rgb, &state);
// The slow path also handles alpha (un)multiply, so forget the operation here.
alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP;
@@ -1351,7 +1347,7 @@
// Convert pixels to half floats (F16), if necessary.
if (rgb->isFloat) {
- return avifRGBImageToF16(rgb, flags);
+ return avifRGBImageToF16(rgb);
}
return AVIF_RESULT_OK;
diff --git a/src/reformat_libyuv.c b/src/reformat_libyuv.c
index 9a9a18d..f4cb5b2 100644
--- a/src/reformat_libyuv.c
+++ b/src/reformat_libyuv.c
@@ -12,11 +12,10 @@
(void)rgb;
return AVIF_RESULT_NOT_IMPLEMENTED;
}
-avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb)
{
(void)image;
(void)rgb;
- (void)flags;
return AVIF_RESULT_NOT_IMPLEMENTED;
}
avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb)
@@ -325,15 +324,13 @@
static avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
avifRGBImage * rgb,
const struct YuvConstants * matrixYUV,
- const struct YuvConstants * matrixYVU,
- avifYUVToRGBFlags flags);
+ const struct YuvConstants * matrixYVU);
static avifResult avifImageYUVToRGBLibYUVHighBitDepth(const avifImage * image,
avifRGBImage * rgb,
const struct YuvConstants * matrixYUV,
- const struct YuvConstants * matrixYVU,
- avifYUVToRGBFlags flags);
+ const struct YuvConstants * matrixYVU);
-avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb)
{
// See if the current settings can be accomplished with libyuv, and use it (if possible).
@@ -469,11 +466,11 @@
}
if ((image->depth == 8) && (rgb->depth == 8)) {
- return avifImageYUVToRGBLibYUV8bpc(image, rgb, matrixYUV, matrixYVU, flags);
+ return avifImageYUVToRGBLibYUV8bpc(image, rgb, matrixYUV, matrixYVU);
}
if (((image->depth == 10) || (image->depth == 12)) && (rgb->depth == 8)) {
- return avifImageYUVToRGBLibYUVHighBitDepth(image, rgb, matrixYUV, matrixYVU, flags);
+ return avifImageYUVToRGBLibYUVHighBitDepth(image, rgb, matrixYUV, matrixYVU);
}
// This function didn't do anything; use the built-in YUV conversion
@@ -497,8 +494,7 @@
avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
avifRGBImage * rgb,
const struct YuvConstants * matrixYUV,
- const struct YuvConstants * matrixYVU,
- avifYUVToRGBFlags flags)
+ const struct YuvConstants * matrixYVU)
{
// See if the current settings can be accomplished with libyuv, and use it (if possible).
@@ -575,7 +571,10 @@
};
YUVToRGBMatrixFilter yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[rgb->format][image->yuvFormat];
if (yuvToRgbMatrixFilter != NULL) {
- const enum FilterMode filter = (flags & AVIF_CHROMA_UPSAMPLING_NEAREST) ? kFilterNone : kFilterBilinear;
+ const enum FilterMode filter = ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) ||
+ (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST))
+ ? kFilterNone
+ : kFilterBilinear;
if (yuvToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
image->yuvRowBytes[AVIF_CHAN_Y],
image->yuvPlanes[uPlaneIndex],
@@ -594,7 +593,7 @@
}
// Only proceed with the nearest-neighbor filter if explicitly specified or left as default.
- if (flags & AVIF_CHROMA_UPSAMPLING_BILINEAR) {
+ if ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BEST_QUALITY) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BILINEAR)) {
return AVIF_RESULT_NOT_IMPLEMENTED;
}
// Lookup table for YUV To RGB Matrix (nearest-neighbor filter).
@@ -636,8 +635,7 @@
avifResult avifImageYUVToRGBLibYUVHighBitDepth(const avifImage * image,
avifRGBImage * rgb,
const struct YuvConstants * matrixYUV,
- const struct YuvConstants * matrixYVU,
- avifYUVToRGBFlags flags)
+ const struct YuvConstants * matrixYVU)
{
// See if the current settings can be accomplished with libyuv, and use it (if possible).
@@ -704,7 +702,10 @@
};
YUVToRGBMatrixFilter yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[depthIndex][rgb->format][image->yuvFormat];
if (yuvToRgbMatrixFilter != NULL) {
- const enum FilterMode filter = (flags & AVIF_CHROMA_UPSAMPLING_NEAREST) ? kFilterNone : kFilterBilinear;
+ const enum FilterMode filter =
+ ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST))
+ ? kFilterNone
+ : kFilterBilinear;
if (yuvToRgbMatrixFilter((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y],
image->yuvRowBytes[AVIF_CHAN_Y] / 2,
(const uint16_t *)image->yuvPlanes[uPlaneIndex],
@@ -723,7 +724,7 @@
}
// Only proceed with the nearest-neighbor filter if explicitly specified or left as default.
- if (flags & AVIF_CHROMA_UPSAMPLING_BILINEAR) {
+ if ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BEST_QUALITY) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BILINEAR)) {
return AVIF_RESULT_NOT_IMPLEMENTED;
}
// Lookup table for YUV To RGB Matrix (nearest-neighbor filter).
diff --git a/tests/avifyuv.c b/tests/avifyuv.c
index a98dad7..b7e2df1 100644
--- a/tests/avifyuv.c
+++ b/tests/avifyuv.c
@@ -203,8 +203,8 @@
}
}
- avifImageRGBToYUV(image, &srcRGB, AVIF_RGB_TO_YUV_DEFAULT);
- avifImageYUVToRGB(image, &dstRGB, AVIF_YUV_TO_RGB_DEFAULT);
+ avifImageRGBToYUV(image, &srcRGB);
+ avifImageYUVToRGB(image, &dstRGB);
for (int y = 0; y < dim; ++y) {
const uint8_t * srcRow = &srcRGB.pixels[y * srcRGB.rowBytes];
@@ -344,23 +344,23 @@
avifImageFreePlanes(image, AVIF_PLANES_ALL);
image->depth = yuvDepth;
image->yuvRange = yuvRange;
- avifImageRGBToYUV(image, &srcRGB, AVIF_RGB_TO_YUV_DEFAULT);
+ avifImageRGBToYUV(image, &srcRGB);
avifRGBImage intermediateRGB;
avifRGBImageSetDefaults(&intermediateRGB, image);
intermediateRGB.depth = rgbDepth;
intermediateRGB.format = rgbFormat;
avifRGBImageAllocatePixels(&intermediateRGB);
- avifImageYUVToRGB(image, &intermediateRGB, AVIF_YUV_TO_RGB_DEFAULT);
+ avifImageYUVToRGB(image, &intermediateRGB);
avifImageFreePlanes(image, AVIF_PLANES_ALL);
- avifImageRGBToYUV(image, &intermediateRGB, AVIF_RGB_TO_YUV_DEFAULT);
+ avifImageRGBToYUV(image, &intermediateRGB);
avifRGBImage dstRGB;
avifRGBImageSetDefaults(&dstRGB, image);
dstRGB.depth = yuvDepth;
avifRGBImageAllocatePixels(&dstRGB);
- avifImageYUVToRGB(image, &dstRGB, AVIF_YUV_TO_RGB_DEFAULT);
+ avifImageYUVToRGB(image, &dstRGB);
avifBool moveOn = AVIF_FALSE;
for (uint32_t j = 0; j < originalHeight; ++j) {
diff --git a/tests/gtest/are_images_equal.cc b/tests/gtest/are_images_equal.cc
index ac9c1d2..93a1012 100644
--- a/tests/gtest/are_images_equal.cc
+++ b/tests/gtest/are_images_equal.cc
@@ -31,10 +31,10 @@
// Make sure no color conversion happens.
decoded[i]->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY;
if (avifReadImage(argv[i + 1], requestedFormat, kRequestedDepth,
- AVIF_RGB_TO_YUV_DEFAULT, /*ignoreICC=*/AVIF_FALSE,
- /*ignoreExif=*/AVIF_FALSE, /*ignoreXMP=*/AVIF_FALSE,
- decoded[i].get(), &depth[i], nullptr,
- nullptr) == AVIF_APP_FILE_FORMAT_UNKNOWN) {
+ AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
+ /*ignoreICC=*/AVIF_FALSE, /*ignoreExif=*/AVIF_FALSE,
+ /*ignoreXMP=*/AVIF_FALSE, decoded[i].get(), &depth[i],
+ nullptr, nullptr) == AVIF_APP_FILE_FORMAT_UNKNOWN) {
std::cerr << "Image " << argv[i + 1] << " cannot be read." << std::endl;
return 2;
}
diff --git a/tests/gtest/avifmetadatatest.cc b/tests/gtest/avifmetadatatest.cc
index 4ac7961..5017f13 100644
--- a/tests/gtest/avifmetadatatest.cc
+++ b/tests/gtest/avifmetadatatest.cc
@@ -121,8 +121,8 @@
const bool expect_xmp = std::get<6>(GetParam());
const testutil::AvifImagePtr image = testutil::ReadImage(
- data_path, file_name, AVIF_PIXEL_FORMAT_NONE, 0, AVIF_RGB_TO_YUV_DEFAULT,
- !use_icc, !use_exif, !use_xmp);
+ data_path, file_name, AVIF_PIXEL_FORMAT_NONE, 0,
+ AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, !use_icc, !use_exif, !use_xmp);
ASSERT_NE(image, nullptr);
EXPECT_NE(image->width * image->height, 0u);
diff --git a/tests/gtest/avifrgbtoyuvtest.cc b/tests/gtest/avifrgbtoyuvtest.cc
index 08e417e..81b5675 100644
--- a/tests/gtest/avifrgbtoyuvtest.cc
+++ b/tests/gtest/avifrgbtoyuvtest.cc
@@ -112,12 +112,11 @@
//------------------------------------------------------------------------------
class RGBToYUVTest
- : public testing::TestWithParam<
- std::tuple</*rgb_depth=*/int, /*yuv_depth=*/int, avifRGBFormat,
- avifPixelFormat, avifRange, avifMatrixCoefficients,
- /*sharpYuv=*/bool,
- /*add_noise=*/bool, /*rgb_step=*/uint32_t,
- /*max_abs_average_diff=*/double, /*min_psnr=*/double>> {};
+ : public testing::TestWithParam<std::tuple<
+ /*rgb_depth=*/int, /*yuv_depth=*/int, avifRGBFormat, avifPixelFormat,
+ avifRange, avifMatrixCoefficients, avifChromaDownsampling,
+ /*add_noise=*/bool, /*rgb_step=*/uint32_t,
+ /*max_abs_average_diff=*/double, /*min_psnr=*/double>> {};
// Converts from RGB to YUV and back to RGB for all RGB combinations, separated
// by a color step for reasonable timing. If add_noise is true, also applies
@@ -129,7 +128,7 @@
const avifPixelFormat yuv_format = std::get<3>(GetParam());
const avifRange yuv_range = std::get<4>(GetParam());
const avifMatrixCoefficients matrix_coefficients = std::get<5>(GetParam());
- const avifRGBToYUVFlags rgb_to_yuv_flags = std::get<6>(GetParam());
+ const avifChromaDownsampling chroma_downsampling = std::get<6>(GetParam());
// Whether to add noise to the input RGB samples. Should only impact
// subsampled chroma (4:2:2 and 4:2:0).
const bool add_noise = std::get<7>(GetParam());
@@ -154,6 +153,7 @@
yuv->matrixCoefficients = matrix_coefficients;
yuv->yuvRange = yuv_range;
testutil::AvifRgbImage src_rgb(yuv.get(), rgb_depth, rgb_format);
+ src_rgb.chromaDownsampling = chroma_downsampling;
testutil::AvifRgbImage dst_rgb(yuv.get(), rgb_depth, rgb_format);
const testutil::RgbChannelOffsets offsets =
testutil::GetRgbChannelOffsets(rgb_format);
@@ -184,10 +184,8 @@
ModifyImageChannel(&src_rgb, offsets.b, kBlueNoise);
}
- ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb, rgb_to_yuv_flags),
- AVIF_RESULT_OK);
- ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb, AVIF_YUV_TO_RGB_DEFAULT),
- AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
GetDiffSumAndSqDiffSum(src_rgb, dst_rgb, &diff_sum, &abs_diff_sum,
&sq_diff_sum, &max_abs_diff);
num_diffs += src_rgb.width * src_rgb.height * 3; // Alpha is lossless.
@@ -205,12 +203,8 @@
ModifyImageChannel(&src_rgb, offsets.b, kBlueNoise);
}
- ASSERT_EQ(
- avifImageRGBToYUV(yuv.get(), &src_rgb, AVIF_RGB_TO_YUV_DEFAULT),
- AVIF_RESULT_OK);
- ASSERT_EQ(
- avifImageYUVToRGB(yuv.get(), &dst_rgb, AVIF_YUV_TO_RGB_DEFAULT),
- AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
GetDiffSumAndSqDiffSum(src_rgb, dst_rgb, &diff_sum, &abs_diff_sum,
&sq_diff_sum, &max_abs_diff);
num_diffs +=
@@ -254,8 +248,9 @@
const avifPixelFormat yuv_format = std::get<3>(GetParam());
const avifRange yuv_range = std::get<4>(GetParam());
const avifMatrixCoefficients matrix_coefficients = std::get<5>(GetParam());
+ const avifChromaDownsampling chroma_downsampling = std::get<6>(GetParam());
// Whether to add noise to the input RGB samples.
- const bool add_noise = std::get<6>(GetParam());
+ const bool add_noise = std::get<7>(GetParam());
// Threshold to pass.
const double min_psnr = std::get<9>(GetParam());
// Deduced constants.
@@ -274,6 +269,7 @@
yuv->matrixCoefficients = matrix_coefficients;
yuv->yuvRange = yuv_range;
testutil::AvifRgbImage src_rgb(yuv.get(), rgb_depth, rgb_format);
+ src_rgb.chromaDownsampling = chroma_downsampling;
testutil::AvifRgbImage dst_rgb(yuv.get(), rgb_depth, rgb_format);
const testutil::RgbChannelOffsets offsets =
testutil::GetRgbChannelOffsets(rgb_format);
@@ -294,10 +290,8 @@
testutil::FillImageChannel(&src_rgb, offsets.a, rgb_max);
}
- ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb, AVIF_RGB_TO_YUV_DEFAULT),
- AVIF_RESULT_OK);
- ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb, AVIF_YUV_TO_RGB_DEFAULT),
- AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
+ ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
GetDiffSumAndSqDiffSum(src_rgb, dst_rgb, &diff_sum, &abs_diff_sum,
&sq_diff_sum, &max_abs_diff);
num_diffs += src_rgb.width * src_rgb.height * 3;
@@ -330,7 +324,7 @@
/*yuv_depth=*/Values(8), Values(AVIF_RGB_FORMAT_RGBA),
Values(AVIF_PIXEL_FORMAT_YUV420), Values(AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(true),
/*rgb_step=*/Values(3),
/*max_abs_average_diff=*/Values(0.1), // The color drift is almost
@@ -340,39 +334,42 @@
// Keeping RGB samples in full range and same or higher bit depth should not
// bring any loss in the roundtrip.
-INSTANTIATE_TEST_SUITE_P(
- Identity8b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(8),
- /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsIdentity),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(true),
- /*rgb_step=*/Values(31),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
-INSTANTIATE_TEST_SUITE_P(
- Identity10b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(10),
- /*yuv_depth=*/Values(10, 12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsIdentity),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(true),
- /*rgb_step=*/Values(101),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
-INSTANTIATE_TEST_SUITE_P(
- Identity12b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(12),
- /*yuv_depth=*/Values(12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV444), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsIdentity),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(true),
- /*rgb_step=*/Values(401),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(Identity8b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(8),
+ /*yuv_depth=*/Values(8, 10, 12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV444),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsIdentity),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(true),
+ /*rgb_step=*/Values(31),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(Identity10b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(10),
+ /*yuv_depth=*/Values(10, 12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV444),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsIdentity),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(true),
+ /*rgb_step=*/Values(101),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(Identity12b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(12),
+ /*yuv_depth=*/Values(12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV444),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsIdentity),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(true),
+ /*rgb_step=*/Values(401),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
// 4:4:4 and chroma subsampling have similar distortions on plain color inputs.
INSTANTIATE_TEST_SUITE_P(
@@ -383,7 +380,7 @@
Values(AVIF_PIXEL_FORMAT_YUV444, AVIF_PIXEL_FORMAT_YUV422,
AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_FULL), Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(false),
/*rgb_step=*/Values(17),
/*max_abs_average_diff=*/Values(0.02), // The color drift is centered.
@@ -393,39 +390,42 @@
// Converting grey RGB samples to full-range monochrome of same or greater bit
// depth should be lossless.
-INSTANTIATE_TEST_SUITE_P(
- MonochromeLossless8b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(8),
- /*yuv_depth=*/Values(8, 10, 12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(false),
- /*rgb_step=*/Values(1),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
-INSTANTIATE_TEST_SUITE_P(
- MonochromeLossless10b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(10),
- /*yuv_depth=*/Values(10, 12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(false),
- /*rgb_step=*/Values(1),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
-INSTANTIATE_TEST_SUITE_P(
- MonochromeLossless12b, RGBToYUVTest,
- Combine(/*rgb_depth=*/Values(12),
- /*yuv_depth=*/Values(12), ValuesIn(kAllRgbFormats),
- Values(AVIF_PIXEL_FORMAT_YUV400), Values(AVIF_RANGE_FULL),
- Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
- /*add_noise=*/Values(false),
- /*rgb_step=*/Values(1),
- /*max_abs_average_diff=*/Values(0.),
- /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(MonochromeLossless8b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(8),
+ /*yuv_depth=*/Values(8, 10, 12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV400),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsBT601),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(false),
+ /*rgb_step=*/Values(1),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(MonochromeLossless10b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(10),
+ /*yuv_depth=*/Values(10, 12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV400),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsBT601),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(false),
+ /*rgb_step=*/Values(1),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
+INSTANTIATE_TEST_SUITE_P(MonochromeLossless12b, RGBToYUVTest,
+ Combine(/*rgb_depth=*/Values(12),
+ /*yuv_depth=*/Values(12),
+ ValuesIn(kAllRgbFormats),
+ Values(AVIF_PIXEL_FORMAT_YUV400),
+ Values(AVIF_RANGE_FULL),
+ Values(kMatrixCoefficientsBT601),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
+ /*add_noise=*/Values(false),
+ /*rgb_step=*/Values(1),
+ /*max_abs_average_diff=*/Values(0.),
+ /*min_psnr=*/Values(99.)));
// Can be used to print the drift of all RGB to YUV conversion possibilities.
// Also used for coverage.
@@ -437,7 +437,7 @@
Values(AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601, kMatrixCoefficientsBT709),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
+ Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
/*add_noise=*/Values(true),
/*rgb_step=*/Values(17),
/*max_abs_average_diff=*/Values(1.2), // Sharp YUV introduces some
@@ -452,7 +452,7 @@
Values(AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
+ Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
/*add_noise=*/Values(true),
/*rgb_step=*/Values(211), // High or it would be too slow.
/*max_abs_average_diff=*/Values(1.2), // Sharp YUV introduces some
@@ -467,7 +467,7 @@
Values(AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
+ Values(AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV),
/*add_noise=*/Values(true),
/*rgb_step=*/Values(840), // High or it would be too slow.
/*max_abs_average_diff=*/Values(1.2), // Sharp YUV introduces some
@@ -485,7 +485,7 @@
AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(false, true),
/*rgb_step=*/Values(61), // High or it would be too slow.
/*max_abs_average_diff=*/Values(1.), // Not very accurate because
@@ -499,7 +499,7 @@
AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(false, true),
/*rgb_step=*/Values(211), // High or it would be too slow.
/*max_abs_average_diff=*/Values(0.2), // Not very accurate because
@@ -513,7 +513,7 @@
AVIF_PIXEL_FORMAT_YUV420),
Values(AVIF_RANGE_LIMITED, AVIF_RANGE_FULL),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(false, true),
/*rgb_step=*/Values(809), // High or it would be too slow.
/*max_abs_average_diff=*/Values(0.3), // Not very accurate because
@@ -532,7 +532,7 @@
AVIF_PIXEL_FORMAT_YUV420, AVIF_PIXEL_FORMAT_YUV400),
Values(AVIF_RANGE_FULL, AVIF_RANGE_LIMITED),
Values(kMatrixCoefficientsBT601),
- /*avif_rgb_to_yuv_flags=*/Values(AVIF_RGB_TO_YUV_DEFAULT),
+ Values(AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC),
/*add_noise=*/Values(false, true),
/*rgb_step=*/Values(3), // way faster and 99% similar to rgb_step=1
/*max_abs_average_diff=*/Values(10.),
diff --git a/tests/gtest/aviftest_helpers.cc b/tests/gtest/aviftest_helpers.cc
index 25827b6..57d8929 100644
--- a/tests/gtest/aviftest_helpers.cc
+++ b/tests/gtest/aviftest_helpers.cc
@@ -233,7 +233,7 @@
AvifImagePtr ReadImage(const char* folder_path, const char* file_name,
avifPixelFormat requested_format, int requested_depth,
- avifRGBToYUVFlags flags, avifBool ignore_icc,
+ avifChromaDownsampling flags, avifBool ignore_icc,
avifBool ignore_exif, avifBool ignore_xmp) {
testutil::AvifImagePtr image(avifImageCreateEmpty(), avifImageDestroy);
if (!image ||
diff --git a/tests/gtest/aviftest_helpers.h b/tests/gtest/aviftest_helpers.h
index 842b60d..a0b1c05 100644
--- a/tests/gtest/aviftest_helpers.h
+++ b/tests/gtest/aviftest_helpers.h
@@ -75,7 +75,8 @@
AvifImagePtr ReadImage(
const char* folder_path, const char* file_name,
avifPixelFormat requested_format = AVIF_PIXEL_FORMAT_NONE,
- int requested_depth = 0, avifRGBToYUVFlags flags = AVIF_RGB_TO_YUV_DEFAULT,
+ int requested_depth = 0,
+ avifChromaDownsampling flags = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC,
avifBool ignore_icc = false, avifBool ignore_exif = false,
avifBool ignore_xmp = false);
diff --git a/tests/oss-fuzz/avif_decode_fuzzer.cc b/tests/oss-fuzz/avif_decode_fuzzer.cc
index 702185c..ba3f924 100644
--- a/tests/oss-fuzz/avif_decode_fuzzer.cc
+++ b/tests/oss-fuzz/avif_decode_fuzzer.cc
@@ -8,7 +8,7 @@
static avifRGBFormat rgbFormats[] = { AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA };
static size_t rgbFormatsCount = sizeof(rgbFormats) / sizeof(rgbFormats[0]);
- static avifYUVToRGBFlags upsamplingFlags[] = { AVIF_CHROMA_UPSAMPLING_BILINEAR, AVIF_CHROMA_UPSAMPLING_NEAREST };
+ static avifChromaUpsampling upsamplingFlags[] = { AVIF_CHROMA_UPSAMPLING_BILINEAR, AVIF_CHROMA_UPSAMPLING_NEAREST };
static size_t upsamplingFlagsCount = sizeof(upsamplingFlags) / sizeof(upsamplingFlags[0]);
static uint32_t rgbDepths[] = { 8, 10 };
@@ -47,10 +47,11 @@
for (size_t rgbDepthsIndex = 0; rgbDepthsIndex < rgbDepthsCount; ++rgbDepthsIndex) {
// Convert to RGB
rgb.format = rgbFormats[rgbFormatsIndex];
+ rgb.chromaUpsampling = upsamplingFlags[upsamplingFlagsIndex];
+ rgb.avoidLibYUV = AVIF_TRUE;
rgb.depth = rgbDepths[rgbDepthsIndex];
avifRGBImageAllocatePixels(&rgb);
- avifResult rgbResult =
- avifImageYUVToRGB(decoder->image, &rgb, AVIF_YUV_TO_RGB_AVOID_LIBYUV | upsamplingFlags[upsamplingFlagsIndex]);
+ avifResult rgbResult = avifImageYUVToRGB(decoder->image, &rgb);
// Since avifImageRGBToYUV() ignores upsamplingFlags, we only need
// to test avifImageRGBToYUV() with a single upsamplingFlagsIndex.
if ((rgbResult == AVIF_RESULT_OK) && (upsamplingFlagsIndex == 0)) {
@@ -60,7 +61,7 @@
decoder->image->height,
yuvDepths[yuvDepthsIndex],
decoder->image->yuvFormat);
- avifResult yuvResult = avifImageRGBToYUV(tempImage, &rgb, AVIF_RGB_TO_YUV_AVOID_LIBYUV);
+ avifResult yuvResult = avifImageRGBToYUV(tempImage, &rgb);
if (yuvResult != AVIF_RESULT_OK) {
}
avifImageDestroy(tempImage);