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>
This commit is contained in:
Sergio Garcia Murillo 2022-04-06 20:57:53 +02:00 committed by libyuv LUCI CQ
parent 4589081cea
commit a77d615e10
4 changed files with 191 additions and 1 deletions

View File

@ -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,

View File

@ -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_

View File

@ -544,6 +544,90 @@ int I420Rotate(const uint8_t* src_y,
return -1;
}
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,

View File

@ -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,