aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergio Garcia Murillo <sergio.garcia.murillo@gmail.com>2022-04-06 20:57:53 +0200
committerlibyuv LUCI CQ <libyuv-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-04-06 23:49:35 +0000
commita77d615e1067fe5bb48444a073f7ee08fd273b8a (patch)
tree6f85baa2f8a3d60a40b31f317e1d10663e2b6f72
parent4589081cea3dc5364dc433f9a20e67706fd3e29c (diff)
downloadlibyuv-a77d615e1067fe5bb48444a073f7ee08fd273b8a.tar.gz
Add tentative I422Rotate.
When doing 90 or 270 degrees rotation we need to do a rotate&scale of the UV planes, as there are no helper optimized functions to do this, we use the Y plane as temporal memory and perform each of the transforms independently: First U plane is rotated, putting the result in the Y plane. After the rotation, the output has double the samples horizontally and half the samples vertically, so it is scaled into the final U plane. Same process is done with the V plane. Last the Y plane that can be just rotated without scaling. It would be great to have an optimized version for this, but maybe this is helpfull for triggering the discussions. Bug: libyuv:926 Change-Id: I188af103c4d0e3f9522021b4bf2b63c9d5de8b93 Reviewed-on: https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3568424 Reviewed-by: Frank Barchard <fbarchard@chromium.org> Commit-Queue: Frank Barchard <fbarchard@chromium.org>
-rw-r--r--include/libyuv/rotate.h18
-rw-r--r--include/libyuv/version.h2
-rw-r--r--source/rotate.cc84
-rw-r--r--unit_test/rotate_test.cc88
4 files changed, 191 insertions, 1 deletions
diff --git a/include/libyuv/rotate.h b/include/libyuv/rotate.h
index 8d01aa87..8c428d97 100644
--- a/include/libyuv/rotate.h
+++ b/include/libyuv/rotate.h
@@ -49,6 +49,24 @@ int I420Rotate(const uint8_t* src_y,
int height,
enum RotationMode mode);
+// Rotate I422 frame.
+LIBYUV_API
+int I422Rotate(const uint8_t* src_y,
+ int src_stride_y,
+ const uint8_t* src_u,
+ int src_stride_u,
+ const uint8_t* src_v,
+ int src_stride_v,
+ uint8_t* dst_y,
+ int dst_stride_y,
+ uint8_t* dst_u,
+ int dst_stride_u,
+ uint8_t* dst_v,
+ int dst_stride_v,
+ int width,
+ int height,
+ enum libyuv::RotationMode mode);
+
// Rotate I444 frame.
LIBYUV_API
int I444Rotate(const uint8_t* src_y,
diff --git a/include/libyuv/version.h b/include/libyuv/version.h
index 426e7423..1747659f 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 1816
+#define LIBYUV_VERSION 1817
#endif // INCLUDE_LIBYUV_VERSION_H_
diff --git a/source/rotate.cc b/source/rotate.cc
index b274e8db..f193bad7 100644
--- a/source/rotate.cc
+++ b/source/rotate.cc
@@ -545,6 +545,90 @@ int I420Rotate(const uint8_t* src_y,
}
LIBYUV_API
+int I422Rotate(const uint8_t* src_y,
+ int src_stride_y,
+ const uint8_t* src_u,
+ int src_stride_u,
+ const uint8_t* src_v,
+ int src_stride_v,
+ uint8_t* dst_y,
+ int dst_stride_y,
+ uint8_t* dst_u,
+ int dst_stride_u,
+ uint8_t* dst_v,
+ int dst_stride_v,
+ int width,
+ int height,
+ enum libyuv::RotationMode mode) {
+ int halfwidth = (width + 1) >> 1;
+ int halfheight = (height + 1) >> 1;
+ if (!src_y || !src_u || !src_v || width <= 0 || height == 0 || !dst_y ||
+ !dst_u || !dst_v) {
+ return -1;
+ }
+ // Negative height means invert the image.
+ if (height < 0) {
+ height = -height;
+ src_y = src_y + (height - 1) * src_stride_y;
+ src_u = src_u + (height - 1) * src_stride_u;
+ src_v = src_v + (height - 1) * src_stride_v;
+ src_stride_y = -src_stride_y;
+ src_stride_u = -src_stride_u;
+ src_stride_v = -src_stride_v;
+ }
+
+ switch (mode) {
+ case libyuv::kRotate0:
+ // copy frame
+ libyuv::CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width,
+ height);
+ libyuv::CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
+ height);
+ libyuv::CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
+ height);
+ return 0;
+ case libyuv::kRotate90:
+ // We need to rotate and rescale, we use plane Y as temporal storage.
+ libyuv::RotatePlane90(src_u, src_stride_u, dst_y, height, halfwidth,
+ height);
+ libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight,
+ halfheight, width, libyuv::kFilterBilinear);
+ libyuv::RotatePlane90(src_v, src_stride_v, dst_y, height, halfwidth,
+ height);
+ libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight,
+ halfheight, width, libyuv::kFilterLinear);
+ libyuv::RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width,
+ height);
+ return 0;
+ case libyuv::kRotate270:
+ // We need to rotate and rescale, we use plane Y as temporal storage.
+ libyuv::RotatePlane270(src_u, src_stride_u, dst_y, height, halfwidth,
+ height);
+ libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_u, halfheight,
+ halfheight, width, libyuv::kFilterBilinear);
+ libyuv::RotatePlane270(src_v, src_stride_v, dst_y, height, halfwidth,
+ height);
+ libyuv::ScalePlane(dst_y, height, height, halfwidth, dst_v, halfheight,
+ halfheight, width, libyuv::kFilterLinear);
+ libyuv::RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width,
+ height);
+
+ return 0;
+ case libyuv::kRotate180:
+ libyuv::RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width,
+ height);
+ libyuv::RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u,
+ halfwidth, height);
+ libyuv::RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v,
+ halfwidth, height);
+ return 0;
+ default:
+ break;
+ }
+ return -1;
+}
+
+LIBYUV_API
int I444Rotate(const uint8_t* src_y,
int src_stride_y,
const uint8_t* src_u,
diff --git a/unit_test/rotate_test.cc b/unit_test/rotate_test.cc
index c140960e..d3887414 100644
--- a/unit_test/rotate_test.cc
+++ b/unit_test/rotate_test.cc
@@ -137,6 +137,94 @@ TEST_F(LibYUVRotateTest, DISABLED_I420Rotate270_Odd) {
benchmark_cpu_info_);
}
+static void I422TestRotate(int src_width,
+ int src_height,
+ int dst_width,
+ int dst_height,
+ libyuv::RotationMode mode,
+ int benchmark_iterations,
+ int disable_cpu_flags,
+ int benchmark_cpu_info) {
+ if (src_width < 1) {
+ src_width = 1;
+ }
+ if (src_height == 0) {
+ src_height = 1;
+ }
+ if (dst_width < 1) {
+ dst_width = 1;
+ }
+ if (dst_height < 1) {
+ dst_height = 1;
+ }
+ int src_i422_y_size = src_width * Abs(src_height);
+ int src_i422_uv_size = ((src_width + 1) / 2) * Abs(src_height);
+ int src_i422_size = src_i422_y_size + src_i422_uv_size * 2;
+ align_buffer_page_end(src_i422, src_i422_size);
+ for (int i = 0; i < src_i422_size; ++i) {
+ src_i422[i] = fastrand() & 0xff;
+ }
+
+ int dst_i422_y_size = dst_width * dst_height;
+ int dst_i422_uv_size = ((dst_width + 1) / 2) * dst_height;
+ int dst_i422_size = dst_i422_y_size + dst_i422_uv_size * 2;
+ align_buffer_page_end(dst_i422_c, dst_i422_size);
+ align_buffer_page_end(dst_i422_opt, dst_i422_size);
+ memset(dst_i422_c, 2, dst_i422_size);
+ memset(dst_i422_opt, 3, dst_i422_size);
+
+ MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization.
+ I422Rotate(src_i422, src_width, src_i422 + src_i422_y_size,
+ (src_width + 1) / 2, src_i422 + src_i422_y_size + src_i422_uv_size,
+ (src_width + 1) / 2, dst_i422_c, dst_width,
+ dst_i422_c + dst_i422_y_size, (dst_width + 1) / 2,
+ dst_i422_c + dst_i422_y_size + dst_i422_uv_size,
+ (dst_width + 1) / 2, src_width, src_height, mode);
+
+ MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization.
+ for (int i = 0; i < benchmark_iterations; ++i) {
+ I422Rotate(
+ src_i422, src_width, src_i422 + src_i422_y_size, (src_width + 1) / 2,
+ src_i422 + src_i422_y_size + src_i422_uv_size, (src_width + 1) / 2,
+ dst_i422_opt, dst_width, dst_i422_opt + dst_i422_y_size,
+ (dst_width + 1) / 2, dst_i422_opt + dst_i422_y_size + dst_i422_uv_size,
+ (dst_width + 1) / 2, src_width, src_height, mode);
+ }
+
+ // Rotation should be exact.
+ for (int i = 0; i < dst_i422_size; ++i) {
+ EXPECT_EQ(dst_i422_c[i], dst_i422_opt[i]);
+ }
+
+ free_aligned_buffer_page_end(dst_i422_c);
+ free_aligned_buffer_page_end(dst_i422_opt);
+ free_aligned_buffer_page_end(src_i422);
+}
+
+TEST_F(LibYUVRotateTest, I422Rotate0_Opt) {
+ I422TestRotate(benchmark_width_, benchmark_height_, benchmark_width_,
+ benchmark_height_, kRotate0, benchmark_iterations_,
+ disable_cpu_flags_, benchmark_cpu_info_);
+}
+
+TEST_F(LibYUVRotateTest, I422Rotate90_Opt) {
+ I422TestRotate(benchmark_width_, benchmark_height_, benchmark_height_,
+ benchmark_width_, kRotate90, benchmark_iterations_,
+ disable_cpu_flags_, benchmark_cpu_info_);
+}
+
+TEST_F(LibYUVRotateTest, I422Rotate180_Opt) {
+ I422TestRotate(benchmark_width_, benchmark_height_, benchmark_width_,
+ benchmark_height_, kRotate180, benchmark_iterations_,
+ disable_cpu_flags_, benchmark_cpu_info_);
+}
+
+TEST_F(LibYUVRotateTest, I422Rotate270_Opt) {
+ I422TestRotate(benchmark_width_, benchmark_height_, benchmark_height_,
+ benchmark_width_, kRotate270, benchmark_iterations_,
+ disable_cpu_flags_, benchmark_cpu_info_);
+}
+
static void I444TestRotate(int src_width,
int src_height,
int dst_width,