Commit a8aa921c authored by Robert Bares's avatar Robert Bares Committed by Commit Bot

Allow negative height when ConvertToI420/ARGB is called with NV12/NV21

ConvertToI420 and ConvertToARGB support the use of a negative height
parameter to flip the image vertically. When converting from NV12 or
NV21 this parameter was misinterpreted, resulting in invalid output.
This CL introduces the use of abs_src_height to correctly calculate
the location of the source UV plane.

The sign of crop_height is not used, to reduce confusion ConvertToI420
and ConvertToARGB no longer accept negative crop height.

Unit tests for Android420ToI420 are updated to fix miscalculation of
src_stride_uv, fix incorrect pixel strides, and to test inversion.
New unit tests are included to test inversion for ConvertToARGB,
ConvertToI420, Android420ToARGB, and Android420ToABGR.
For consistency the test NV12Crop is renamed ConvertToI420_NV12_Crop.

Bug: libyuv:446
Test: out/Release/libyuv_unittest --gtest_filter=*.ConvertTo*:*.Android420To*
Change-Id: Idc98e62671cb30272cfa7e24fafbc8b73712f7c6
Reviewed-on: https://chromium-review.googlesource.com/994074
Commit-Queue: Frank Barchard <fbarchard@chromium.org>
Reviewed-by: 's avatarFrank Barchard <fbarchard@chromium.org>
parent 5669005f
# Names should be added to this file like so: # Names should be added to this file like so:
# Name or Organization <email address> # Name or Organization <email address>
Robert Bares <robert@bares.me>
Google Inc. Google Inc.
...@@ -46,7 +46,7 @@ int ConvertToARGB(const uint8_t* sample, ...@@ -46,7 +46,7 @@ int ConvertToARGB(const uint8_t* sample,
const uint8_t* src; const uint8_t* src;
const uint8_t* src_uv; const uint8_t* src_uv;
int abs_src_height = (src_height < 0) ? -src_height : src_height; int abs_src_height = (src_height < 0) ? -src_height : src_height;
int inv_crop_height = (crop_height < 0) ? -crop_height : crop_height; int inv_crop_height;
int r = 0; int r = 0;
// One pass rotation is available for some formats. For the rest, convert // One pass rotation is available for some formats. For the rest, convert
...@@ -59,18 +59,16 @@ int ConvertToARGB(const uint8_t* sample, ...@@ -59,18 +59,16 @@ int ConvertToARGB(const uint8_t* sample,
uint8_t* dest_argb = dst_argb; uint8_t* dest_argb = dst_argb;
int dest_dst_stride_argb = dst_stride_argb; int dest_dst_stride_argb = dst_stride_argb;
uint8_t* rotate_buffer = NULL; uint8_t* rotate_buffer = NULL;
int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
if (dst_argb == NULL || sample == NULL || src_width <= 0 || crop_width <= 0 || if (dst_argb == NULL || sample == NULL || src_width <= 0 || crop_width <= 0 ||
src_height == 0 || crop_height == 0) { src_height == 0 || crop_height <= 0) {
return -1; return -1;
} }
if (src_height < 0) {
inv_crop_height = -inv_crop_height; inv_crop_height = (src_height < 0) ? -crop_height : crop_height;
}
if (need_buf) { if (need_buf) {
int argb_size = crop_width * 4 * abs_crop_height; int argb_size = crop_width * 4 * crop_height;
rotate_buffer = (uint8_t*)malloc(argb_size); /* NOLINT */ rotate_buffer = (uint8_t*)malloc(argb_size); /* NOLINT */
if (!rotate_buffer) { if (!rotate_buffer) {
return 1; // Out of memory runtime error. return 1; // Out of memory runtime error.
...@@ -147,13 +145,13 @@ int ConvertToARGB(const uint8_t* sample, ...@@ -147,13 +145,13 @@ int ConvertToARGB(const uint8_t* sample,
// Biplanar formats // Biplanar formats
case FOURCC_NV12: case FOURCC_NV12:
src = sample + (src_width * crop_y + crop_x); src = sample + (src_width * crop_y + crop_x);
src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x; src_uv = sample + aligned_src_width * (abs_src_height + crop_y / 2) + crop_x;
r = NV12ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb, r = NV12ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb,
dst_stride_argb, crop_width, inv_crop_height); dst_stride_argb, crop_width, inv_crop_height);
break; break;
case FOURCC_NV21: case FOURCC_NV21:
src = sample + (src_width * crop_y + crop_x); src = sample + (src_width * crop_y + crop_x);
src_uv = sample + aligned_src_width * (src_height + crop_y / 2) + crop_x; src_uv = sample + aligned_src_width * (abs_src_height + crop_y / 2) + crop_x;
// Call NV12 but with u and v parameters swapped. // Call NV12 but with u and v parameters swapped.
r = NV21ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb, r = NV21ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb,
dst_stride_argb, crop_width, inv_crop_height); dst_stride_argb, crop_width, inv_crop_height);
...@@ -252,7 +250,7 @@ int ConvertToARGB(const uint8_t* sample, ...@@ -252,7 +250,7 @@ int ConvertToARGB(const uint8_t* sample,
if (need_buf) { if (need_buf) {
if (!r) { if (!r) {
r = ARGBRotate(dst_argb, dst_stride_argb, dest_argb, dest_dst_stride_argb, r = ARGBRotate(dst_argb, dst_stride_argb, dest_argb, dest_dst_stride_argb,
crop_width, abs_crop_height, rotation); crop_width, crop_height, rotation);
} }
free(rotate_buffer); free(rotate_buffer);
} else if (rotation) { } else if (rotation) {
......
...@@ -46,8 +46,6 @@ int ConvertToI420(const uint8_t* sample, ...@@ -46,8 +46,6 @@ int ConvertToI420(const uint8_t* sample,
const uint8_t* src; const uint8_t* src;
const uint8_t* src_uv; const uint8_t* src_uv;
const int abs_src_height = (src_height < 0) ? -src_height : src_height; const int abs_src_height = (src_height < 0) ? -src_height : src_height;
// TODO(nisse): Why allow crop_height < 0?
const int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
int r = 0; int r = 0;
LIBYUV_BOOL need_buf = LIBYUV_BOOL need_buf =
(rotation && format != FOURCC_I420 && format != FOURCC_NV12 && (rotation && format != FOURCC_I420 && format != FOURCC_NV12 &&
...@@ -60,22 +58,23 @@ int ConvertToI420(const uint8_t* sample, ...@@ -60,22 +58,23 @@ int ConvertToI420(const uint8_t* sample,
int tmp_u_stride = dst_stride_u; int tmp_u_stride = dst_stride_u;
int tmp_v_stride = dst_stride_v; int tmp_v_stride = dst_stride_v;
uint8_t* rotate_buffer = NULL; uint8_t* rotate_buffer = NULL;
const int inv_crop_height = int inv_crop_height;
(src_height < 0) ? -abs_crop_height : abs_crop_height;
if (!dst_y || !dst_u || !dst_v || !sample || src_width <= 0 || if (!dst_y || !dst_u || !dst_v || !sample || src_width <= 0 ||
crop_width <= 0 || src_height == 0 || crop_height == 0) { crop_width <= 0 || src_height == 0 || crop_height <= 0) {
return -1; return -1;
} }
inv_crop_height = (src_height < 0) ? -crop_height : crop_height;
// One pass rotation is available for some formats. For the rest, convert // One pass rotation is available for some formats. For the rest, convert
// to I420 (with optional vertical flipping) into a temporary I420 buffer, // to I420 (with optional vertical flipping) into a temporary I420 buffer,
// and then rotate the I420 to the final destination buffer. // and then rotate the I420 to the final destination buffer.
// For in-place conversion, if destination dst_y is same as source sample, // For in-place conversion, if destination dst_y is same as source sample,
// also enable temporary buffer. // also enable temporary buffer.
if (need_buf) { if (need_buf) {
int y_size = crop_width * abs_crop_height; int y_size = crop_width * crop_height;
int uv_size = ((crop_width + 1) / 2) * ((abs_crop_height + 1) / 2); int uv_size = ((crop_width + 1) / 2) * ((crop_height + 1) / 2);
rotate_buffer = (uint8_t*)malloc(y_size + uv_size * 2); /* NOLINT */ rotate_buffer = (uint8_t*)malloc(y_size + uv_size * 2); /* NOLINT */
if (!rotate_buffer) { if (!rotate_buffer) {
return 1; // Out of memory runtime error. return 1; // Out of memory runtime error.
...@@ -163,7 +162,7 @@ int ConvertToI420(const uint8_t* sample, ...@@ -163,7 +162,7 @@ int ConvertToI420(const uint8_t* sample,
// Biplanar formats // Biplanar formats
case FOURCC_NV12: case FOURCC_NV12:
src = sample + (src_width * crop_y + crop_x); src = sample + (src_width * crop_y + crop_x);
src_uv = sample + (src_width * src_height) + src_uv = sample + (src_width * abs_src_height) +
((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2); ((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2);
r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y, r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_y, dst_u, dst_stride_u, dst_v,
...@@ -171,7 +170,7 @@ int ConvertToI420(const uint8_t* sample, ...@@ -171,7 +170,7 @@ int ConvertToI420(const uint8_t* sample,
break; break;
case FOURCC_NV21: case FOURCC_NV21:
src = sample + (src_width * crop_y + crop_x); src = sample + (src_width * crop_y + crop_x);
src_uv = sample + (src_width * src_height) + src_uv = sample + (src_width * abs_src_height) +
((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2); ((crop_y / 2) * aligned_src_width) + ((crop_x / 2) * 2);
// Call NV12 but with dst_u and dst_v parameters swapped. // Call NV12 but with dst_u and dst_v parameters swapped.
r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y, r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y,
...@@ -261,7 +260,7 @@ int ConvertToI420(const uint8_t* sample, ...@@ -261,7 +260,7 @@ int ConvertToI420(const uint8_t* sample,
if (!r) { if (!r) {
r = I420Rotate(dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v, r = I420Rotate(dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v,
dst_stride_v, tmp_y, tmp_y_stride, tmp_u, tmp_u_stride, dst_stride_v, tmp_y, tmp_y_stride, tmp_u, tmp_u_stride,
tmp_v, tmp_v_stride, crop_width, abs_crop_height, tmp_v, tmp_v_stride, crop_width, crop_height,
rotation); rotation);
} }
free(rotate_buffer); free(rotate_buffer);
......
...@@ -156,31 +156,34 @@ TESTPLANARTOP(H010, uint16_t, 2, 2, 2, H010, uint16_t, 2, 2, 2) ...@@ -156,31 +156,34 @@ TESTPLANARTOP(H010, uint16_t, 2, 2, 2, H010, uint16_t, 2, 2, 2)
TESTPLANARTOP(H010, uint16_t, 2, 2, 2, H420, uint8_t, 1, 2, 2) TESTPLANARTOP(H010, uint16_t, 2, 2, 2, H420, uint8_t, 1, 2, 2)
TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2) TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2)
// Test Android 420 to I420 // Test Android 420 to I420.
#define TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, \ #define TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, \
SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \
W1280, N, NEG, OFF, PN, OFF_U, OFF_V) \ W1280, N, NEG, OFF, PN, OFF_U, OFF_V, BASELINE) \
TEST_F(LibYUVConvertTest, SRC_FMT_PLANAR##To##FMT_PLANAR##_##PN##N) { \ TEST_F(LibYUVConvertTest, SRC_FMT_PLANAR##To##FMT_PLANAR##_##PN##N) { \
const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ const int kWidth = ((W1280) > 0) ? (W1280) : 1; \
const int kHeight = benchmark_height_; \ const int kHeight = benchmark_height_; \
const int kSizeUV = \ const int kStrideUVSrc = SUBSAMPLE(kWidth, SRC_SUBSAMP_X); \
SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); \ const int kStrideUVDst = SUBSAMPLE(kWidth, SUBSAMP_X); \
align_buffer_page_end(src_y, kWidth* kHeight + OFF); \ const int kSizeUVSrc = \
kStrideUVSrc * SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); \
const int kSizeUVDst = \
kStrideUVDst * SUBSAMPLE(kHeight, SUBSAMP_Y); \
align_buffer_page_end(src_y, kWidth * kHeight + OFF); \
align_buffer_page_end(src_uv, \ align_buffer_page_end(src_uv, \
kSizeUV*((PIXEL_STRIDE == 3) ? 3 : 2) + OFF); \ kSizeUVSrc*((PIXEL_STRIDE == 3) ? 3 : 2) + OFF); \
align_buffer_page_end(dst_y_c, kWidth* kHeight); \ align_buffer_page_end(dst_y_c, kWidth * kHeight); \
align_buffer_page_end(dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \ align_buffer_page_end(dst_u_c, kSizeUVDst); \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \ align_buffer_page_end(dst_v_c, kSizeUVDst); \
align_buffer_page_end(dst_v_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \ align_buffer_page_end(dst_y_baseline, kWidth * kHeight); \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \ align_buffer_page_end(dst_u_baseline, kSizeUVDst); \
align_buffer_page_end(dst_y_opt, kWidth* kHeight); \ align_buffer_page_end(dst_v_baseline, kSizeUVDst); \
align_buffer_page_end(dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \ align_buffer_page_end(dst_y_opt, kWidth * kHeight); \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \ align_buffer_page_end(dst_u_opt, kSizeUVDst); \
align_buffer_page_end(dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \ align_buffer_page_end(dst_v_opt, kSizeUVDst); \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
uint8_t* src_u = src_uv + OFF_U; \ uint8_t* src_u = src_uv + OFF_U; \
uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUV : OFF_V); \ uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUVSrc : OFF_V); \
int src_stride_uv = SUBSAMPLE(kWidth, SUBSAMP_X) * PIXEL_STRIDE; \ int src_stride_uv = kStrideUVSrc * PIXEL_STRIDE; \
for (int i = 0; i < kHeight; ++i) \ for (int i = 0; i < kHeight; ++i) \
for (int j = 0; j < kWidth; ++j) \ for (int j = 0; j < kWidth; ++j) \
src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \ src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \
...@@ -192,31 +195,68 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2) ...@@ -192,31 +195,68 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2)
(fastrand() & 0xff); \ (fastrand() & 0xff); \
} \ } \
} \ } \
memset(dst_y_c, 1, kWidth* kHeight); \ memset(dst_y_c, 1, kWidth * kHeight); \
memset(dst_u_c, 2, \ memset(dst_u_c, 2, kSizeUVDst); \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ memset(dst_v_c, 3, kSizeUVDst); \
memset(dst_v_c, 3, \ memset(dst_y_baseline, 51, kWidth * kHeight); \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ memset(dst_u_baseline, 52, kSizeUVDst); \
memset(dst_y_opt, 101, kWidth* kHeight); \ memset(dst_v_baseline, 53, kSizeUVDst); \
memset(dst_u_opt, 102, \ memset(dst_y_opt, 101, kWidth * kHeight); \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ memset(dst_u_opt, 102, kSizeUVDst); \
memset(dst_v_opt, 103, \ memset(dst_v_opt, 103, kSizeUVDst); \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
MaskCpuFlags(disable_cpu_flags_); \ MaskCpuFlags(disable_cpu_flags_); \
SRC_FMT_PLANAR##To##FMT_PLANAR( \ SRC_FMT_PLANAR##To##FMT_PLANAR( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \ src_y + OFF, kWidth, src_u + OFF, src_stride_uv, \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, dst_y_c, \ src_v + OFF, src_stride_uv, PIXEL_STRIDE, dst_y_c, \
kWidth, dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X), dst_v_c, \ kWidth, dst_u_c, kStrideUVDst, dst_v_c, \
SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight); \ kStrideUVDst, kWidth, NEG kHeight); \
BASELINE(src_y + OFF, kWidth, src_uv + OFF, src_stride_uv, \
dst_y_baseline, kWidth, dst_u_baseline, \
kStrideUVDst, dst_v_baseline, \
kStrideUVDst, kWidth, NEG kHeight); \
MaskCpuFlags(benchmark_cpu_info_); \ MaskCpuFlags(benchmark_cpu_info_); \
for (int i = 0; i < benchmark_iterations_; ++i) { \ for (int i = 0; i < benchmark_iterations_; ++i) { \
SRC_FMT_PLANAR##To##FMT_PLANAR( \ SRC_FMT_PLANAR##To##FMT_PLANAR( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \ src_y + OFF, kWidth, src_u + OFF, src_stride_uv, \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, \ src_v + OFF, src_stride_uv, PIXEL_STRIDE, \
dst_y_opt, kWidth, dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \ dst_y_opt, kWidth, dst_u_opt, kStrideUVDst, \
dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight); \ dst_v_opt, kStrideUVDst, kWidth, NEG kHeight); \
} \ } \
int max_diff = 0; \ int max_diff = 0; \
for (int i = 0; i < kHeight; ++i) { \
for (int j = 0; j < kWidth; ++j) { \
int abs_diff = abs(static_cast<int>(dst_y_c[i * kWidth + j]) - \
static_cast<int>(dst_y_baseline[i * kWidth + j])); \
if (abs_diff > max_diff) { \
max_diff = abs_diff; \
} \
} \
} \
EXPECT_EQ(0, max_diff); \
for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \
for (int j = 0; j < SUBSAMPLE(kWidth, SUBSAMP_X); ++j) { \
int abs_diff = abs( \
static_cast<int>(dst_u_c[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j]) - \
static_cast<int>( \
dst_u_baseline[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \
if (abs_diff > max_diff) { \
max_diff = abs_diff; \
} \
} \
} \
EXPECT_EQ(0, max_diff); \
for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \
for (int j = 0; j < SUBSAMPLE(kWidth, SUBSAMP_X); ++j) { \
int abs_diff = abs( \
static_cast<int>(dst_v_c[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j]) - \
static_cast<int>( \
dst_v_baseline[i * SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \
if (abs_diff > max_diff) { \
max_diff = abs_diff; \
} \
} \
} \
EXPECT_EQ(0, max_diff); \
for (int i = 0; i < kHeight; ++i) { \ for (int i = 0; i < kHeight; ++i) { \
for (int j = 0; j < kWidth; ++j) { \ for (int j = 0; j < kWidth; ++j) { \
int abs_diff = abs(static_cast<int>(dst_y_c[i * kWidth + j]) - \ int abs_diff = abs(static_cast<int>(dst_y_c[i * kWidth + j]) - \
...@@ -254,6 +294,9 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2) ...@@ -254,6 +294,9 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2)
free_aligned_buffer_page_end(dst_y_c); \ free_aligned_buffer_page_end(dst_y_c); \
free_aligned_buffer_page_end(dst_u_c); \ free_aligned_buffer_page_end(dst_u_c); \
free_aligned_buffer_page_end(dst_v_c); \ free_aligned_buffer_page_end(dst_v_c); \
free_aligned_buffer_page_end(dst_y_baseline); \
free_aligned_buffer_page_end(dst_u_baseline); \
free_aligned_buffer_page_end(dst_v_baseline); \
free_aligned_buffer_page_end(dst_y_opt); \ free_aligned_buffer_page_end(dst_y_opt); \
free_aligned_buffer_page_end(dst_u_opt); \ free_aligned_buffer_page_end(dst_u_opt); \
free_aligned_buffer_page_end(dst_v_opt); \ free_aligned_buffer_page_end(dst_v_opt); \
...@@ -263,23 +306,33 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2) ...@@ -263,23 +306,33 @@ TESTPLANARTOP(H420, uint8_t, 1, 2, 2, H010, uint16_t, 2, 2, 2)
#define TESTAPLANARTOP(SRC_FMT_PLANAR, PN, PIXEL_STRIDE, OFF_U, OFF_V, \ #define TESTAPLANARTOP(SRC_FMT_PLANAR, PN, PIXEL_STRIDE, OFF_U, OFF_V, \
SRC_SUBSAMP_X, SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, \ SRC_SUBSAMP_X, SRC_SUBSAMP_Y, FMT_PLANAR, SUBSAMP_X, \
SUBSAMP_Y) \ SUBSAMP_Y, BASELINE) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_ - 4, \ FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_ - 4, \
_Any, +, 0, PN, OFF_U, OFF_V) \ _Any, +, 0, PN, OFF_U, OFF_V, BASELINE) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, \ FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, \
_Unaligned, +, 1, PN, OFF_U, OFF_V) \ _Unaligned, +, 1, PN, OFF_U, OFF_V, BASELINE) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Invert, \ FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Invert, \
-, 0, PN, OFF_U, OFF_V) \ -, 0, PN, OFF_U, OFF_V, BASELINE) \
TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ TESTAPLANARTOPI(SRC_FMT_PLANAR, PIXEL_STRIDE, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Opt, +, \ FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, benchmark_width_, _Opt, +, \
0, PN, OFF_U, OFF_V) 0, PN, OFF_U, OFF_V, BASELINE)
TESTAPLANARTOP(Android420, I420, 1, 0, 0, 2, 2, I420, 2, 2) // I420ToI420 matching the NV12ToI420 signature, requires kSizeUVSrc in scope.
TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2) #define I420TOI420_I(src_y, src_stride_y, src_uv, src_stride_uv, \
TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2) dst_y, dst_stride_y, dst_u, dst_stride_u, \
dst_v, dst_stride_v, width, height) \
I420ToI420(src_y, src_stride_y, src_uv, src_stride_uv, \
src_uv + kSizeUVSrc, src_stride_uv, dst_y, dst_stride_y, \
dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)
TESTAPLANARTOP(Android420, I420, 1, 0, 0, 2, 2, I420, 2, 2, I420TOI420_I)
TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2, NV12ToI420)
TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2, NV21ToI420)
#undef I420TOI420_I
#define TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ #define TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \ FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \
...@@ -1424,85 +1477,216 @@ TEST_F(LibYUVConvertTest, MJPGToARGB) { ...@@ -1424,85 +1477,216 @@ TEST_F(LibYUVConvertTest, MJPGToARGB) {
#endif // HAVE_JPEG #endif // HAVE_JPEG
TEST_F(LibYUVConvertTest, NV12Crop) { #define TESTTOI420(FMT, NEG, N) \
const int SUBSAMP_X = 2; TEST_F(LibYUVConvertTest, ConvertToI420_##FMT##N) { \
const int SUBSAMP_Y = 2; const int SUBSAMP_X = 2; \
const int kWidth = benchmark_width_; const int SUBSAMP_Y = 2; \
const int kHeight = benchmark_height_; const int kWidth = benchmark_width_; \
const int crop_y = const int kHeight = benchmark_height_; \
((benchmark_height_ - (benchmark_height_ * 360 / 480)) / 2 + 1) & ~1; const int crop_y = \
const int kDestWidth = benchmark_width_; ((benchmark_height_ - (benchmark_height_ * 360 / 480)) / 2 + 1) & ~1; \
const int kDestHeight = benchmark_height_ - crop_y * 2; const int kDestWidth = benchmark_width_; \
const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); const int kDestHeight = benchmark_height_ - crop_y * 2; \
const int sample_size = const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \
kWidth * kHeight + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y) * 2; const int sample_size = \
align_buffer_page_end(src_y, sample_size); kWidth * kHeight + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y) * 2; \
uint8_t* src_uv = src_y + kWidth * kHeight; align_buffer_page_end(src_y, sample_size); \
uint8_t* src_uv = src_y + kWidth * kHeight; \
align_buffer_page_end(dst_y, kDestWidth * kDestHeight); \
align_buffer_page_end(dst_u, SUBSAMPLE(kDestWidth, SUBSAMP_X) * align_buffer_page_end(dst_y, kDestWidth * kDestHeight); \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); align_buffer_page_end(dst_u, SUBSAMPLE(kDestWidth, SUBSAMP_X) * \
align_buffer_page_end(dst_v, SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); align_buffer_page_end(dst_v, SUBSAMPLE(kDestWidth, SUBSAMP_X) * \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_y_2, kDestWidth * kDestHeight); \
align_buffer_page_end(dst_u_2, SUBSAMPLE(kDestWidth, SUBSAMP_X) * align_buffer_page_end(dst_y_2, kDestWidth * kDestHeight); \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); align_buffer_page_end(dst_u_2, SUBSAMPLE(kDestWidth, SUBSAMP_X) * \
align_buffer_page_end(dst_v_2, SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); align_buffer_page_end(dst_v_2, SUBSAMPLE(kDestWidth, SUBSAMP_X) * \
SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
for (int i = 0; i < kHeight * kWidth; ++i) { \
src_y[i] = (fastrand() & 0xff); for (int i = 0; i < kHeight * kWidth; ++i) { \
} src_y[i] = (fastrand() & 0xff); \
for (int i = 0; i < (SUBSAMPLE(kHeight, SUBSAMP_Y) * kStrideUV) * 2; ++i) { } \
src_uv[i] = (fastrand() & 0xff); for (int i = 0; i < (SUBSAMPLE(kHeight, SUBSAMP_Y) * kStrideUV) * 2; ++i) { \
} src_uv[i] = (fastrand() & 0xff); \
memset(dst_y, 1, kDestWidth * kDestHeight); } \
memset(dst_u, 2, memset(dst_y, 1, kDestWidth * kDestHeight); \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); memset(dst_u, 2, \
memset(dst_v, 3, SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); memset(dst_v, 3, \
memset(dst_y_2, 1, kDestWidth * kDestHeight); SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
memset(dst_u_2, 2, memset(dst_y_2, 1, kDestWidth * kDestHeight); \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); memset(dst_u_2, 2, \
memset(dst_v_2, 3, SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); memset(dst_v_2, 3, \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
ConvertToI420(src_y, sample_size, dst_y_2, kDestWidth, dst_u_2, \
SUBSAMPLE(kDestWidth, SUBSAMP_X), dst_v_2, ConvertToI420(src_y, sample_size, dst_y_2, kDestWidth, dst_u_2, \
SUBSAMPLE(kDestWidth, SUBSAMP_X), 0, crop_y, kWidth, kHeight, SUBSAMPLE(kDestWidth, SUBSAMP_X), dst_v_2, \
kDestWidth, kDestHeight, libyuv::kRotate0, libyuv::FOURCC_NV12); SUBSAMPLE(kDestWidth, SUBSAMP_X), 0, crop_y, kWidth, NEG kHeight, \
kDestWidth, kDestHeight, libyuv::kRotate0, libyuv::FOURCC_##FMT); \
NV12ToI420(src_y + crop_y * kWidth, kWidth, \
src_uv + (crop_y / 2) * kStrideUV * 2, kStrideUV * 2, dst_y, FMT##ToI420(src_y + crop_y * kWidth, kWidth, \
kDestWidth, dst_u, SUBSAMPLE(kDestWidth, SUBSAMP_X), dst_v, src_uv + (crop_y / 2) * kStrideUV * 2, kStrideUV * 2, dst_y, \
SUBSAMPLE(kDestWidth, SUBSAMP_X), kDestWidth, kDestHeight); kDestWidth, dst_u, SUBSAMPLE(kDestWidth, SUBSAMP_X), dst_v, \
SUBSAMPLE(kDestWidth, SUBSAMP_X), kDestWidth, NEG kDestHeight); \
for (int i = 0; i < kDestHeight; ++i) { \
for (int j = 0; j < kDestWidth; ++j) { for (int i = 0; i < kDestHeight; ++i) { \
EXPECT_EQ(dst_y[i * kWidth + j], dst_y_2[i * kWidth + j]); for (int j = 0; j < kDestWidth; ++j) { \
} EXPECT_EQ(dst_y[i * kWidth + j], dst_y_2[i * kWidth + j]); \
} } \
for (int i = 0; i < SUBSAMPLE(kDestHeight, SUBSAMP_Y); ++i) { } \
for (int j = 0; j < SUBSAMPLE(kDestWidth, SUBSAMP_X); ++j) { for (int i = 0; i < SUBSAMPLE(kDestHeight, SUBSAMP_Y); ++i) { \
EXPECT_EQ(dst_u[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j], for (int j = 0; j < SUBSAMPLE(kDestWidth, SUBSAMP_X); ++j) { \
dst_u_2[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j]); EXPECT_EQ(dst_u[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j], \
} dst_u_2[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j]); \
} } \
for (int i = 0; i < SUBSAMPLE(kDestHeight, SUBSAMP_Y); ++i) { } \
for (int j = 0; j < SUBSAMPLE(kDestWidth, SUBSAMP_X); ++j) { for (int i = 0; i < SUBSAMPLE(kDestHeight, SUBSAMP_Y); ++i) { \
EXPECT_EQ(dst_v[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j], for (int j = 0; j < SUBSAMPLE(kDestWidth, SUBSAMP_X); ++j) { \
dst_v_2[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j]); EXPECT_EQ(dst_v[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j], \
} dst_v_2[i * SUBSAMPLE(kDestWidth, SUBSAMP_X) + j]); \
} } \
free_aligned_buffer_page_end(dst_y); } \
free_aligned_buffer_page_end(dst_u); free_aligned_buffer_page_end(dst_y); \
free_aligned_buffer_page_end(dst_v); free_aligned_buffer_page_end(dst_u); \
free_aligned_buffer_page_end(dst_y_2); free_aligned_buffer_page_end(dst_v); \
free_aligned_buffer_page_end(dst_u_2); free_aligned_buffer_page_end(dst_y_2); \
free_aligned_buffer_page_end(dst_v_2); free_aligned_buffer_page_end(dst_u_2); \
free_aligned_buffer_page_end(src_y); free_aligned_buffer_page_end(dst_v_2); \
} free_aligned_buffer_page_end(src_y); \
}
TESTTOI420(NV12, +, _Crop)
TESTTOI420(NV12, -, _Crop_Invert)
TESTTOI420(NV21, +, _Crop)
TESTTOI420(NV21, -, _Crop_Invert)
#define TESTTOARGB(FMT, NEG, N) \
TEST_F(LibYUVConvertTest, ConvertToARGB_##FMT##N) { \
const int SUBSAMP_X = 2; \
const int SUBSAMP_Y = 2; \
const int kWidth = benchmark_width_; \
const int kHeight = benchmark_height_; \
const int crop_y = \
((benchmark_height_ - (benchmark_height_ * 360 / 480)) / 2 + 1) & ~1; \
const int kDestWidth = benchmark_width_; \
const int kDestHeight = benchmark_height_ - crop_y * 2; \
const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \
const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \
const int sample_size = kWidth * kHeight + kSizeUV * 2; \
align_buffer_page_end(src_y, sample_size); \
uint8_t* src_uv = src_y + kWidth * kHeight; \
\
align_buffer_page_end(dst_argb, kWidth * 4 * kHeight); \
align_buffer_page_end(dst_argb_2, kWidth * 4 * kHeight); \
\
for (int i = 0; i < kHeight * kWidth; ++i) { \
src_y[i] = (fastrand() & 0xff); \
} \
for (int i = 0; i < kSizeUV * 2; ++i) { \
src_uv[i] = (fastrand() & 0xff); \
} \
memset(dst_argb, 1, kDestWidth * 4 * kDestHeight); \
memset(dst_argb_2, 1, kDestWidth * 4 * kDestHeight); \
\
ConvertToARGB(src_y, sample_size, dst_argb_2, kDestWidth * 4, 0, crop_y, \
kWidth, NEG kHeight, kDestWidth, kDestHeight, \
libyuv::kRotate0, libyuv::FOURCC_##FMT); \
\
FMT##ToARGB(src_y + crop_y * kWidth, kWidth, \
src_uv + (crop_y / 2) * kStrideUV * 2, kStrideUV * 2, \
dst_argb, kDestWidth * 4, kDestWidth, NEG kDestHeight); \
\
for (int i = 0; i < kDestHeight; ++i) { \
for (int j = 0; j < kDestWidth * 4; ++j) { \
EXPECT_EQ(dst_argb[i * kWidth + j], dst_argb_2[i * kWidth + j]); \
} \
} \
free_aligned_buffer_page_end(dst_argb); \
free_aligned_buffer_page_end(dst_argb_2); \
free_aligned_buffer_page_end(src_y); \
}
TESTTOARGB(NV12, +, _Crop)
TESTTOARGB(NV12, -, _Crop_Invert)
TESTTOARGB(NV21, +, _Crop)
TESTTOARGB(NV21, -, _Crop_Invert)
#define TESTANDROID420TOBI(FMT_A, PIXEL_STRIDE, OFF_U, OFF_V, FMT_B, BASELINE, \
NEG, N) \
TEST_F(LibYUVConvertTest, Android420To##FMT_B##_##FMT_A##N) { \
const int SUBSAMP_X = 2; \
const int SUBSAMP_Y = 2; \
const int kWidth = benchmark_width_; \
const int kHeight = benchmark_height_; \
const int kDestWidth = benchmark_width_; \
const int kDestHeight = benchmark_height_; \
const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \
const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \
const int sample_size = kWidth * kHeight + kSizeUV * 2; \
align_buffer_page_end(src_y, sample_size); \
uint8_t* src_uv = src_y + kWidth * kHeight; \
uint8_t* src_u = src_uv + OFF_U; \
uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUV : OFF_V); \
int src_stride_uv = kStrideUV * PIXEL_STRIDE; \
align_buffer_page_end(dst_argb, kWidth * 4 * kHeight); \
align_buffer_page_end(dst_argb_2, kWidth * 4 * kHeight); \
\
for (int i = 0; i < kHeight * kWidth; ++i) { \
src_y[i] = (fastrand() & 0xff); \
} \
for (int i = 0; i < kSizeUV * 2; ++i) { \
src_uv[i] = (fastrand() & 0xff); \
} \
memset(dst_argb, 1, kDestWidth * 4 * kDestHeight); \
memset(dst_argb_2, 1, kDestWidth * 4 * kDestHeight); \
\
Android420To##FMT_B(src_y, kWidth, src_u, src_stride_uv, src_v, \
src_stride_uv, PIXEL_STRIDE, dst_argb_2, \
kDestWidth * 4, kWidth, NEG kHeight); \
\
BASELINE(src_y, kWidth, src_uv, src_stride_uv, dst_argb, \
kDestWidth * 4, kDestWidth, NEG kDestHeight); \
\
for (int i = 0; i < kDestHeight; ++i) { \
for (int j = 0; j < kDestWidth * 4; ++j) { \
EXPECT_EQ(dst_argb[i * kWidth + j], dst_argb_2[i * kWidth + j]); \
} \
} \
free_aligned_buffer_page_end(dst_argb); \
free_aligned_buffer_page_end(dst_argb_2); \
free_aligned_buffer_page_end(src_y); \
}
#define TESTANDROID420TOB(FMT_A, PIXEL_STRIDE, OFF_U, OFF_V, FMT_B, BASELINE) \
TESTANDROID420TOBI(FMT_A, PIXEL_STRIDE, OFF_U, OFF_V, FMT_B, BASELINE, \
+, ) \
TESTANDROID420TOBI(FMT_A, PIXEL_STRIDE, OFF_U, OFF_V, FMT_B, BASELINE, \
-, _Invert)
// I420ToARGB/ABGR matching the NV12ToI420 signature, requires kSizeUV in scope.
#define I420TOARGB_I(src_y, src_stride_y, src_uv, src_stride_uv, \
dst_argb, dst_stride_argb, width, height) \
I420ToARGB(src_y, src_stride_y, src_uv, src_stride_uv, \
src_uv + kSizeUV, src_stride_uv, dst_argb, \
dst_stride_argb, width, height)
#define I420TOABGR_I(src_y, src_stride_y, src_uv, src_stride_uv, \
dst_argb, dst_stride_argb, width, height) \
I420ToABGR(src_y, src_stride_y, src_uv, src_stride_uv, \
src_uv + kSizeUV, src_stride_uv, dst_argb, \
dst_stride_argb, width, height)
TESTANDROID420TOB(NV12, 2, 0, 1, ARGB, NV12ToARGB)
TESTANDROID420TOB(NV21, 2, 1, 0, ARGB, NV21ToARGB)
TESTANDROID420TOB(I420, 1, 0, 0, ARGB, I420TOARGB_I)
TESTANDROID420TOB(NV12, 2, 0, 1, ABGR, NV12ToABGR)
TESTANDROID420TOB(NV21, 2, 1, 0, ABGR, NV21ToABGR)
TESTANDROID420TOB(I420, 1, 0, 0, ABGR, I420TOABGR_I)
#undef I420TOABGR_I
#undef I420TOARGB_I
TEST_F(LibYUVConvertTest, TestYToARGB) { TEST_F(LibYUVConvertTest, TestYToARGB) {
uint8_t y[32]; uint8_t y[32];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment