NV12 Copy, include scale_uv.h

Bug: None
Change-Id: I8148def3f1253913eb62fcc000e5f72704262a17
Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/2569748
Reviewed-by: Frank Barchard <fbarchard@chromium.org>
diff --git a/README.chromium b/README.chromium
index 4ff4ea4..4a7e30b 100644
--- a/README.chromium
+++ b/README.chromium
@@ -1,6 +1,6 @@
 Name: libyuv
 URL: http://code.google.com/p/libyuv/
-Version: 1767
+Version: 1768
 License: BSD
 License File: LICENSE
 
diff --git a/include/libyuv.h b/include/libyuv.h
index aeffd5e..a06e123 100644
--- a/include/libyuv.h
+++ b/include/libyuv.h
@@ -26,6 +26,7 @@
 #include "libyuv/scale.h"
 #include "libyuv/scale_argb.h"
 #include "libyuv/scale_row.h"
+#include "libyuv/scale_uv.h"
 #include "libyuv/version.h"
 #include "libyuv/video_common.h"
 
diff --git a/include/libyuv/planar_functions.h b/include/libyuv/planar_functions.h
index 9e0038f..8d868b9 100644
--- a/include/libyuv/planar_functions.h
+++ b/include/libyuv/planar_functions.h
@@ -200,6 +200,16 @@
              int width,
              int height);
 
+// Copy NV12. Supports inverting.
+int NV12Copy(const uint8_t* src_y, int src_stride_y, const uint8_t* src_uv,
+             int src_stride_uv, uint8_t* dst_y, int dst_stride_y,
+             uint8_t* dst_uv, int dst_stride_uv, int width, int height);
+
+// Copy NV21. Supports inverting.
+int NV21Copy(const uint8_t* src_y, int src_stride_y, const uint8_t* src_vu,
+             int src_stride_vu, uint8_t* dst_y, int dst_stride_y,
+             uint8_t* dst_vu, int dst_stride_vu, int width, int height);
+
 // Convert YUY2 to I422.
 LIBYUV_API
 int YUY2ToI422(const uint8_t* src_yuy2,
diff --git a/include/libyuv/version.h b/include/libyuv/version.h
index 1d08596..efaac73 100644
--- a/include/libyuv/version.h
+++ b/include/libyuv/version.h
@@ -11,6 +11,6 @@
 #ifndef INCLUDE_LIBYUV_VERSION_H_
 #define INCLUDE_LIBYUV_VERSION_H_
 
-#define LIBYUV_VERSION 1767
+#define LIBYUV_VERSION 1768
 
 #endif  // INCLUDE_LIBYUV_VERSION_H_
diff --git a/source/planar_functions.cc b/source/planar_functions.cc
index d5cd7e6..4e8908c 100644
--- a/source/planar_functions.cc
+++ b/source/planar_functions.cc
@@ -349,6 +349,39 @@
   return 0;
 }
 
+// Copy NV12. Supports inverting.
+int NV12Copy(const uint8_t* src_y, int src_stride_y, const uint8_t* src_uv,
+             int src_stride_uv, uint8_t* dst_y, int dst_stride_y,
+             uint8_t* dst_uv, int dst_stride_uv, int width, int height) {
+  if (!src_y || !dst_y || !src_uv || !dst_uv || width <= 0 || height == 0) {
+    return -1;
+  }
+
+  int halfwidth = (width + 1) >> 1;
+  int halfheight = (height + 1) >> 1;
+  // Negative height means invert the image.
+  if (height < 0) {
+    height = -height;
+    halfheight = (height + 1) >> 1;
+    src_y = src_y + (height - 1) * src_stride_y;
+    src_uv = src_uv + (halfheight - 1) * src_stride_uv;
+    src_stride_y = -src_stride_y;
+    src_stride_uv = -src_stride_uv;
+  }
+  CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
+  CopyPlane(src_uv, src_stride_uv, dst_uv, dst_stride_uv, halfwidth * 2,
+            halfheight);
+  return 0;
+}
+
+// Copy NV21. Supports inverting.
+int NV21Copy(const uint8_t* src_y, int src_stride_y, const uint8_t* src_vu,
+             int src_stride_vu, uint8_t* dst_y, int dst_stride_y,
+             uint8_t* dst_vu, int dst_stride_vu, int width, int height) {
+  return NV12Copy(src_y, src_stride_y, src_vu, src_stride_vu, dst_y,
+                  dst_stride_y, dst_vu, dst_stride_vu, width, height);
+}
+
 // Support function for NV12 etc UV channels.
 // Width and height are plane sizes (typically half pixel width).
 LIBYUV_API
diff --git a/unit_test/planar_test.cc b/unit_test/planar_test.cc
index 65aa46e..e05ff15 100644
--- a/unit_test/planar_test.cc
+++ b/unit_test/planar_test.cc
@@ -3562,4 +3562,68 @@
   free_aligned_buffer_page_end(dst_pixels_uv_c);
 }
 
+TEST_F(LibYUVPlanarTest, NV12Copy) {
+  const int halfwidth = (benchmark_width_ + 1) >> 1;
+  const int halfheight = (benchmark_height_ + 1) >> 1;
+  align_buffer_page_end(src_y, benchmark_width_ * benchmark_height_);
+  align_buffer_page_end(src_uv, halfwidth * 2 * halfheight);
+  align_buffer_page_end(dst_y, benchmark_width_ * benchmark_height_);
+  align_buffer_page_end(dst_uv, halfwidth * 2 * halfheight);
+
+  MemRandomize(src_y, benchmark_width_ * benchmark_height_);
+  MemRandomize(src_uv, halfwidth * 2 * halfheight);
+  MemRandomize(dst_y, benchmark_width_ * benchmark_height_);
+  MemRandomize(dst_uv, halfwidth * 2 * halfheight);
+
+  for (int i = 0; i < benchmark_iterations_; ++i) {
+    NV12Copy(src_y, benchmark_width_, src_uv, halfwidth * 2, dst_y,
+             benchmark_width_, dst_uv, halfwidth * 2, benchmark_width_,
+             benchmark_height_);
+  }
+
+  for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) {
+    EXPECT_EQ(src_y[i], dst_y[i]);
+  }
+  for (int i = 0; i < halfwidth * 2 * halfheight; ++i) {
+    EXPECT_EQ(src_uv[i], dst_uv[i]);
+  }
+
+  free_aligned_buffer_page_end(src_y);
+  free_aligned_buffer_page_end(src_uv);
+  free_aligned_buffer_page_end(dst_y);
+  free_aligned_buffer_page_end(dst_uv);
+}
+
+TEST_F(LibYUVPlanarTest, NV21Copy) {
+  const int halfwidth = (benchmark_width_ + 1) >> 1;
+  const int halfheight = (benchmark_height_ + 1) >> 1;
+  align_buffer_page_end(src_y, benchmark_width_ * benchmark_height_);
+  align_buffer_page_end(src_vu, halfwidth * 2 * halfheight);
+  align_buffer_page_end(dst_y, benchmark_width_ * benchmark_height_);
+  align_buffer_page_end(dst_vu, halfwidth * 2 * halfheight);
+
+  MemRandomize(src_y, benchmark_width_ * benchmark_height_);
+  MemRandomize(src_vu, halfwidth * 2 * halfheight);
+  MemRandomize(dst_y, benchmark_width_ * benchmark_height_);
+  MemRandomize(dst_vu, halfwidth * 2 * halfheight);
+
+  for (int i = 0; i < benchmark_iterations_; ++i) {
+    NV21Copy(src_y, benchmark_width_, src_vu, halfwidth * 2, dst_y,
+             benchmark_width_, dst_vu, halfwidth * 2, benchmark_width_,
+             benchmark_height_);
+  }
+
+  for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) {
+    EXPECT_EQ(src_y[i], dst_y[i]);
+  }
+  for (int i = 0; i < halfwidth * 2 * halfheight; ++i) {
+    EXPECT_EQ(src_vu[i], dst_vu[i]);
+  }
+
+  free_aligned_buffer_page_end(src_y);
+  free_aligned_buffer_page_end(src_vu);
+  free_aligned_buffer_page_end(dst_y);
+  free_aligned_buffer_page_end(dst_vu);
+}
+
 }  // namespace libyuv
diff --git a/winarm.mk b/winarm.mk
index c4307a4..b0a344a 100644
--- a/winarm.mk
+++ b/winarm.mk
@@ -31,6 +31,7 @@
 	source/scale_any.o\
 	source/scale_argb.o\
 	source/scale_common.o\
+	source/scale_uv.o\
 	source/video_common.o
 
 .cc.o: