Refactor avifChromaDownsampling into avifRGBToYUVFlag

Refactor avifChromaUpsampling into avifYUVToRGBFlag.
diff --git a/apps/avifdec.c b/apps/avifdec.c
index 055a919..6617187 100644
--- a/apps/avifdec.c
+++ b/apps/avifdec.c
@@ -61,7 +61,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;
-    avifChromaUpsampling chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
+    avifYUVToRGBFlags upsamplingFlags = AVIF_YUV_TO_RGB_DEFAULT;
     avifBool ignoreICC = AVIF_FALSE;
     avifBool rawColor = AVIF_FALSE;
     avifBool allowProgressive = AVIF_FALSE;
@@ -134,15 +134,15 @@
         } else if (!strcmp(arg, "-u") || !strcmp(arg, "--upsampling")) {
             NEXTARG();
             if (!strcmp(arg, "automatic")) {
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
+                upsamplingFlags = AVIF_YUV_TO_RGB_DEFAULT;
             } else if (!strcmp(arg, "fastest")) {
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_FASTEST;
+                upsamplingFlags = AVIF_CHROMA_UPSAMPLING_NEAREST;
             } else if (!strcmp(arg, "best")) {
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_BEST_QUALITY;
+                upsamplingFlags = AVIF_YUV_TO_RGB_AVOID_LIBYUV | AVIF_CHROMA_UPSAMPLING_BILINEAR;
             } else if (!strcmp(arg, "nearest")) {
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_NEAREST;
+                upsamplingFlags = AVIF_CHROMA_UPSAMPLING_NEAREST;
             } else if (!strcmp(arg, "bilinear")) {
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_BILINEAR;
+                upsamplingFlags = AVIF_CHROMA_UPSAMPLING_BILINEAR;
             } else {
                 fprintf(stderr, "ERROR: invalid upsampling: %s\n", arg);
                 return 1;
@@ -324,11 +324,11 @@
         if (rawColor) {
             decoder->image->alphaPremultiplied = AVIF_TRUE;
         }
-        if (!avifJPEGWrite(outputFilename, decoder->image, jpegQuality, chromaUpsampling)) {
+        if (!avifJPEGWrite(outputFilename, decoder->image, jpegQuality, upsamplingFlags)) {
             returnCode = 1;
         }
     } else if (outputFormat == AVIF_APP_FILE_FORMAT_PNG) {
-        if (!avifPNGWrite(outputFilename, decoder->image, requestedDepth, chromaUpsampling, pngCompressionLevel)) {
+        if (!avifPNGWrite(outputFilename, decoder->image, requestedDepth, upsamplingFlags, pngCompressionLevel)) {
             returnCode = 1;
         }
     } else {
diff --git a/apps/shared/avifjpeg.c b/apps/shared/avifjpeg.c
index 0934c4f..3ff9a1e 100644
--- a/apps/shared/avifjpeg.c
+++ b/apps/shared/avifjpeg.c
@@ -314,7 +314,7 @@
             memcpy(pixelRow, buffer[0], rgb.rowBytes);
             ++row;
         }
-        if (avifImageRGBToYUV(avif, &rgb) != AVIF_RESULT_OK) {
+        if (avifImageRGBToYUV(avif, &rgb, AVIF_RGB_TO_YUV_DEFAULT) != AVIF_RESULT_OK) {
             fprintf(stderr, "Conversion to YUV failed: %s\n", inputFilename);
             goto cleanup;
         }
@@ -332,7 +332,7 @@
     return ret;
 }
 
-avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifChromaUpsampling chromaUpsampling)
+avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifYUVToRGBFlags conversionFlags)
 {
     avifBool ret = AVIF_FALSE;
     FILE * f = NULL;
@@ -346,10 +346,9 @@
     avifRGBImage rgb;
     avifRGBImageSetDefaults(&rgb, avif);
     rgb.format = AVIF_RGB_FORMAT_RGB;
-    rgb.chromaUpsampling = chromaUpsampling;
     rgb.depth = 8;
     avifRGBImageAllocatePixels(&rgb);
-    if (avifImageYUVToRGB(avif, &rgb) != AVIF_RESULT_OK) {
+    if (avifImageYUVToRGB(avif, &rgb, conversionFlags) != 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 305375d..a928fa4 100644
--- a/apps/shared/avifjpeg.h
+++ b/apps/shared/avifjpeg.h
@@ -11,7 +11,7 @@
 #endif
 
 avifBool avifJPEGRead(const char * inputFilename, avifImage * avif, avifPixelFormat requestedFormat, uint32_t requestedDepth);
-avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifChromaUpsampling chromaUpsampling);
+avifBool avifJPEGWrite(const char * outputFilename, const avifImage * avif, int jpegQuality, avifYUVToRGBFlags conversionFlags);
 
 #ifdef __cplusplus
 } // extern "C"
diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c
index effaa6b..7f8ce45 100644
--- a/apps/shared/avifpng.c
+++ b/apps/shared/avifpng.c
@@ -153,7 +153,7 @@
         rowPointers[y] = &rgb.pixels[y * rgb.rowBytes];
     }
     png_read_image(png, rowPointers);
-    if (avifImageRGBToYUV(avif, &rgb) != AVIF_RESULT_OK) {
+    if (avifImageRGBToYUV(avif, &rgb, AVIF_RGB_TO_YUV_DEFAULT) != AVIF_RESULT_OK) {
         fprintf(stderr, "Conversion to YUV failed: %s\n", inputFilename);
         goto cleanup;
     }
@@ -173,7 +173,7 @@
     return readResult;
 }
 
-avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling chromaUpsampling, int compressionLevel)
+avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifYUVToRGBFlags conversionFlags, int compressionLevel)
 {
     volatile avifBool writeResult = AVIF_FALSE;
     png_structp png = NULL;
@@ -200,9 +200,8 @@
         colorType = PNG_COLOR_TYPE_RGB;
         rgb.format = AVIF_RGB_FORMAT_RGB;
     }
-    rgb.chromaUpsampling = chromaUpsampling;
     avifRGBImageAllocatePixels(&rgb);
-    if (avifImageYUVToRGB(avif, &rgb) != AVIF_RESULT_OK) {
+    if (avifImageYUVToRGB(avif, &rgb, conversionFlags) != 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 5e130ce..20ffd93 100644
--- a/apps/shared/avifpng.h
+++ b/apps/shared/avifpng.h
@@ -15,7 +15,7 @@
 avifBool avifPNGWrite(const char * outputFilename,
                       const avifImage * avif,
                       uint32_t requestedDepth,
-                      avifChromaUpsampling chromaUpsampling,
+                      avifYUVToRGBFlags conversionFlags,
                       int compressionLevel);
 
 #ifdef __cplusplus
diff --git a/contrib/gdk-pixbuf/loader.c b/contrib/gdk-pixbuf/loader.c
index 32c6eee..1ce68e6 100644
--- a/contrib/gdk-pixbuf/loader.c
+++ b/contrib/gdk-pixbuf/loader.c
@@ -448,7 +448,7 @@
         rgb.format = AVIF_RGB_FORMAT_RGB;
     }
 
-    res = avifImageRGBToYUV(avif, &rgb);
+    res = avifImageRGBToYUV(avif, &rgb, AVIF_CONVERSION_AUTO);
     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 12b0a7e..1f8d1fa 100644
--- a/examples/avif_example_decode_file.c
+++ b/examples/avif_example_decode_file.c
@@ -53,13 +53,14 @@
         // * this frame's sequence timing
 
         avifRGBImageSetDefaults(&rgb, decoder->image);
-        // Override YUV(A)->RGB(A) defaults here: depth, format, chromaUpsampling, ignoreAlpha, alphaPremultiplied, libYUVUsage, etc
+        // Override YUV(A)->RGB(A) defaults here: depth, format, 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);
 
-        if (avifImageYUVToRGB(decoder->image, &rgb) != AVIF_RESULT_OK) {
+        // 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) {
             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 a50d248..d5a9aa9 100644
--- a/examples/avif_example_decode_memory.c
+++ b/examples/avif_example_decode_memory.c
@@ -75,13 +75,14 @@
         // * this frame's sequence timing
 
         avifRGBImageSetDefaults(&rgb, decoder->image);
-        // Override YUV(A)->RGB(A) defaults here: depth, format, chromaUpsampling, ignoreAlpha, alphaPremultiplied, libYUVUsage, etc
+        // Override YUV(A)->RGB(A) defaults here: depth, format, 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);
 
-        if (avifImageYUVToRGB(decoder->image, &rgb) != AVIF_RESULT_OK) {
+        // 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) {
             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 36c4ca9..b95cf0a 100644
--- a/examples/avif_example_encode.c
+++ b/examples/avif_example_encode.c
@@ -57,7 +57,7 @@
         printf("Encoding from converted RGBA\n");
 
         avifRGBImageSetDefaults(&rgb, image);
-        // Override RGB(A)->YUV(A) defaults here: depth, format, chromaDownsampling, ignoreAlpha, alphaPremultiplied, libYUVUsage, etc
+        // Override RGB(A)->YUV(A) defaults here: depth, format, 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,7 +66,8 @@
         // Fill your RGB(A) data here
         memset(rgb.pixels, 255, rgb.rowBytes * image->height);
 
-        avifResult convertResult = avifImageRGBToYUV(image, &rgb);
+        // 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);
         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 08a1d50..71cf54f 100644
--- a/include/avif/avif.h
+++ b/include/avif/avif.h
@@ -528,12 +528,13 @@
 // 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, libavif will use libyuv for conversion from
-// YUV to RGB if the following requirements are met:
-//
+// 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.chromaUpsampling: AVIF_CHROMA_UPSAMPLING_AUTOMATIC, AVIF_CHROMA_UPSAMPLING_FASTEST
 // * 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):
@@ -542,12 +543,9 @@
 //   * x/x/[1|2|5|6|9]/Limited
 //   * [1|2|5|6|9]/x/12/Limited
 
-// If libavif is built with libyuv fast paths enabled, libavif will use libyuv for conversion from
-// RGB to YUV if the following requirements are met:
-//
+// Conversion from RGB to YUV:
 // * YUV depth: 8
 // * RGB depth: 8
-// * rgb.chromaDownsampling: AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC, AVIF_CHROMA_DOWNSAMPLING_FASTEST
 // * One of the following combinations (avifRGBFormat to avifPixelFormat/MC/Range):
 //   *  BGRA            to  YUV400        /  x  /[Full|Limited]
 //   *  BGRA            to [YUV420|YUV422]/[5|6]/[Full|Limited]
@@ -582,33 +580,12 @@
 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 (prefers libyuv, else uses BEST_QUALITY)
-    AVIF_CHROMA_UPSAMPLING_FASTEST = 1,      // Chooses speed over quality (prefers libyuv, else uses NEAREST)
-    AVIF_CHROMA_UPSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling, given settings (avoids libyuv)
-    AVIF_CHROMA_UPSAMPLING_NEAREST = 3,      // Uses nearest-neighbor filter (built-in)
-    AVIF_CHROMA_UPSAMPLING_BILINEAR = 4      // Uses bilinear filter (built-in)
-} avifChromaUpsampling;
-
-typedef enum avifChromaDownsampling
-{
-    AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC = 0,    // Chooses best trade off of speed/quality (prefers libyuv, else uses BEST_QUALITY)
-    AVIF_CHROMA_DOWNSAMPLING_FASTEST = 1,      // Chooses speed over quality (prefers libyuv, else uses AVERAGE)
-    AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling (avoids libyuv, uses AVERAGE)
-    AVIF_CHROMA_DOWNSAMPLING_AVERAGE = 3       // Uses floating point RGB-to-YUV conversion then averaging (built-in)
-} 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
-    avifChromaUpsampling chromaUpsampling; // Defaults to AVIF_CHROMA_UPSAMPLING_AUTOMATIC: How to upsample non-4:4:4 UV (ignored for 444) when converting to RGB.
-                                           // Unused when converting to YUV. avifRGBImageSetDefaults() prefers quality over speed.
-    avifChromaDownsampling chromaDownsampling; // How to convert (and downsample to non-4:4:4 UV) when converting to YUV.
-                                               // Unused when converting to RGB. Defaults to AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC.
+    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.
     avifBool alphaPremultiplied; // indicates if RGB value is pre-multiplied by alpha. Default: false
@@ -628,9 +605,42 @@
 AVIF_API void avifRGBImageAllocatePixels(avifRGBImage * rgb);
 AVIF_API void avifRGBImageFreePixels(avifRGBImage * rgb);
 
-// The main conversion functions
-AVIF_API avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb);
-AVIF_API avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb);
+// Conversion options.
+typedef enum avifRGBToYUVFlag
+{
+    AVIF_RGB_TO_YUV_DEFAULT = 0, // Uses the first available upsampling 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)
+} avifRGBToYUVFlag;
+typedef uint32_t avifRGBToYUVFlags;
+typedef enum avifYUVToRGBFlag
+{
+    AVIF_YUV_TO_RGB_DEFAULT = 0, // Uses the first available downsampling 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);
 
 // 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 da650ae..c3a2749 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);
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags);
 
 // Returns:
 // * AVIF_RESULT_OK              - Converted successfully with libsharpyuv
diff --git a/src/avif.c b/src/avif.c
index fd4a9ed..5381b44 100644
--- a/src/avif.c
+++ b/src/avif.c
@@ -457,8 +457,6 @@
     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->ignoreAlpha = AVIF_FALSE;
     rgb->pixels = NULL;
     rgb->rowBytes = 0;
diff --git a/src/reformat.c b/src/reformat.c
index 7e5246a..9559fad 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -191,7 +191,7 @@
     return AVIF_CLAMP(unorm, 0, state->yuvMaxChannel);
 }
 
-avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb)
+avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb, avifRGBToYUVFlags flags)
 {
     if (!rgb->pixels || rgb->format == AVIF_RGB_FORMAT_RGB_565) {
         return AVIF_RESULT_REFORMAT_FAILED;
@@ -222,7 +222,7 @@
     }
 
     avifBool convertedWithLibYUV = AVIF_FALSE;
-    if (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP) {
+    if (!(flags & AVIF_RGB_TO_YUV_AVOID_LIBYUV) && (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP)) {
         avifResult libyuvResult = avifImageRGBToYUVLibYUV(image, rgb);
         if (libyuvResult == AVIF_RESULT_OK) {
             convertedWithLibYUV = AVIF_TRUE;
@@ -465,10 +465,7 @@
 }
 
 // Note: This function handles alpha (un)multiply.
-static avifResult avifImageYUVAnyToRGBAnySlow(const avifImage * image,
-                                              avifRGBImage * rgb,
-                                              avifReformatState * state,
-                                              const avifChromaUpsampling chromaUpsampling)
+static avifResult avifImageYUVAnyToRGBAnySlow(const avifImage * image, avifRGBImage * rgb, avifReformatState * state, avifYUVToRGBFlags flags)
 {
     // Aliases for some state
     const float kr = state->kr;
@@ -494,9 +491,6 @@
     const uint16_t yuvMaxChannel = (uint16_t)state->yuvMaxChannel;
     const float rgbMaxChannelF = state->rgbMaxChannelF;
 
-    // These are the only supported built-ins
-    assert((chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BILINEAR) || (chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST));
-
     // If toRGBAlphaMode is active (not no-op), assert that the alpha plane is present. The end of
     // the avifPrepareReformatState() function should ensure this, but this assert makes it clear
     // to clang's analyzer.
@@ -639,18 +633,16 @@
                         }
                     }
 
-                    if (chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BILINEAR) {
+                    if (flags & AVIF_CHROMA_UPSAMPLING_NEAREST) {
+                        // Nearest neighbor; ignore all UVs but the closest one
+                        Cb = unormFloatTableUV[unormU[0][0]];
+                        Cr = unormFloatTableUV[unormV[0][0]];
+                    } else {
                         // Bilinear filtering with weights
                         Cb = (unormFloatTableUV[unormU[0][0]] * (9.0f / 16.0f)) + (unormFloatTableUV[unormU[1][0]] * (3.0f / 16.0f)) +
                              (unormFloatTableUV[unormU[0][1]] * (3.0f / 16.0f)) + (unormFloatTableUV[unormU[1][1]] * (1.0f / 16.0f));
                         Cr = (unormFloatTableUV[unormV[0][0]] * (9.0f / 16.0f)) + (unormFloatTableUV[unormV[1][0]] * (3.0f / 16.0f)) +
                              (unormFloatTableUV[unormV[0][1]] * (3.0f / 16.0f)) + (unormFloatTableUV[unormV[1][1]] * (1.0f / 16.0f));
-                    } else {
-                        assert(chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST);
-
-                        // Nearest neighbor; ignore all UVs but the closest one
-                        Cb = unormFloatTableUV[unormU[0][0]];
-                        Cr = unormFloatTableUV[unormV[0][0]];
                     }
                 }
             }
@@ -1157,9 +1149,12 @@
     return AVIF_RESULT_OK;
 }
 
-static avifResult avifRGBImageToF16(avifRGBImage * rgb)
+static avifResult avifRGBImageToF16(avifRGBImage * rgb, avifYUVToRGBFlags flags)
 {
-    avifResult libyuvResult = avifRGBImageToF16LibYUV(rgb);
+    avifResult libyuvResult = AVIF_RESULT_NOT_IMPLEMENTED;
+    if (!(flags & AVIF_YUV_TO_RGB_AVOID_LIBYUV)) {
+        libyuvResult = avifRGBImageToF16LibYUV(rgb);
+    }
     if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) {
         return libyuvResult;
     }
@@ -1186,7 +1181,7 @@
     return AVIF_RESULT_OK;
 }
 
-avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb)
+avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
 {
     if (!image->yuvPlanes[AVIF_CHAN_Y]) {
         return AVIF_RESULT_REFORMAT_FAILED;
@@ -1197,10 +1192,16 @@
         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 (alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP || avifRGBFormatHasAlpha(rgb->format)) {
-        avifResult libyuvResult = avifImageYUVToRGBLibYUV(image, rgb);
+    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 (libyuvResult == AVIF_RESULT_OK) {
             convertedWithLibYUV = AVIF_TRUE;
         } else {
@@ -1245,25 +1246,10 @@
 
         avifResult convertResult = AVIF_RESULT_NOT_IMPLEMENTED;
 
-        avifChromaUpsampling chromaUpsampling;
-        switch (rgb->chromaUpsampling) {
-            case AVIF_CHROMA_UPSAMPLING_AUTOMATIC:
-            case AVIF_CHROMA_UPSAMPLING_BEST_QUALITY:
-            case AVIF_CHROMA_UPSAMPLING_BILINEAR:
-            default:
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_BILINEAR;
-                break;
-
-            case AVIF_CHROMA_UPSAMPLING_FASTEST:
-            case AVIF_CHROMA_UPSAMPLING_NEAREST:
-                chromaUpsampling = AVIF_CHROMA_UPSAMPLING_NEAREST;
-                break;
-        }
-
         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) || (chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST)) &&
+        if ((!hasColor || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) || (flags & 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 +1311,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, chromaUpsampling);
+            convertResult = avifImageYUVAnyToRGBAnySlow(image, rgb, &state, flags);
 
             // The slow path also handles alpha (un)multiply, so forget the operation here.
             alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP;
@@ -1351,7 +1337,7 @@
 
     // Convert pixels to half floats (F16), if necessary.
     if (rgb->isFloat) {
-        return avifRGBImageToF16(rgb);
+        return avifRGBImageToF16(rgb, flags);
     }
 
     return AVIF_RESULT_OK;
diff --git a/src/reformat_libyuv.c b/src/reformat_libyuv.c
index 3a37ba0..267fb02 100644
--- a/src/reformat_libyuv.c
+++ b/src/reformat_libyuv.c
@@ -12,10 +12,11 @@
     (void)rgb;
     return AVIF_RESULT_NOT_IMPLEMENTED;
 }
-avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb)
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
 {
     (void)image;
     (void)rgb;
+    (void)flags;
     return AVIF_RESULT_NOT_IMPLEMENTED;
 }
 avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb)
@@ -61,15 +62,6 @@
 
 avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb)
 {
-    if ((rgb->chromaDownsampling != AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC) && (rgb->chromaDownsampling != AVIF_CHROMA_DOWNSAMPLING_FASTEST)) {
-        // libyuv uses integer/fixed-point averaging and RGB-to-YUV conversion.
-        // We do not ensure a specific ordering of these two steps and libyuv
-        // may perform one or the other depending on the implementation or
-        // platform. Also libyuv trades a bit of accuracy for speed, so if the
-        // end user requested best quality, avoid using libyuv as well.
-        return AVIF_RESULT_NOT_IMPLEMENTED;
-    }
-
     if ((image->depth == 8) && (rgb->depth == 8)) {
         return avifImageRGBToYUVLibYUV8bpc(image, rgb);
     }
@@ -235,23 +227,18 @@
 static avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
                                               avifRGBImage * rgb,
                                               const struct YuvConstants * matrixYUV,
-                                              const struct YuvConstants * matrixYVU);
+                                              const struct YuvConstants * matrixYVU,
+                                              avifYUVToRGBFlags flags);
 static avifResult avifImageYUVToRGBLibYUVHighBitDepth(const avifImage * image,
                                                       avifRGBImage * rgb,
                                                       const struct YuvConstants * matrixYUV,
-                                                      const struct YuvConstants * matrixYVU);
+                                                      const struct YuvConstants * matrixYVU,
+                                                      avifYUVToRGBFlags flags);
 
-avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb)
+avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifYUVToRGBFlags flags)
 {
     // See if the current settings can be accomplished with libyuv, and use it (if possible).
 
-    if ((rgb->chromaUpsampling != AVIF_CHROMA_UPSAMPLING_AUTOMATIC) && (rgb->chromaUpsampling != AVIF_CHROMA_UPSAMPLING_FASTEST)) {
-        // We do not ensure a specific upsampling filter is used when calling libyuv, so if the end
-        // user chose a specific one, avoid using libyuv. Also libyuv trades a bit of accuracy for
-        // speed, so if the end user requested best quality, avoid using libyuv as well.
-        return AVIF_RESULT_NOT_IMPLEMENTED;
-    }
-
     // Find the correct libyuv YuvConstants, based on range and CP/MC
     const struct YuvConstants * matrixYUV = NULL;
     const struct YuvConstants * matrixYVU = NULL;
@@ -384,11 +371,11 @@
     }
 
     if ((image->depth == 8) && (rgb->depth == 8)) {
-        return avifImageYUVToRGBLibYUV8bpc(image, rgb, matrixYUV, matrixYVU);
+        return avifImageYUVToRGBLibYUV8bpc(image, rgb, matrixYUV, matrixYVU, flags);
     }
 
     if (((image->depth == 10) || (image->depth == 12)) && (rgb->depth == 8)) {
-        return avifImageYUVToRGBLibYUVHighBitDepth(image, rgb, matrixYUV, matrixYVU);
+        return avifImageYUVToRGBLibYUVHighBitDepth(image, rgb, matrixYUV, matrixYVU, flags);
     }
 
     // This function didn't do anything; use the built-in YUV conversion
@@ -432,7 +419,8 @@
 avifResult avifImageYUVToRGBLibYUV8bpc(const avifImage * image,
                                        avifRGBImage * rgb,
                                        const struct YuvConstants * matrixYUV,
-                                       const struct YuvConstants * matrixYVU)
+                                       const struct YuvConstants * matrixYVU,
+                                       avifYUVToRGBFlags flags)
 {
     // See if the current settings can be accomplished with libyuv, and use it (if possible).
 
@@ -507,8 +495,7 @@
         };
         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;
+            const enum FilterMode filter = (flags & AVIF_CHROMA_UPSAMPLING_NEAREST) ? kFilterNone : kFilterBilinear;
             if (yuvToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
                                      image->yuvRowBytes[AVIF_CHAN_Y],
                                      image->yuvPlanes[uPlaneIndex],
@@ -526,7 +513,11 @@
             return AVIF_RESULT_OK;
         }
 
-        // Lookup table for YUV To RGB Matrix (without filter).
+        // Only proceed with the nearest-neighbor filter if explicitly specified or left as default.
+        if (flags & AVIF_CHROMA_UPSAMPLING_BILINEAR) {
+            return AVIF_RESULT_NOT_IMPLEMENTED;
+        }
+        // Lookup table for YUV To RGB Matrix (nearest-neighbor 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] = {
@@ -564,7 +555,8 @@
 avifResult avifImageYUVToRGBLibYUVHighBitDepth(const avifImage * image,
                                                avifRGBImage * rgb,
                                                const struct YuvConstants * matrixYUV,
-                                               const struct YuvConstants * matrixYVU)
+                                               const struct YuvConstants * matrixYVU,
+                                               avifYUVToRGBFlags flags)
 {
     // See if the current settings can be accomplished with libyuv, and use it (if possible).
 
@@ -627,8 +619,7 @@
     };
     YUVToRGBMatrixFilter yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[depthIndex][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;
+        const enum FilterMode filter = (flags & 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],
@@ -646,7 +637,11 @@
         return AVIF_RESULT_OK;
     }
 
-    // Lookup table for YUV To RGB Matrix (without filter).
+    // Only proceed with the nearest-neighbor filter if explicitly specified or left as default.
+    if (flags & AVIF_CHROMA_UPSAMPLING_BILINEAR) {
+        return AVIF_RESULT_NOT_IMPLEMENTED;
+    }
+    // Lookup table for YUV To RGB Matrix (nearest-neighbor filter).
     typedef int (
         *YUVToRGBMatrix)(const uint16_t *, int, const uint16_t *, int, const uint16_t *, int, uint8_t *, int, const struct YuvConstants *, int, int);
     // First index: 0: 10bpc, 1: 12bpc
diff --git a/tests/avifyuv.c b/tests/avifyuv.c
index b7e2df1..a98dad7 100644
--- a/tests/avifyuv.c
+++ b/tests/avifyuv.c
@@ -203,8 +203,8 @@
                                 }
                             }
 
-                            avifImageRGBToYUV(image, &srcRGB);
-                            avifImageYUVToRGB(image, &dstRGB);
+                            avifImageRGBToYUV(image, &srcRGB, AVIF_RGB_TO_YUV_DEFAULT);
+                            avifImageYUVToRGB(image, &dstRGB, AVIF_YUV_TO_RGB_DEFAULT);
 
                             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);
+                        avifImageRGBToYUV(image, &srcRGB, AVIF_RGB_TO_YUV_DEFAULT);
 
                         avifRGBImage intermediateRGB;
                         avifRGBImageSetDefaults(&intermediateRGB, image);
                         intermediateRGB.depth = rgbDepth;
                         intermediateRGB.format = rgbFormat;
                         avifRGBImageAllocatePixels(&intermediateRGB);
-                        avifImageYUVToRGB(image, &intermediateRGB);
+                        avifImageYUVToRGB(image, &intermediateRGB, AVIF_YUV_TO_RGB_DEFAULT);
 
                         avifImageFreePlanes(image, AVIF_PLANES_ALL);
-                        avifImageRGBToYUV(image, &intermediateRGB);
+                        avifImageRGBToYUV(image, &intermediateRGB, AVIF_RGB_TO_YUV_DEFAULT);
 
                         avifRGBImage dstRGB;
                         avifRGBImageSetDefaults(&dstRGB, image);
                         dstRGB.depth = yuvDepth;
                         avifRGBImageAllocatePixels(&dstRGB);
-                        avifImageYUVToRGB(image, &dstRGB);
+                        avifImageYUVToRGB(image, &dstRGB, AVIF_YUV_TO_RGB_DEFAULT);
 
                         avifBool moveOn = AVIF_FALSE;
                         for (uint32_t j = 0; j < originalHeight; ++j) {
diff --git a/tests/gtest/avifrgbtoyuvtest.cc b/tests/gtest/avifrgbtoyuvtest.cc
index 27f5607..b7bb0d2 100644
--- a/tests/gtest/avifrgbtoyuvtest.cc
+++ b/tests/gtest/avifrgbtoyuvtest.cc
@@ -182,12 +182,10 @@
         ModifyImageChannel(&src_rgb, offsets.b, kBlueNoise);
       }
 
-      // Change these to BEST_QUALITY to force built-in over libyuv conversion.
-      src_rgb.chromaDownsampling = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC;
-      dst_rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
-
-      ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
-      ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
+      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);
       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,13 +203,12 @@
             ModifyImageChannel(&src_rgb, offsets.b, kBlueNoise);
           }
 
-          // Change these to BEST_QUALITY to force built-in over libyuv
-          // conversion.
-          src_rgb.chromaDownsampling = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC;
-          dst_rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
-
-          ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
-          ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
+          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);
           GetDiffSumAndSqDiffSum(src_rgb, dst_rgb, &diff_sum, &abs_diff_sum,
                                  &sq_diff_sum, &max_abs_diff);
           num_diffs +=
@@ -276,9 +273,6 @@
       yuv->yuvRange = yuv_range;
       testutil::AvifRgbImage src_rgb(yuv.get(), rgb_depth, rgb_format);
       testutil::AvifRgbImage dst_rgb(yuv.get(), rgb_depth, rgb_format);
-      // Change these to BEST_QUALITY to force built-in over libyuv conversion.
-      src_rgb.chromaDownsampling = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC;
-      dst_rgb.chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
       const testutil::RgbChannelOffsets offsets =
           testutil::GetRgbChannelOffsets(rgb_format);
 
@@ -298,8 +292,10 @@
         testutil::FillImageChannel(&src_rgb, offsets.a, rgb_max);
       }
 
-      ASSERT_EQ(avifImageRGBToYUV(yuv.get(), &src_rgb), AVIF_RESULT_OK);
-      ASSERT_EQ(avifImageYUVToRGB(yuv.get(), &dst_rgb), AVIF_RESULT_OK);
+      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);
       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;
diff --git a/tests/oss-fuzz/avif_decode_fuzzer.cc b/tests/oss-fuzz/avif_decode_fuzzer.cc
index 173040f..80e5d97 100644
--- a/tests/oss-fuzz/avif_decode_fuzzer.cc
+++ b/tests/oss-fuzz/avif_decode_fuzzer.cc
@@ -8,8 +8,8 @@
     static avifRGBFormat rgbFormats[] = { AVIF_RGB_FORMAT_RGB, AVIF_RGB_FORMAT_RGBA };
     static size_t rgbFormatsCount = sizeof(rgbFormats) / sizeof(rgbFormats[0]);
 
-    static avifChromaUpsampling upsamplings[] = { AVIF_CHROMA_UPSAMPLING_BILINEAR, AVIF_CHROMA_UPSAMPLING_NEAREST };
-    static size_t upsamplingsCount = sizeof(upsamplings) / sizeof(upsamplings[0]);
+    static avifYUVToRGBFlags upsamplingFlags[] = { AVIF_CHROMA_UPSAMPLING_BILINEAR, AVIF_CHROMA_UPSAMPLING_NEAREST };
+    static size_t upsamplingFlagsCount = sizeof(upsamplingFlags) / sizeof(upsamplingFlags[0]);
 
     static uint32_t rgbDepths[] = { 8, 10 };
     static size_t rgbDepthsCount = sizeof(rgbDepths) / sizeof(rgbDepths[0]);
@@ -43,24 +43,24 @@
                 avifRGBImageSetDefaults(&rgb, decoder->image);
 
                 for (size_t rgbFormatsIndex = 0; rgbFormatsIndex < rgbFormatsCount; ++rgbFormatsIndex) {
-                    for (size_t upsamplingsIndex = 0; upsamplingsIndex < upsamplingsCount; ++upsamplingsIndex) {
+                    for (size_t upsamplingFlagsIndex = 0; upsamplingFlagsIndex < upsamplingFlagsCount; ++upsamplingFlagsIndex) {
                         for (size_t rgbDepthsIndex = 0; rgbDepthsIndex < rgbDepthsCount; ++rgbDepthsIndex) {
                             // Convert to RGB
                             rgb.format = rgbFormats[rgbFormatsIndex];
                             rgb.depth = rgbDepths[rgbDepthsIndex];
-                            rgb.chromaUpsampling = upsamplings[upsamplingsIndex];
                             avifRGBImageAllocatePixels(&rgb);
-                            avifResult rgbResult = avifImageYUVToRGB(decoder->image, &rgb);
-                            // Since avifImageRGBToYUV() ignores rgb.chromaUpsampling, we only need
-                            // to test avifImageRGBToYUV() with a single upsamplingsIndex.
-                            if ((rgbResult == AVIF_RESULT_OK) && (upsamplingsIndex == 0)) {
+                            avifResult rgbResult =
+                                avifImageYUVToRGB(decoder->image, &rgb, AVIF_YUV_TO_RGB_AVOID_LIBYUV | upsamplingFlags[upsamplingFlagsIndex]);
+                            // Since avifImageRGBToYUV() ignores upsamplingFlags, we only need
+                            // to test avifImageRGBToYUV() with a single upsamplingFlagsIndex.
+                            if ((rgbResult == AVIF_RESULT_OK) && (upsamplingFlagsIndex == 0)) {
                                 for (size_t yuvDepthsIndex = 0; yuvDepthsIndex < yuvDepthsCount; ++yuvDepthsIndex) {
                                     // ... and back to YUV
                                     avifImage * tempImage = avifImageCreate(decoder->image->width,
                                                                             decoder->image->height,
                                                                             yuvDepths[yuvDepthsIndex],
                                                                             decoder->image->yuvFormat);
-                                    avifResult yuvResult = avifImageRGBToYUV(tempImage, &rgb);
+                                    avifResult yuvResult = avifImageRGBToYUV(tempImage, &rgb, AVIF_RGB_TO_YUV_AVOID_LIBYUV);
                                     if (yuvResult != AVIF_RESULT_OK) {
                                     }
                                     avifImageDestroy(tempImage);