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:
# Name or Organization <email address>
Robert Bares <robert@bares.me>
Google Inc.
......@@ -46,7 +46,7 @@ int ConvertToARGB(const uint8_t* sample,
const uint8_t* src;
const uint8_t* src_uv;
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;
// One pass rotation is available for some formats. For the rest, convert
......@@ -59,18 +59,16 @@ int ConvertToARGB(const uint8_t* sample,
uint8_t* dest_argb = dst_argb;
int dest_dst_stride_argb = dst_stride_argb;
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 ||
src_height == 0 || crop_height == 0) {
src_height == 0 || crop_height <= 0) {
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) {
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 */
if (!rotate_buffer) {
return 1; // Out of memory runtime error.
......@@ -147,13 +145,13 @@ int ConvertToARGB(const uint8_t* sample,
// Biplanar formats
case FOURCC_NV12:
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,
dst_stride_argb, crop_width, inv_crop_height);
break;
case FOURCC_NV21:
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.
r = NV21ToARGB(src, src_width, src_uv, aligned_src_width, dst_argb,
dst_stride_argb, crop_width, inv_crop_height);
......@@ -252,7 +250,7 @@ int ConvertToARGB(const uint8_t* sample,
if (need_buf) {
if (!r) {
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);
} else if (rotation) {
......
......@@ -46,8 +46,6 @@ int ConvertToI420(const uint8_t* sample,
const uint8_t* src;
const uint8_t* src_uv;
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;
LIBYUV_BOOL need_buf =
(rotation && format != FOURCC_I420 && format != FOURCC_NV12 &&
......@@ -60,22 +58,23 @@ int ConvertToI420(const uint8_t* sample,
int tmp_u_stride = dst_stride_u;
int tmp_v_stride = dst_stride_v;
uint8_t* rotate_buffer = NULL;
const int inv_crop_height =
(src_height < 0) ? -abs_crop_height : abs_crop_height;
int inv_crop_height;
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;
}
inv_crop_height = (src_height < 0) ? -crop_height : crop_height;
// One pass rotation is available for some formats. For the rest, convert
// to I420 (with optional vertical flipping) into a temporary I420 buffer,
// and then rotate the I420 to the final destination buffer.
// For in-place conversion, if destination dst_y is same as source sample,
// also enable temporary buffer.
if (need_buf) {
int y_size = crop_width * abs_crop_height;
int uv_size = ((crop_width + 1) / 2) * ((abs_crop_height + 1) / 2);
int y_size = crop_width * crop_height;
int uv_size = ((crop_width + 1) / 2) * ((crop_height + 1) / 2);
rotate_buffer = (uint8_t*)malloc(y_size + uv_size * 2); /* NOLINT */
if (!rotate_buffer) {
return 1; // Out of memory runtime error.
......@@ -163,7 +162,7 @@ int ConvertToI420(const uint8_t* sample,
// Biplanar formats
case FOURCC_NV12:
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);
r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y,
dst_stride_y, dst_u, dst_stride_u, dst_v,
......@@ -171,7 +170,7 @@ int ConvertToI420(const uint8_t* sample,
break;
case FOURCC_NV21:
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);
// Call NV12 but with dst_u and dst_v parameters swapped.
r = NV12ToI420Rotate(src, src_width, src_uv, aligned_src_width, dst_y,
......@@ -261,7 +260,7 @@ int ConvertToI420(const uint8_t* sample,
if (!r) {
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,
tmp_v, tmp_v_stride, crop_width, abs_crop_height,
tmp_v, tmp_v_stride, crop_width, crop_height,
rotation);
}
free(rotate_buffer);
......
......@@ -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(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, \
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) { \
const int kWidth = ((W1280) > 0) ? (W1280) : 1; \
const int kHeight = benchmark_height_; \
const int kSizeUV = \
SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); \
align_buffer_page_end(src_y, kWidth* kHeight + OFF); \
const int kStrideUVSrc = SUBSAMPLE(kWidth, SRC_SUBSAMP_X); \
const int kStrideUVDst = SUBSAMPLE(kWidth, SUBSAMP_X); \
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, \
kSizeUV*((PIXEL_STRIDE == 3) ? 3 : 2) + OFF); \
align_buffer_page_end(dst_y_c, kWidth* kHeight); \
align_buffer_page_end(dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_v_c, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_y_opt, kWidth* kHeight); \
align_buffer_page_end(dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
align_buffer_page_end(dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X) * \
SUBSAMPLE(kHeight, SUBSAMP_Y)); \
kSizeUVSrc*((PIXEL_STRIDE == 3) ? 3 : 2) + OFF); \
align_buffer_page_end(dst_y_c, kWidth * kHeight); \
align_buffer_page_end(dst_u_c, kSizeUVDst); \
align_buffer_page_end(dst_v_c, kSizeUVDst); \
align_buffer_page_end(dst_y_baseline, kWidth * kHeight); \
align_buffer_page_end(dst_u_baseline, kSizeUVDst); \
align_buffer_page_end(dst_v_baseline, kSizeUVDst); \
align_buffer_page_end(dst_y_opt, kWidth * kHeight); \
align_buffer_page_end(dst_u_opt, kSizeUVDst); \
align_buffer_page_end(dst_v_opt, kSizeUVDst); \
uint8_t* src_u = src_uv + OFF_U; \
uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUV : OFF_V); \
int src_stride_uv = SUBSAMPLE(kWidth, SUBSAMP_X) * PIXEL_STRIDE; \
uint8_t* src_v = src_uv + (PIXEL_STRIDE == 1 ? kSizeUVSrc : OFF_V); \
int src_stride_uv = kStrideUVSrc * PIXEL_STRIDE; \
for (int i = 0; i < kHeight; ++i) \
for (int j = 0; j < kWidth; ++j) \
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)
(fastrand() & 0xff); \
} \
} \
memset(dst_y_c, 1, kWidth* kHeight); \
memset(dst_u_c, 2, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_v_c, 3, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_y_opt, 101, kWidth* kHeight); \
memset(dst_u_opt, 102, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_v_opt, 103, \
SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y)); \
memset(dst_y_c, 1, kWidth * kHeight); \
memset(dst_u_c, 2, kSizeUVDst); \
memset(dst_v_c, 3, kSizeUVDst); \
memset(dst_y_baseline, 51, kWidth * kHeight); \
memset(dst_u_baseline, 52, kSizeUVDst); \
memset(dst_v_baseline, 53, kSizeUVDst); \
memset(dst_y_opt, 101, kWidth * kHeight); \
memset(dst_u_opt, 102, kSizeUVDst); \
memset(dst_v_opt, 103, kSizeUVDst); \
MaskCpuFlags(disable_cpu_flags_); \
SRC_FMT_PLANAR##To##FMT_PLANAR( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, dst_y_c, \
kWidth, dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X), dst_v_c, \
SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight); \
src_y + OFF, kWidth, src_u + OFF, src_stride_uv, \
src_v + OFF, src_stride_uv, PIXEL_STRIDE, dst_y_c, \
kWidth, dst_u_c, kStrideUVDst, dst_v_c, \
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_); \
for (int i = 0; i < benchmark_iterations_; ++i) { \
SRC_FMT_PLANAR##To##FMT_PLANAR( \
src_y + OFF, kWidth, src_u + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \
src_v + OFF, SUBSAMPLE(kWidth, SRC_SUBSAMP_X), PIXEL_STRIDE, \
dst_y_opt, kWidth, dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \
dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X), kWidth, NEG kHeight); \
src_y + OFF, kWidth, src_u + OFF, src_stride_uv, \
src_v + OFF, src_stride_uv, PIXEL_STRIDE, \
dst_y_opt, kWidth, dst_u_opt, kStrideUVDst, \
dst_v_opt, kStrideUVDst, kWidth, NEG kHeight); \
} \
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 j = 0; j < 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)
free_aligned_buffer_page_end(dst_y_c); \
free_aligned_buffer_page_end(dst_u_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_u_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)
#define TESTAPLANARTOP(SRC_FMT_PLANAR, PN, PIXEL_STRIDE, OFF_U, OFF_V, \
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, \
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, \
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, \
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, \
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)
TESTAPLANARTOP(Android420, NV12, 2, 0, 1, 2, 2, I420, 2, 2)
TESTAPLANARTOP(Android420, NV21, 2, 1, 0, 2, 2, I420, 2, 2)
// I420ToI420 matching the NV12ToI420 signature, requires kSizeUVSrc in scope.
#define I420TOI420_I(src_y, src_stride_y, src_uv, src_stride_uv, \
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, \
FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \
......@@ -1424,85 +1477,216 @@ TEST_F(LibYUVConvertTest, MJPGToARGB) {
#endif // HAVE_JPEG
TEST_F(LibYUVConvertTest, NV12Crop) {
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 sample_size =
kWidth * kHeight + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y) * 2;
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) *
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) *
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 < (SUBSAMPLE(kHeight, SUBSAMP_Y) * kStrideUV) * 2; ++i) {
src_uv[i] = (fastrand() & 0xff);
}
memset(dst_y, 1, kDestWidth * kDestHeight);
memset(dst_u, 2,
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y));
memset(dst_v, 3,
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y));
memset(dst_y_2, 1, kDestWidth * kDestHeight);
memset(dst_u_2, 2,
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,
SUBSAMPLE(kDestWidth, SUBSAMP_X), 0, crop_y, kWidth, kHeight,
kDestWidth, kDestHeight, libyuv::kRotate0, libyuv::FOURCC_NV12);
NV12ToI420(src_y + crop_y * kWidth, kWidth,
src_uv + (crop_y / 2) * kStrideUV * 2, kStrideUV * 2, dst_y,
kDestWidth, dst_u, SUBSAMPLE(kDestWidth, SUBSAMP_X), dst_v,
SUBSAMPLE(kDestWidth, SUBSAMP_X), kDestWidth, kDestHeight);
for (int i = 0; i < kDestHeight; ++i) {
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) {
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) {
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_v);
free_aligned_buffer_page_end(dst_y_2);
free_aligned_buffer_page_end(dst_u_2);
free_aligned_buffer_page_end(dst_v_2);
free_aligned_buffer_page_end(src_y);
}
#define TESTTOI420(FMT, NEG, N) \
TEST_F(LibYUVConvertTest, ConvertToI420_##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 sample_size = \
kWidth * kHeight + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y) * 2; \
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) * \
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) * \
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 < (SUBSAMPLE(kHeight, SUBSAMP_Y) * kStrideUV) * 2; ++i) { \
src_uv[i] = (fastrand() & 0xff); \
} \
memset(dst_y, 1, kDestWidth * kDestHeight); \
memset(dst_u, 2, \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
memset(dst_v, 3, \
SUBSAMPLE(kDestWidth, SUBSAMP_X) * SUBSAMPLE(kDestHeight, SUBSAMP_Y)); \
memset(dst_y_2, 1, kDestWidth * kDestHeight); \
memset(dst_u_2, 2, \
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, \
SUBSAMPLE(kDestWidth, SUBSAMP_X), 0, crop_y, kWidth, NEG kHeight, \
kDestWidth, kDestHeight, libyuv::kRotate0, libyuv::FOURCC_##FMT); \
\
FMT##ToI420(src_y + crop_y * kWidth, kWidth, \
src_uv + (crop_y / 2) * kStrideUV * 2, kStrideUV * 2, dst_y, \
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) { \
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) { \
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) { \
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_v); \
free_aligned_buffer_page_end(dst_y_2); \
free_aligned_buffer_page_end(dst_u_2); \
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) {
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