Utilize bilinear chroma upsample in libyuv when possible (#873)

* Utilize bilinear chroma upsample in libyuv when possible

* Add more info to comment about -Wnewline-eof

* Add a comment about -Wnewline-eof

* Edit comment

Co-authored-by: Wan-Teh Chang <wtc@google.com>
diff --git a/ext/libyuv.cmd b/ext/libyuv.cmd
index 67cc5e3..010e925 100755
--- a/ext/libyuv.cmd
+++ b/ext/libyuv.cmd
@@ -10,7 +10,7 @@
 git clone --single-branch https://chromium.googlesource.com/libyuv/libyuv
 
 cd libyuv
-git checkout 2f0cbb9
+git checkout 3aebf69
 
 mkdir build
 cd build
diff --git a/src/reformat_libyuv.c b/src/reformat_libyuv.c
index 7984365..0bf5501 100644
--- a/src/reformat_libyuv.c
+++ b/src/reformat_libyuv.c
@@ -37,6 +37,11 @@
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype"
+// The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored
+// in version 1813:
+// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182
+// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834
+#pragma clang diagnostic ignored "-Wnewline-eof" // "no newline at end of file"
 #endif
 #include <libyuv.h>
 #if defined(__clang__)
@@ -52,7 +57,9 @@
     }
 
     if ((rgb->chromaUpsampling != AVIF_CHROMA_UPSAMPLING_AUTOMATIC) && (rgb->chromaUpsampling != AVIF_CHROMA_UPSAMPLING_FASTEST)) {
-        // libyuv uses its own upsampling filter. If the enduser chose a specific one, avoid using libyuv.
+        // 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;
     }
 
@@ -187,6 +194,14 @@
         return AVIF_RESULT_NOT_IMPLEMENTED;
     }
 
+#if LIBYUV_VERSION >= 1813
+    enum FilterMode filter = kFilterBilinear;
+    if (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) {
+        // 'None' (Nearest neighbor) filter is faster than bilinear.
+        filter = kFilterNone;
+    }
+#endif
+
     // This following section might be a bit complicated to audit without a bit of explanation:
     //
     // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address,
@@ -222,6 +237,22 @@
             }
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
+#if LIBYUV_VERSION >= 1813
+            if (I422ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
+                                       image->yuvRowBytes[AVIF_CHAN_Y],
+                                       image->yuvPlanes[AVIF_CHAN_U],
+                                       image->yuvRowBytes[AVIF_CHAN_U],
+                                       image->yuvPlanes[AVIF_CHAN_V],
+                                       image->yuvRowBytes[AVIF_CHAN_V],
+                                       rgb->pixels,
+                                       rgb->rowBytes,
+                                       matrixYUV,
+                                       image->width,
+                                       image->height,
+                                       filter) != 0) {
+                return AVIF_RESULT_REFORMAT_FAILED;
+            }
+#else
             if (I422ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
                                  image->yuvRowBytes[AVIF_CHAN_Y],
                                  image->yuvPlanes[AVIF_CHAN_U],
@@ -235,8 +266,25 @@
                                  image->height) != 0) {
                 return AVIF_RESULT_REFORMAT_FAILED;
             }
+#endif
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
+#if LIBYUV_VERSION >= 1813
+            if (I420ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
+                                       image->yuvRowBytes[AVIF_CHAN_Y],
+                                       image->yuvPlanes[AVIF_CHAN_U],
+                                       image->yuvRowBytes[AVIF_CHAN_U],
+                                       image->yuvPlanes[AVIF_CHAN_V],
+                                       image->yuvRowBytes[AVIF_CHAN_V],
+                                       rgb->pixels,
+                                       rgb->rowBytes,
+                                       matrixYUV,
+                                       image->width,
+                                       image->height,
+                                       filter) != 0) {
+                return AVIF_RESULT_REFORMAT_FAILED;
+            }
+#else
             if (I420ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
                                  image->yuvRowBytes[AVIF_CHAN_Y],
                                  image->yuvPlanes[AVIF_CHAN_U],
@@ -250,6 +298,7 @@
                                  image->height) != 0) {
                 return AVIF_RESULT_REFORMAT_FAILED;
             }
+#endif
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
             if (I400ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
@@ -282,6 +331,22 @@
             }
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) {
+#if LIBYUV_VERSION >= 1813
+            if (I422ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
+                                       image->yuvRowBytes[AVIF_CHAN_Y],
+                                       image->yuvPlanes[AVIF_CHAN_V],
+                                       image->yuvRowBytes[AVIF_CHAN_V],
+                                       image->yuvPlanes[AVIF_CHAN_U],
+                                       image->yuvRowBytes[AVIF_CHAN_U],
+                                       rgb->pixels,
+                                       rgb->rowBytes,
+                                       matrixYVU,
+                                       image->width,
+                                       image->height,
+                                       filter) != 0) {
+                return AVIF_RESULT_REFORMAT_FAILED;
+            }
+#else
             if (I422ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
                                  image->yuvRowBytes[AVIF_CHAN_Y],
                                  image->yuvPlanes[AVIF_CHAN_V],
@@ -295,8 +360,25 @@
                                  image->height) != 0) {
                 return AVIF_RESULT_REFORMAT_FAILED;
             }
+#endif
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) {
+#if LIBYUV_VERSION >= 1813
+            if (I420ToARGBMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y],
+                                       image->yuvRowBytes[AVIF_CHAN_Y],
+                                       image->yuvPlanes[AVIF_CHAN_V],
+                                       image->yuvRowBytes[AVIF_CHAN_V],
+                                       image->yuvPlanes[AVIF_CHAN_U],
+                                       image->yuvRowBytes[AVIF_CHAN_U],
+                                       rgb->pixels,
+                                       rgb->rowBytes,
+                                       matrixYVU,
+                                       image->width,
+                                       image->height,
+                                       filter) != 0) {
+                return AVIF_RESULT_REFORMAT_FAILED;
+            }
+#else
             if (I420ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
                                  image->yuvRowBytes[AVIF_CHAN_Y],
                                  image->yuvPlanes[AVIF_CHAN_V],
@@ -310,6 +392,7 @@
                                  image->height) != 0) {
                 return AVIF_RESULT_REFORMAT_FAILED;
             }
+#endif
             return AVIF_RESULT_OK;
         } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) {
             if (I400ToARGBMatrix(image->yuvPlanes[AVIF_CHAN_Y],
diff --git a/src/scale.c b/src/scale.c
index 1bbbdd8..23c7de6 100644
--- a/src/scale.c
+++ b/src/scale.c
@@ -22,6 +22,11 @@
 #if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype"
+// The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored
+// in version 1813:
+// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182
+// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834
+#pragma clang diagnostic ignored "-Wnewline-eof" // "no newline at end of file"
 #endif
 #include <libyuv.h>
 #if defined(__clang__)