diff options
Diffstat (limited to 'media/libyuv/unit_test')
-rw-r--r-- | media/libyuv/unit_test/basictypes_test.cc | 60 | ||||
-rw-r--r-- | media/libyuv/unit_test/color_test.cc | 570 | ||||
-rw-r--r-- | media/libyuv/unit_test/compare_test.cc | 513 | ||||
-rw-r--r-- | media/libyuv/unit_test/convert_test.cc | 1861 | ||||
-rw-r--r-- | media/libyuv/unit_test/cpu_test.cc | 139 | ||||
-rw-r--r-- | media/libyuv/unit_test/math_test.cc | 155 | ||||
-rw-r--r-- | media/libyuv/unit_test/planar_test.cc | 2361 | ||||
-rw-r--r-- | media/libyuv/unit_test/rotate_argb_test.cc | 196 | ||||
-rw-r--r-- | media/libyuv/unit_test/rotate_test.cc | 296 | ||||
-rw-r--r-- | media/libyuv/unit_test/scale_argb_test.cc | 462 | ||||
-rw-r--r-- | media/libyuv/unit_test/scale_test.cc | 372 | ||||
-rw-r--r-- | media/libyuv/unit_test/testdata/arm_v7.txt | 12 | ||||
-rw-r--r-- | media/libyuv/unit_test/testdata/juno.txt | 15 | ||||
-rw-r--r-- | media/libyuv/unit_test/testdata/tegra3.txt | 23 | ||||
-rw-r--r-- | media/libyuv/unit_test/unit_test.cc | 356 | ||||
-rw-r--r-- | media/libyuv/unit_test/unit_test.h | 199 | ||||
-rw-r--r-- | media/libyuv/unit_test/video_common_test.cc | 107 |
17 files changed, 7697 insertions, 0 deletions
diff --git a/media/libyuv/unit_test/basictypes_test.cc b/media/libyuv/unit_test/basictypes_test.cc new file mode 100644 index 000000000..89f7644d5 --- /dev/null +++ b/media/libyuv/unit_test/basictypes_test.cc @@ -0,0 +1,60 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "../unit_test/unit_test.h" +#include "libyuv/basic_types.h" + +namespace libyuv { + +TEST_F(LibYUVBaseTest, Endian) { + uint16 v16 = 0x1234u; + uint8 first_byte = *reinterpret_cast<uint8*>(&v16); +#if defined(LIBYUV_LITTLE_ENDIAN) + EXPECT_EQ(0x34u, first_byte); +#else + EXPECT_EQ(0x12u, first_byte); +#endif +} + +TEST_F(LibYUVBaseTest, SizeOfTypes) { + int8 i8 = -1; + uint8 u8 = 1u; + int16 i16 = -1; + uint16 u16 = 1u; + int32 i32 = -1; + uint32 u32 = 1u; + int64 i64 = -1; + uint64 u64 = 1u; + EXPECT_EQ(1u, sizeof(i8)); + EXPECT_EQ(1u, sizeof(u8)); + EXPECT_EQ(2u, sizeof(i16)); + EXPECT_EQ(2u, sizeof(u16)); + EXPECT_EQ(4u, sizeof(i32)); + EXPECT_EQ(4u, sizeof(u32)); + EXPECT_EQ(8u, sizeof(i64)); + EXPECT_EQ(8u, sizeof(u64)); + EXPECT_GT(0, i8); + EXPECT_LT(0u, u8); + EXPECT_GT(0, i16); + EXPECT_LT(0u, u16); + EXPECT_GT(0, i32); + EXPECT_LT(0u, u32); + EXPECT_GT(0, i64); + EXPECT_LT(0u, u64); +} + +TEST_F(LibYUVBaseTest, SizeOfConstants) { + EXPECT_EQ(8u, sizeof(INT64_C(0))); + EXPECT_EQ(8u, sizeof(UINT64_C(0))); + EXPECT_EQ(8u, sizeof(INT64_C(0x1234567887654321))); + EXPECT_EQ(8u, sizeof(UINT64_C(0x8765432112345678))); +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/color_test.cc b/media/libyuv/unit_test/color_test.cc new file mode 100644 index 000000000..36041d998 --- /dev/null +++ b/media/libyuv/unit_test/color_test.cc @@ -0,0 +1,570 @@ +/* + * Copyright 2015 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> + +#include "libyuv/basic_types.h" +#include "libyuv/convert.h" +#include "libyuv/convert_argb.h" +#include "libyuv/convert_from.h" +#include "libyuv/convert_from_argb.h" +#include "libyuv/cpu_id.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +// TODO(fbarchard): Port high accuracy YUV to RGB to Neon. +#if !defined(LIBYUV_DISABLE_NEON) && \ + (defined(__aarch64__) || defined(__ARM_NEON__) || defined(LIBYUV_NEON)) +#define ERROR_R 1 +#define ERROR_G 1 +#define ERROR_B 3 +#define ERROR_FULL 6 +#define ERROR_J420 5 +#else +#define ERROR_R 1 +#define ERROR_G 1 +#define ERROR_B 3 +#define ERROR_FULL 5 +#define ERROR_J420 3 +#endif + +#define TESTCS(TESTNAME, YUVTOARGB, ARGBTOYUV, HS1, HS, HN, DIFF) \ + TEST_F(LibYUVColorTest, TESTNAME) { \ + const int kPixels = benchmark_width_ * benchmark_height_; \ + const int kHalfPixels = ((benchmark_width_ + 1) / 2) * \ + ((benchmark_height_ + HS1) / HS); \ + align_buffer_page_end(orig_y, kPixels); \ + align_buffer_page_end(orig_u, kHalfPixels); \ + align_buffer_page_end(orig_v, kHalfPixels); \ + align_buffer_page_end(orig_pixels, kPixels * 4); \ + align_buffer_page_end(temp_y, kPixels); \ + align_buffer_page_end(temp_u, kHalfPixels); \ + align_buffer_page_end(temp_v, kHalfPixels); \ + align_buffer_page_end(dst_pixels_opt, kPixels * 4); \ + align_buffer_page_end(dst_pixels_c, kPixels * 4); \ + \ + MemRandomize(orig_pixels, kPixels * 4); \ + MemRandomize(orig_y, kPixels); \ + MemRandomize(orig_u, kHalfPixels); \ + MemRandomize(orig_v, kHalfPixels); \ + MemRandomize(temp_y, kPixels); \ + MemRandomize(temp_u, kHalfPixels); \ + MemRandomize(temp_v, kHalfPixels); \ + MemRandomize(dst_pixels_opt, kPixels * 4); \ + MemRandomize(dst_pixels_c, kPixels * 4); \ + \ + /* The test is overall for color conversion matrix being reversible, so */ \ + /* this initializes the pixel with 2x2 blocks to eliminate subsampling. */ \ + uint8* p = orig_y; \ + for (int y = 0; y < benchmark_height_ - HS1; y += HS) { \ + for (int x = 0; x < benchmark_width_ - 1; x += 2) { \ + uint8 r = static_cast<uint8>(fastrand()); \ + p[0] = r; \ + p[1] = r; \ + p[HN] = r; \ + p[HN + 1] = r; \ + p += 2; \ + } \ + if (benchmark_width_ & 1) { \ + uint8 r = static_cast<uint8>(fastrand()); \ + p[0] = r; \ + p[HN] = r; \ + p += 1; \ + } \ + p += HN; \ + } \ + if ((benchmark_height_ & 1) && HS == 2) { \ + for (int x = 0; x < benchmark_width_ - 1; x += 2) { \ + uint8 r = static_cast<uint8>(fastrand()); \ + p[0] = r; \ + p[1] = r; \ + p += 2; \ + } \ + if (benchmark_width_ & 1) { \ + uint8 r = static_cast<uint8>(fastrand()); \ + p[0] = r; \ + p += 1; \ + } \ + } \ + /* Start with YUV converted to ARGB. */ \ + YUVTOARGB(orig_y, benchmark_width_, \ + orig_u, (benchmark_width_ + 1) / 2, \ + orig_v, (benchmark_width_ + 1) / 2, \ + orig_pixels, benchmark_width_ * 4, \ + benchmark_width_, benchmark_height_); \ + \ + ARGBTOYUV(orig_pixels, benchmark_width_ * 4, \ + temp_y, benchmark_width_, \ + temp_u, (benchmark_width_ + 1) / 2, \ + temp_v, (benchmark_width_ + 1) / 2, \ + benchmark_width_, benchmark_height_); \ + \ + MaskCpuFlags(disable_cpu_flags_); \ + YUVTOARGB(temp_y, benchmark_width_, \ + temp_u, (benchmark_width_ + 1) / 2, \ + temp_v, (benchmark_width_ + 1) / 2, \ + dst_pixels_c, benchmark_width_ * 4, \ + benchmark_width_, benchmark_height_); \ + MaskCpuFlags(benchmark_cpu_info_); \ + \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + YUVTOARGB(temp_y, benchmark_width_, \ + temp_u, (benchmark_width_ + 1) / 2, \ + temp_v, (benchmark_width_ + 1) / 2, \ + dst_pixels_opt, benchmark_width_ * 4, \ + benchmark_width_, benchmark_height_); \ + } \ + /* Test C and SIMD match. */ \ + for (int i = 0; i < kPixels * 4; ++i) { \ + EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]); \ + } \ + /* Test SIMD is close to original. */ \ + for (int i = 0; i < kPixels * 4; ++i) { \ + EXPECT_NEAR(static_cast<int>(orig_pixels[i]), \ + static_cast<int>(dst_pixels_opt[i]), DIFF); \ + } \ + \ + free_aligned_buffer_page_end(orig_pixels); \ + free_aligned_buffer_page_end(orig_y); \ + free_aligned_buffer_page_end(orig_u); \ + free_aligned_buffer_page_end(orig_v); \ + free_aligned_buffer_page_end(temp_y); \ + free_aligned_buffer_page_end(temp_u); \ + free_aligned_buffer_page_end(temp_v); \ + free_aligned_buffer_page_end(dst_pixels_opt); \ + free_aligned_buffer_page_end(dst_pixels_c); \ +} \ + +TESTCS(TestI420, I420ToARGB, ARGBToI420, 1, 2, benchmark_width_, ERROR_FULL) +TESTCS(TestI422, I422ToARGB, ARGBToI422, 0, 1, 0, ERROR_FULL) +TESTCS(TestJ420, J420ToARGB, ARGBToJ420, 1, 2, benchmark_width_, ERROR_J420) +TESTCS(TestJ422, J422ToARGB, ARGBToJ422, 0, 1, 0, ERROR_J420) + +static void YUVToRGB(int y, int u, int v, int* r, int* g, int* b) { + const int kWidth = 16; + const int kHeight = 1; + const int kPixels = kWidth * kHeight; + const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2); + + SIMD_ALIGNED(uint8 orig_y[16]); + SIMD_ALIGNED(uint8 orig_u[8]); + SIMD_ALIGNED(uint8 orig_v[8]); + SIMD_ALIGNED(uint8 orig_pixels[16 * 4]); + memset(orig_y, y, kPixels); + memset(orig_u, u, kHalfPixels); + memset(orig_v, v, kHalfPixels); + + /* YUV converted to ARGB. */ + I422ToARGB(orig_y, kWidth, + orig_u, (kWidth + 1) / 2, + orig_v, (kWidth + 1) / 2, + orig_pixels, kWidth * 4, + kWidth, kHeight); + + *b = orig_pixels[0]; + *g = orig_pixels[1]; + *r = orig_pixels[2]; +} + +static void YUVJToRGB(int y, int u, int v, int* r, int* g, int* b) { + const int kWidth = 16; + const int kHeight = 1; + const int kPixels = kWidth * kHeight; + const int kHalfPixels = ((kWidth + 1) / 2) * ((kHeight + 1) / 2); + + SIMD_ALIGNED(uint8 orig_y[16]); + SIMD_ALIGNED(uint8 orig_u[8]); + SIMD_ALIGNED(uint8 orig_v[8]); + SIMD_ALIGNED(uint8 orig_pixels[16 * 4]); + memset(orig_y, y, kPixels); + memset(orig_u, u, kHalfPixels); + memset(orig_v, v, kHalfPixels); + + /* YUV converted to ARGB. */ + J422ToARGB(orig_y, kWidth, + orig_u, (kWidth + 1) / 2, + orig_v, (kWidth + 1) / 2, + orig_pixels, kWidth * 4, + kWidth, kHeight); + + *b = orig_pixels[0]; + *g = orig_pixels[1]; + *r = orig_pixels[2]; +} + +static void YToRGB(int y, int* r, int* g, int* b) { + const int kWidth = 16; + const int kHeight = 1; + const int kPixels = kWidth * kHeight; + + SIMD_ALIGNED(uint8 orig_y[16]); + SIMD_ALIGNED(uint8 orig_pixels[16 * 4]); + memset(orig_y, y, kPixels); + + /* YUV converted to ARGB. */ + I400ToARGB(orig_y, kWidth, orig_pixels, kWidth * 4, kWidth, kHeight); + + *b = orig_pixels[0]; + *g = orig_pixels[1]; + *r = orig_pixels[2]; +} + +static void YJToRGB(int y, int* r, int* g, int* b) { + const int kWidth = 16; + const int kHeight = 1; + const int kPixels = kWidth * kHeight; + + SIMD_ALIGNED(uint8 orig_y[16]); + SIMD_ALIGNED(uint8 orig_pixels[16 * 4]); + memset(orig_y, y, kPixels); + + /* YUV converted to ARGB. */ + J400ToARGB(orig_y, kWidth, orig_pixels, kWidth * 4, kWidth, kHeight); + + *b = orig_pixels[0]; + *g = orig_pixels[1]; + *r = orig_pixels[2]; +} + +// Pick a method for clamping. +// #define CLAMPMETHOD_IF 1 +// #define CLAMPMETHOD_TABLE 1 +#define CLAMPMETHOD_TERNARY 1 +// #define CLAMPMETHOD_MASK 1 + +// Pick a method for rounding. +#define ROUND(f) static_cast<int>(f + 0.5f) +// #define ROUND(f) lrintf(f) +// #define ROUND(f) static_cast<int>(round(f)) +// #define ROUND(f) _mm_cvt_ss2si(_mm_load_ss(&f)) + +#if defined(CLAMPMETHOD_IF) +static int RoundToByte(float f) { + int i = ROUND(f); + if (i < 0) { + i = 0; + } + if (i > 255) { + i = 255; + } + return i; +} +#elif defined(CLAMPMETHOD_TABLE) +static const unsigned char clamptable[811] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, + 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, + 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, + 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, + 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, + 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + +static int RoundToByte(float f) { + return clamptable[ROUND(f) + 276]; +} +#elif defined(CLAMPMETHOD_TERNARY) +static int RoundToByte(float f) { + int i = ROUND(f); + return (i < 0) ? 0 : ((i > 255) ? 255 : i); +} +#elif defined(CLAMPMETHOD_MASK) +static int RoundToByte(float f) { + int i = ROUND(f); + i = ((-(i) >> 31) & (i)); // clamp to 0. + return (((255 - (i)) >> 31) | (i)) & 255; // clamp to 255. +} +#endif + +#define RANDOM256(s) ((s & 1) ? ((s >> 1) ^ 0xb8) : (s >> 1)) + +TEST_F(LibYUVColorTest, TestRoundToByte) { + int allb = 0; + int count = benchmark_width_ * benchmark_height_; + for (int i = 0; i < benchmark_iterations_; ++i) { + float f = (fastrand() & 255) * 3.14f - 260.f; + for (int j = 0; j < count; ++j) { + int b = RoundToByte(f); + f += 0.91f; + allb |= b; + } + } + EXPECT_GE(allb, 0); + EXPECT_LE(allb, 255); +} + +static void YUVToRGBReference(int y, int u, int v, int* r, int* g, int* b) { + *r = RoundToByte((y - 16) * 1.164 - (v - 128) * -1.596); + *g = RoundToByte((y - 16) * 1.164 - (u - 128) * 0.391 - (v - 128) * 0.813); + *b = RoundToByte((y - 16) * 1.164 - (u - 128) * -2.018); +} + +static void YUVJToRGBReference(int y, int u, int v, int* r, int* g, int* b) { + *r = RoundToByte(y - (v - 128) * -1.40200); + *g = RoundToByte(y - (u - 128) * 0.34414 - (v - 128) * 0.71414); + *b = RoundToByte(y - (u - 128) * -1.77200); +} + +TEST_F(LibYUVColorTest, TestYUV) { + int r0, g0, b0, r1, g1, b1; + + // cyan (less red) + YUVToRGBReference(240, 255, 0, &r0, &g0, &b0); + EXPECT_EQ(56, r0); + EXPECT_EQ(255, g0); + EXPECT_EQ(255, b0); + + YUVToRGB(240, 255, 0, &r1, &g1, &b1); + EXPECT_EQ(57, r1); + EXPECT_EQ(255, g1); + EXPECT_EQ(255, b1); + + // green (less red and blue) + YUVToRGBReference(240, 0, 0, &r0, &g0, &b0); + EXPECT_EQ(56, r0); + EXPECT_EQ(255, g0); + EXPECT_EQ(2, b0); + + YUVToRGB(240, 0, 0, &r1, &g1, &b1); + EXPECT_EQ(57, r1); + EXPECT_EQ(255, g1); + EXPECT_EQ(5, b1); + + for (int i = 0; i < 256; ++i) { + YUVToRGBReference(i, 128, 128, &r0, &g0, &b0); + YUVToRGB(i, 128, 128, &r1, &g1, &b1); + EXPECT_NEAR(r0, r1, ERROR_R); + EXPECT_NEAR(g0, g1, ERROR_G); + EXPECT_NEAR(b0, b1, ERROR_B); + + YUVToRGBReference(i, 0, 0, &r0, &g0, &b0); + YUVToRGB(i, 0, 0, &r1, &g1, &b1); + EXPECT_NEAR(r0, r1, ERROR_R); + EXPECT_NEAR(g0, g1, ERROR_G); + EXPECT_NEAR(b0, b1, ERROR_B); + + YUVToRGBReference(i, 0, 255, &r0, &g0, &b0); + YUVToRGB(i, 0, 255, &r1, &g1, &b1); + EXPECT_NEAR(r0, r1, ERROR_R); + EXPECT_NEAR(g0, g1, ERROR_G); + EXPECT_NEAR(b0, b1, ERROR_B); + } +} + +TEST_F(LibYUVColorTest, TestGreyYUV) { + int r0, g0, b0, r1, g1, b1, r2, g2, b2; + + // black + YUVToRGBReference(16, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(0, r0); + EXPECT_EQ(0, g0); + EXPECT_EQ(0, b0); + + YUVToRGB(16, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(0, r1); + EXPECT_EQ(0, g1); + EXPECT_EQ(0, b1); + + // white + YUVToRGBReference(240, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(255, r0); + EXPECT_EQ(255, g0); + EXPECT_EQ(255, b0); + + YUVToRGB(240, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(255, r1); + EXPECT_EQ(255, g1); + EXPECT_EQ(255, b1); + + // grey + YUVToRGBReference(128, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(130, r0); + EXPECT_EQ(130, g0); + EXPECT_EQ(130, b0); + + YUVToRGB(128, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(130, r1); + EXPECT_EQ(130, g1); + EXPECT_EQ(130, b1); + + + for (int y = 0; y < 256; ++y) { + YUVToRGBReference(y, 128, 128, &r0, &g0, &b0); + YUVToRGB(y, 128, 128, &r1, &g1, &b1); + YToRGB(y, &r2, &g2, &b2); + EXPECT_EQ(r0, r1); + EXPECT_EQ(g0, g1); + EXPECT_EQ(b0, b1); + EXPECT_EQ(r0, r2); + EXPECT_EQ(g0, g2); + EXPECT_EQ(b0, b2); + } +} + +static void PrintHistogram(int rh[256], int gh[256], int bh[256]) { + int i; + printf("hist"); + for (i = 0; i < 256; ++i) { + if (rh[i] || gh[i] || bh[i]) { + printf("\t%8d", i - 128); + } + } + printf("\nred"); + for (i = 0; i < 256; ++i) { + if (rh[i] || gh[i] || bh[i]) { + printf("\t%8d", rh[i]); + } + } + printf("\ngreen"); + for (i = 0; i < 256; ++i) { + if (rh[i] || gh[i] || bh[i]) { + printf("\t%8d", gh[i]); + } + } + printf("\nblue"); + for (i = 0; i < 256; ++i) { + if (rh[i] || gh[i] || bh[i]) { + printf("\t%8d", bh[i]); + } + } + printf("\n"); +} + +TEST_F(LibYUVColorTest, TestFullYUV) { + int rh[256] = { 0, }, gh[256] = { 0, }, bh[256] = { 0, }; + for (int u = 0; u < 256; ++u) { + for (int v = 0; v < 256; ++v) { + for (int y2 = 0; y2 < 256; ++y2) { + int r0, g0, b0, r1, g1, b1; + int y = RANDOM256(y2); + YUVToRGBReference(y, u, v, &r0, &g0, &b0); + YUVToRGB(y, u, v, &r1, &g1, &b1); + EXPECT_NEAR(r0, r1, ERROR_R); + EXPECT_NEAR(g0, g1, ERROR_G); + EXPECT_NEAR(b0, b1, ERROR_B); + ++rh[r1 - r0 + 128]; + ++gh[g1 - g0 + 128]; + ++bh[b1 - b0 + 128]; + } + } + } + PrintHistogram(rh, gh, bh); +} + +TEST_F(LibYUVColorTest, TestFullYUVJ) { + int rh[256] = { 0, }, gh[256] = { 0, }, bh[256] = { 0, }; + for (int u = 0; u < 256; ++u) { + for (int v = 0; v < 256; ++v) { + for (int y2 = 0; y2 < 256; ++y2) { + int r0, g0, b0, r1, g1, b1; + int y = RANDOM256(y2); + YUVJToRGBReference(y, u, v, &r0, &g0, &b0); + YUVJToRGB(y, u, v, &r1, &g1, &b1); + EXPECT_NEAR(r0, r1, 1); + EXPECT_NEAR(g0, g1, 1); + EXPECT_NEAR(b0, b1, 1); + ++rh[r1 - r0 + 128]; + ++gh[g1 - g0 + 128]; + ++bh[b1 - b0 + 128]; + } + } + } + PrintHistogram(rh, gh, bh); +} + +TEST_F(LibYUVColorTest, TestGreyYUVJ) { + int r0, g0, b0, r1, g1, b1, r2, g2, b2; + + // black + YUVJToRGBReference(0, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(0, r0); + EXPECT_EQ(0, g0); + EXPECT_EQ(0, b0); + + YUVJToRGB(0, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(0, r1); + EXPECT_EQ(0, g1); + EXPECT_EQ(0, b1); + + // white + YUVJToRGBReference(255, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(255, r0); + EXPECT_EQ(255, g0); + EXPECT_EQ(255, b0); + + YUVJToRGB(255, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(255, r1); + EXPECT_EQ(255, g1); + EXPECT_EQ(255, b1); + + // grey + YUVJToRGBReference(128, 128, 128, &r0, &g0, &b0); + EXPECT_EQ(128, r0); + EXPECT_EQ(128, g0); + EXPECT_EQ(128, b0); + + YUVJToRGB(128, 128, 128, &r1, &g1, &b1); + EXPECT_EQ(128, r1); + EXPECT_EQ(128, g1); + EXPECT_EQ(128, b1); + + for (int y = 0; y < 256; ++y) { + YUVJToRGBReference(y, 128, 128, &r0, &g0, &b0); + YUVJToRGB(y, 128, 128, &r1, &g1, &b1); + YJToRGB(y, &r2, &g2, &b2); + EXPECT_EQ(r0, r1); + EXPECT_EQ(g0, g1); + EXPECT_EQ(b0, b1); + EXPECT_EQ(r0, r2); + EXPECT_EQ(g0, g2); + EXPECT_EQ(b0, b2); + } +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/compare_test.cc b/media/libyuv/unit_test/compare_test.cc new file mode 100644 index 000000000..a8ce671d6 --- /dev/null +++ b/media/libyuv/unit_test/compare_test.cc @@ -0,0 +1,513 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "../unit_test/unit_test.h" +#include "libyuv/basic_types.h" +#include "libyuv/compare.h" +#include "libyuv/cpu_id.h" +#include "libyuv/video_common.h" + +namespace libyuv { + +// hash seed of 5381 recommended. +static uint32 ReferenceHashDjb2(const uint8* src, uint64 count, uint32 seed) { + uint32 hash = seed; + if (count > 0) { + do { + hash = hash * 33 + *src++; + } while (--count); + } + return hash; +} + +TEST_F(LibYUVBaseTest, Djb2_Test) { + const int kMaxTest = benchmark_width_ * benchmark_height_; + align_buffer_page_end(src_a, kMaxTest); + align_buffer_page_end(src_b, kMaxTest); + + const char* fox = "The quick brown fox jumps over the lazy dog" + " and feels as if he were in the seventh heaven of typography" + " together with Hermann Zapf"; + uint32 foxhash = HashDjb2(reinterpret_cast<const uint8*>(fox), 131, 5381); + const uint32 kExpectedFoxHash = 2611006483u; + EXPECT_EQ(kExpectedFoxHash, foxhash); + + for (int i = 0; i < kMaxTest; ++i) { + src_a[i] = (fastrand() & 0xff); + src_b[i] = (fastrand() & 0xff); + } + // Compare different buffers. Expect hash is different. + uint32 h1 = HashDjb2(src_a, kMaxTest, 5381); + uint32 h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make last half same. Expect hash is different. + memcpy(src_a + kMaxTest / 2, src_b + kMaxTest / 2, kMaxTest / 2); + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make first half same. Expect hash is different. + memcpy(src_a + kMaxTest / 2, src_a, kMaxTest / 2); + memcpy(src_b + kMaxTest / 2, src_b, kMaxTest / 2); + memcpy(src_a, src_b, kMaxTest / 2); + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make same. Expect hash is same. + memcpy(src_a, src_b, kMaxTest); + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_EQ(h1, h2); + + // Mask seed different. Expect hash is different. + memcpy(src_a, src_b, kMaxTest); + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 1234); + EXPECT_NE(h1, h2); + + // Make one byte different in middle. Expect hash is different. + memcpy(src_a, src_b, kMaxTest); + ++src_b[kMaxTest / 2]; + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make first byte different. Expect hash is different. + memcpy(src_a, src_b, kMaxTest); + ++src_b[0]; + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make last byte different. Expect hash is different. + memcpy(src_a, src_b, kMaxTest); + ++src_b[kMaxTest - 1]; + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_b, kMaxTest, 5381); + EXPECT_NE(h1, h2); + + // Make a zeros. Test different lengths. Expect hash is different. + memset(src_a, 0, kMaxTest); + h1 = HashDjb2(src_a, kMaxTest, 5381); + h2 = HashDjb2(src_a, kMaxTest / 2, 5381); + EXPECT_NE(h1, h2); + + // Make a zeros and seed of zero. Test different lengths. Expect hash is same. + memset(src_a, 0, kMaxTest); + h1 = HashDjb2(src_a, kMaxTest, 0); + h2 = HashDjb2(src_a, kMaxTest / 2, 0); + EXPECT_EQ(h1, h2); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, BenchmarkDjb2_Opt) { + const int kMaxTest = benchmark_width_ * benchmark_height_; + align_buffer_page_end(src_a, kMaxTest); + + for (int i = 0; i < kMaxTest; ++i) { + src_a[i] = i; + } + uint32 h2 = ReferenceHashDjb2(src_a, kMaxTest, 5381); + uint32 h1; + for (int i = 0; i < benchmark_iterations_; ++i) { + h1 = HashDjb2(src_a, kMaxTest, 5381); + } + EXPECT_EQ(h1, h2); + free_aligned_buffer_page_end(src_a); +} + +TEST_F(LibYUVBaseTest, BenchmarkDjb2_Unaligned) { + const int kMaxTest = benchmark_width_ * benchmark_height_; + align_buffer_page_end(src_a, kMaxTest + 1); + for (int i = 0; i < kMaxTest; ++i) { + src_a[i + 1] = i; + } + uint32 h2 = ReferenceHashDjb2(src_a + 1, kMaxTest, 5381); + uint32 h1; + for (int i = 0; i < benchmark_iterations_; ++i) { + h1 = HashDjb2(src_a + 1, kMaxTest, 5381); + } + EXPECT_EQ(h1, h2); + free_aligned_buffer_page_end(src_a); +} + +TEST_F(LibYUVBaseTest, BenchmarkARGBDetect_Opt) { + uint32 fourcc; + const int kMaxTest = benchmark_width_ * benchmark_height_ * 4; + align_buffer_page_end(src_a, kMaxTest); + for (int i = 0; i < kMaxTest; ++i) { + src_a[i] = 255; + } + + src_a[0] = 0; + fourcc = ARGBDetect(src_a, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + EXPECT_EQ(libyuv::FOURCC_BGRA, fourcc); + src_a[0] = 255; + src_a[3] = 0; + fourcc = ARGBDetect(src_a, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + EXPECT_EQ(libyuv::FOURCC_ARGB, fourcc); + src_a[3] = 255; + + for (int i = 0; i < benchmark_iterations_; ++i) { + fourcc = ARGBDetect(src_a, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + } + EXPECT_EQ(0, fourcc); + + free_aligned_buffer_page_end(src_a); +} + +TEST_F(LibYUVBaseTest, BenchmarkARGBDetect_Unaligned) { + uint32 fourcc; + const int kMaxTest = benchmark_width_ * benchmark_height_ * 4 + 1; + align_buffer_page_end(src_a, kMaxTest); + for (int i = 1; i < kMaxTest; ++i) { + src_a[i] = 255; + } + + src_a[0 + 1] = 0; + fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + EXPECT_EQ(libyuv::FOURCC_BGRA, fourcc); + src_a[0 + 1] = 255; + src_a[3 + 1] = 0; + fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + EXPECT_EQ(libyuv::FOURCC_ARGB, fourcc); + src_a[3 + 1] = 255; + + for (int i = 0; i < benchmark_iterations_; ++i) { + fourcc = ARGBDetect(src_a + 1, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + } + EXPECT_EQ(0, fourcc); + + free_aligned_buffer_page_end(src_a); +} +TEST_F(LibYUVBaseTest, BenchmarkSumSquareError_Opt) { + const int kMaxWidth = 4096 * 3; + align_buffer_page_end(src_a, kMaxWidth); + align_buffer_page_end(src_b, kMaxWidth); + memset(src_a, 0, kMaxWidth); + memset(src_b, 0, kMaxWidth); + + memcpy(src_a, "test0123test4567", 16); + memcpy(src_b, "tick0123tock4567", 16); + uint64 h1 = ComputeSumSquareError(src_a, src_b, 16); + EXPECT_EQ(790u, h1); + + for (int i = 0; i < kMaxWidth; ++i) { + src_a[i] = i; + src_b[i] = i; + } + memset(src_a, 0, kMaxWidth); + memset(src_b, 0, kMaxWidth); + + int count = benchmark_iterations_ * + ((benchmark_width_ * benchmark_height_ + kMaxWidth - 1) / kMaxWidth); + for (int i = 0; i < count; ++i) { + h1 = ComputeSumSquareError(src_a, src_b, kMaxWidth); + } + + EXPECT_EQ(0, h1); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, SumSquareError) { + const int kMaxWidth = 4096 * 3; + align_buffer_page_end(src_a, kMaxWidth); + align_buffer_page_end(src_b, kMaxWidth); + memset(src_a, 0, kMaxWidth); + memset(src_b, 0, kMaxWidth); + + uint64 err; + err = ComputeSumSquareError(src_a, src_b, kMaxWidth); + + EXPECT_EQ(0, err); + + memset(src_a, 1, kMaxWidth); + err = ComputeSumSquareError(src_a, src_b, kMaxWidth); + + EXPECT_EQ(err, kMaxWidth); + + memset(src_a, 190, kMaxWidth); + memset(src_b, 193, kMaxWidth); + err = ComputeSumSquareError(src_a, src_b, kMaxWidth); + + EXPECT_EQ(kMaxWidth * 3 * 3, err); + + for (int i = 0; i < kMaxWidth; ++i) { + src_a[i] = (fastrand() & 0xff); + src_b[i] = (fastrand() & 0xff); + } + + MaskCpuFlags(disable_cpu_flags_); + uint64 c_err = ComputeSumSquareError(src_a, src_b, kMaxWidth); + + MaskCpuFlags(benchmark_cpu_info_); + uint64 opt_err = ComputeSumSquareError(src_a, src_b, kMaxWidth); + + EXPECT_EQ(c_err, opt_err); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, BenchmarkPsnr_Opt) { + align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_); + align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_); + for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) { + src_a[i] = i; + src_b[i] = i; + } + + MaskCpuFlags(benchmark_cpu_info_); + + double opt_time = get_time(); + for (int i = 0; i < benchmark_iterations_; ++i) + CalcFramePsnr(src_a, benchmark_width_, + src_b, benchmark_width_, + benchmark_width_, benchmark_height_); + + opt_time = (get_time() - opt_time) / benchmark_iterations_; + printf("BenchmarkPsnr_Opt - %8.2f us opt\n", opt_time * 1e6); + + EXPECT_EQ(0, 0); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, BenchmarkPsnr_Unaligned) { + align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_ + 1); + align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_); + for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) { + src_a[i + 1] = i; + src_b[i] = i; + } + + MaskCpuFlags(benchmark_cpu_info_); + + double opt_time = get_time(); + for (int i = 0; i < benchmark_iterations_; ++i) + CalcFramePsnr(src_a + 1, benchmark_width_, + src_b, benchmark_width_, + benchmark_width_, benchmark_height_); + + opt_time = (get_time() - opt_time) / benchmark_iterations_; + printf("BenchmarkPsnr_Opt - %8.2f us opt\n", opt_time * 1e6); + + EXPECT_EQ(0, 0); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, Psnr) { + const int kSrcWidth = benchmark_width_; + const int kSrcHeight = benchmark_height_; + const int b = 128; + const int kSrcPlaneSize = (kSrcWidth + b * 2) * (kSrcHeight + b * 2); + const int kSrcStride = 2 * b + kSrcWidth; + align_buffer_page_end(src_a, kSrcPlaneSize); + align_buffer_page_end(src_b, kSrcPlaneSize); + memset(src_a, 0, kSrcPlaneSize); + memset(src_b, 0, kSrcPlaneSize); + + double err; + err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + EXPECT_EQ(err, kMaxPsnr); + + memset(src_a, 255, kSrcPlaneSize); + + err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + EXPECT_EQ(err, 0.0); + + memset(src_a, 1, kSrcPlaneSize); + + err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + EXPECT_GT(err, 48.0); + EXPECT_LT(err, 49.0); + + for (int i = 0; i < kSrcPlaneSize; ++i) { + src_a[i] = i; + } + + err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + EXPECT_GT(err, 2.0); + if (kSrcWidth * kSrcHeight >= 256) { + EXPECT_LT(err, 6.0); + } + + memset(src_a, 0, kSrcPlaneSize); + memset(src_b, 0, kSrcPlaneSize); + + for (int i = b; i < (kSrcHeight + b); ++i) { + for (int j = b; j < (kSrcWidth + b); ++j) { + src_a[(i * kSrcStride) + j] = (fastrand() & 0xff); + src_b[(i * kSrcStride) + j] = (fastrand() & 0xff); + } + } + + MaskCpuFlags(disable_cpu_flags_); + double c_err, opt_err; + + c_err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + MaskCpuFlags(benchmark_cpu_info_); + + opt_err = CalcFramePsnr(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + EXPECT_EQ(opt_err, c_err); + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, DISABLED_BenchmarkSsim_Opt) { + align_buffer_page_end(src_a, benchmark_width_ * benchmark_height_); + align_buffer_page_end(src_b, benchmark_width_ * benchmark_height_); + for (int i = 0; i < benchmark_width_ * benchmark_height_; ++i) { + src_a[i] = i; + src_b[i] = i; + } + + MaskCpuFlags(benchmark_cpu_info_); + + double opt_time = get_time(); + for (int i = 0; i < benchmark_iterations_; ++i) + CalcFrameSsim(src_a, benchmark_width_, + src_b, benchmark_width_, + benchmark_width_, benchmark_height_); + + opt_time = (get_time() - opt_time) / benchmark_iterations_; + printf("BenchmarkSsim_Opt - %8.2f us opt\n", opt_time * 1e6); + + EXPECT_EQ(0, 0); // Pass if we get this far. + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +TEST_F(LibYUVBaseTest, Ssim) { + const int kSrcWidth = benchmark_width_; + const int kSrcHeight = benchmark_height_; + const int b = 128; + const int kSrcPlaneSize = (kSrcWidth + b * 2) * (kSrcHeight + b * 2); + const int kSrcStride = 2 * b + kSrcWidth; + align_buffer_page_end(src_a, kSrcPlaneSize); + align_buffer_page_end(src_b, kSrcPlaneSize); + memset(src_a, 0, kSrcPlaneSize); + memset(src_b, 0, kSrcPlaneSize); + + if (kSrcWidth <=8 || kSrcHeight <= 8) { + printf("warning - Ssim size too small. Testing function executes.\n"); + } + + double err; + err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + if (kSrcWidth > 8 && kSrcHeight > 8) { + EXPECT_EQ(err, 1.0); + } + + memset(src_a, 255, kSrcPlaneSize); + + err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + if (kSrcWidth > 8 && kSrcHeight > 8) { + EXPECT_LT(err, 0.0001); + } + + memset(src_a, 1, kSrcPlaneSize); + + err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + if (kSrcWidth > 8 && kSrcHeight > 8) { + EXPECT_GT(err, 0.0001); + EXPECT_LT(err, 0.9); + } + + for (int i = 0; i < kSrcPlaneSize; ++i) { + src_a[i] = i; + } + + err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + if (kSrcWidth > 8 && kSrcHeight > 8) { + EXPECT_GT(err, 0.0); + EXPECT_LT(err, 0.01); + } + + for (int i = b; i < (kSrcHeight + b); ++i) { + for (int j = b; j < (kSrcWidth + b); ++j) { + src_a[(i * kSrcStride) + j] = (fastrand() & 0xff); + src_b[(i * kSrcStride) + j] = (fastrand() & 0xff); + } + } + + MaskCpuFlags(disable_cpu_flags_); + double c_err, opt_err; + + c_err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + MaskCpuFlags(benchmark_cpu_info_); + + opt_err = CalcFrameSsim(src_a + kSrcStride * b + b, kSrcStride, + src_b + kSrcStride * b + b, kSrcStride, + kSrcWidth, kSrcHeight); + + if (kSrcWidth > 8 && kSrcHeight > 8) { + EXPECT_EQ(opt_err, c_err); + } + + free_aligned_buffer_page_end(src_a); + free_aligned_buffer_page_end(src_b); +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/convert_test.cc b/media/libyuv/unit_test/convert_test.cc new file mode 100644 index 000000000..56a2bfd82 --- /dev/null +++ b/media/libyuv/unit_test/convert_test.cc @@ -0,0 +1,1861 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <time.h> + +#include "libyuv/basic_types.h" +#include "libyuv/compare.h" +#include "libyuv/convert.h" +#include "libyuv/convert_argb.h" +#include "libyuv/convert_from.h" +#include "libyuv/convert_from_argb.h" +#include "libyuv/cpu_id.h" +#ifdef HAVE_JPEG +#include "libyuv/mjpeg_decoder.h" +#endif +#include "libyuv/planar_functions.h" +#include "libyuv/rotate.h" +#include "libyuv/video_common.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +#define SUBSAMPLE(v, a) ((((v) + (a) - 1)) / (a)) + +#define TESTPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, SRC_FMT_PLANAR##To##FMT_PLANAR##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, \ + SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SRC_SUBSAMP_Y) + OFF); \ + align_buffer_page_end(src_v, \ + SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SRC_SUBSAMP_Y) + 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)); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kWidth; ++j) \ + src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); ++i) { \ + for (int j = 0; j < SUBSAMPLE(kWidth, SRC_SUBSAMP_X); ++j) { \ + src_u[(i * SUBSAMPLE(kWidth, SRC_SUBSAMP_X)) + j + OFF] = \ + (fastrand() & 0xff); \ + src_v[(i * SUBSAMPLE(kWidth, SRC_SUBSAMP_X)) + j + OFF] = \ + (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)); \ + 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), \ + dst_y_c, kWidth, \ + dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X), \ + dst_v_c, SUBSAMPLE(kWidth, SUBSAMP_X), \ + 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), \ + dst_y_opt, kWidth, \ + dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \ + dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \ + 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_opt[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_opt[i * \ + SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 3); \ + 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_opt[i * \ + SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 3); \ + 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_opt); \ + free_aligned_buffer_page_end(dst_u_opt); \ + free_aligned_buffer_page_end(dst_v_opt); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ +} + +#define TESTPLANARTOP(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y) \ + TESTPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ - 4, _Any, +, 0) \ + TESTPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Unaligned, +, 1) \ + TESTPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -, 0) \ + TESTPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +, 0) + +TESTPLANARTOP(I420, 2, 2, I420, 2, 2) +TESTPLANARTOP(I422, 2, 1, I420, 2, 2) +TESTPLANARTOP(I444, 1, 1, I420, 2, 2) +TESTPLANARTOP(I411, 4, 1, I420, 2, 2) +TESTPLANARTOP(I420, 2, 2, I422, 2, 1) +TESTPLANARTOP(I420, 2, 2, I444, 1, 1) +TESTPLANARTOP(I420, 2, 2, I411, 4, 1) +TESTPLANARTOP(I420, 2, 2, I420Mirror, 2, 2) +TESTPLANARTOP(I422, 2, 1, I422, 2, 1) +TESTPLANARTOP(I444, 1, 1, I444, 1, 1) + +#define TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, SRC_FMT_PLANAR##To##FMT_PLANAR##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, \ + SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SRC_SUBSAMP_Y) + OFF); \ + align_buffer_page_end(src_v, \ + SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SRC_SUBSAMP_Y) + OFF); \ + align_buffer_page_end(dst_y_c, kWidth * kHeight); \ + align_buffer_page_end(dst_uv_c, SUBSAMPLE(kWidth * 2, SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + align_buffer_page_end(dst_y_opt, kWidth * kHeight); \ + align_buffer_page_end(dst_uv_opt, SUBSAMPLE(kWidth * 2, SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kWidth; ++j) \ + src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); ++i) { \ + for (int j = 0; j < SUBSAMPLE(kWidth, SRC_SUBSAMP_X); ++j) { \ + src_u[(i * SUBSAMPLE(kWidth, SRC_SUBSAMP_X)) + j + OFF] = \ + (fastrand() & 0xff); \ + src_v[(i * SUBSAMPLE(kWidth, SRC_SUBSAMP_X)) + j + OFF] = \ + (fastrand() & 0xff); \ + } \ + } \ + memset(dst_y_c, 1, kWidth * kHeight); \ + memset(dst_uv_c, 2, SUBSAMPLE(kWidth * 2, SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_y_opt, 101, kWidth * kHeight); \ + memset(dst_uv_opt, 102, SUBSAMPLE(kWidth * 2, SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + 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), \ + dst_y_c, kWidth, \ + dst_uv_c, SUBSAMPLE(kWidth * 2, SUBSAMP_X), \ + 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), \ + dst_y_opt, kWidth, \ + dst_uv_opt, \ + SUBSAMPLE(kWidth * 2, SUBSAMP_X), \ + 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_opt[i * kWidth + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \ + for (int j = 0; j < SUBSAMPLE(kWidth * 2, SUBSAMP_X); ++j) { \ + int abs_diff = \ + abs(static_cast<int>(dst_uv_c[i * \ + SUBSAMPLE(kWidth * 2, SUBSAMP_X) + j]) - \ + static_cast<int>(dst_uv_opt[i * \ + SUBSAMPLE(kWidth * 2, SUBSAMP_X) + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + free_aligned_buffer_page_end(dst_y_c); \ + free_aligned_buffer_page_end(dst_uv_c); \ + free_aligned_buffer_page_end(dst_y_opt); \ + free_aligned_buffer_page_end(dst_uv_opt); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ +} + +#define TESTPLANARTOBP(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y) \ + TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ - 4, _Any, +, 0) \ + TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Unaligned, +, 1) \ + TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -, 0) \ + TESTPLANARTOBPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +, 0) + +TESTPLANARTOBP(I420, 2, 2, NV12, 2, 2) +TESTPLANARTOBP(I420, 2, 2, NV21, 2, 2) + +#define TESTBIPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, SRC_FMT_PLANAR##To##FMT_PLANAR##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_uv, 2 * SUBSAMPLE(kWidth, SRC_SUBSAMP_X) * \ + SUBSAMPLE(kHeight, SRC_SUBSAMP_Y) + 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)); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kWidth; ++j) \ + src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SRC_SUBSAMP_Y); ++i) { \ + for (int j = 0; j < 2 * SUBSAMPLE(kWidth, SRC_SUBSAMP_X); ++j) { \ + src_uv[(i * 2 * SUBSAMPLE(kWidth, SRC_SUBSAMP_X)) + j + OFF] = \ + (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)); \ + MaskCpuFlags(disable_cpu_flags_); \ + SRC_FMT_PLANAR##To##FMT_PLANAR(src_y + OFF, kWidth, \ + src_uv + OFF, \ + 2 * SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \ + dst_y_c, kWidth, \ + dst_u_c, SUBSAMPLE(kWidth, SUBSAMP_X), \ + dst_v_c, SUBSAMPLE(kWidth, SUBSAMP_X), \ + 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_uv + OFF, \ + 2 * SUBSAMPLE(kWidth, SRC_SUBSAMP_X), \ + dst_y_opt, kWidth, \ + dst_u_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \ + dst_v_opt, SUBSAMPLE(kWidth, SUBSAMP_X), \ + 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_opt[i * kWidth + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + 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_opt[i * \ + SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + 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_opt[i * \ + SUBSAMPLE(kWidth, SUBSAMP_X) + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 1); \ + 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_opt); \ + free_aligned_buffer_page_end(dst_u_opt); \ + free_aligned_buffer_page_end(dst_v_opt); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_uv); \ +} + +#define TESTBIPLANARTOP(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y) \ + TESTBIPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ - 4, _Any, +, 0) \ + TESTBIPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Unaligned, +, 1) \ + TESTBIPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -, 0) \ + TESTBIPLANARTOPI(SRC_FMT_PLANAR, SRC_SUBSAMP_X, SRC_SUBSAMP_Y, \ + FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +, 0) + +TESTBIPLANARTOP(NV12, 2, 2, I420, 2, 2) +TESTBIPLANARTOP(NV21, 2, 2, I420, 2, 2) + +#define ALIGNINT(V, ALIGN) (((V) + (ALIGN) - 1) / (ALIGN) * (ALIGN)) + +#define TESTPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, W1280, DIFF, N, NEG, OFF, FMT_C, BPP_C) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = ALIGNINT(benchmark_height_, YALIGN); \ + const int kStrideB = ALIGNINT(kWidth * BPP_B, ALIGN); \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, kSizeUV + OFF); \ + align_buffer_page_end(src_v, kSizeUV + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeight + OFF); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeight + OFF); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + src_y[i + OFF] = (fastrand() & 0xff); \ + } \ + for (int i = 0; i < kSizeUV; ++i) { \ + src_u[i + OFF] = (fastrand() & 0xff); \ + src_v[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c + OFF, 1, kStrideB * kHeight); \ + memset(dst_argb_opt + OFF, 101, kStrideB * kHeight); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_c + OFF, kStrideB, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_opt + OFF, kStrideB, \ + kWidth, NEG kHeight); \ + } \ + int max_diff = 0; \ + /* Convert to ARGB so 565 is expanded to bytes that can be compared. */ \ + align_buffer_page_end(dst_argb32_c, kWidth * BPP_C * kHeight); \ + align_buffer_page_end(dst_argb32_opt, kWidth * BPP_C * kHeight); \ + memset(dst_argb32_c, 2, kWidth * BPP_C * kHeight); \ + memset(dst_argb32_opt, 102, kWidth * BPP_C * kHeight); \ + FMT_B##To##FMT_C(dst_argb_c + OFF, kStrideB, \ + dst_argb32_c, kWidth * BPP_C , \ + kWidth, kHeight); \ + FMT_B##To##FMT_C(dst_argb_opt + OFF, kStrideB, \ + dst_argb32_opt, kWidth * BPP_C , \ + kWidth, kHeight); \ + for (int i = 0; i < kWidth * BPP_C * kHeight; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb32_c[i]) - \ + static_cast<int>(dst_argb32_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ + free_aligned_buffer_page_end(dst_argb32_c); \ + free_aligned_buffer_page_end(dst_argb32_opt); \ +} + +#define TESTPLANARTOB(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, DIFF, FMT_C, BPP_C) \ + TESTPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_ - 4, DIFF, _Any, +, 0, FMT_C, BPP_C) \ + TESTPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Unaligned, +, 1, FMT_C, BPP_C) \ + TESTPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Invert, -, 0, FMT_C, BPP_C) \ + TESTPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Opt, +, 0, FMT_C, BPP_C) + +TESTPLANARTOB(I420, 2, 2, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(J420, 2, 2, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(J420, 2, 2, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(H420, 2, 2, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(H420, 2, 2, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, BGRA, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, RGBA, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, RAW, 3, 3, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, RGB24, 3, 3, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, RGB565, 2, 2, 1, 9, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, ARGB1555, 2, 2, 1, 9, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, ARGB4444, 2, 2, 1, 17, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(J422, 2, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(J422, 2, 1, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(H422, 2, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(H422, 2, 1, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, BGRA, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, RGBA, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I411, 4, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I444, 1, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(J444, 1, 1, ARGB, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I444, 1, 1, ABGR, 4, 4, 1, 2, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, YUY2, 2, 4, 1, 1, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, UYVY, 2, 4, 1, 1, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, YUY2, 2, 4, 1, 0, ARGB, 4) +TESTPLANARTOB(I422, 2, 1, UYVY, 2, 4, 1, 0, ARGB, 4) +TESTPLANARTOB(I420, 2, 2, I400, 1, 1, 1, 0, ARGB, 4) +TESTPLANARTOB(J420, 2, 2, J400, 1, 1, 1, 0, ARGB, 4) + +#define TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, W1280, DIFF, N, NEG, OFF, ATTEN) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = ALIGNINT(benchmark_height_, YALIGN); \ + const int kStrideB = ALIGNINT(kWidth * BPP_B, ALIGN); \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, kSizeUV + OFF); \ + align_buffer_page_end(src_v, kSizeUV + OFF); \ + align_buffer_page_end(src_a, kWidth * kHeight + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeight + OFF); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeight + OFF); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + src_y[i + OFF] = (fastrand() & 0xff); \ + src_a[i + OFF] = (fastrand() & 0xff); \ + } \ + for (int i = 0; i < kSizeUV; ++i) { \ + src_u[i + OFF] = (fastrand() & 0xff); \ + src_v[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c + OFF, 1, kStrideB * kHeight); \ + memset(dst_argb_opt + OFF, 101, kStrideB * kHeight); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + src_a + OFF, kWidth, \ + dst_argb_c + OFF, kStrideB, \ + kWidth, NEG kHeight, ATTEN); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + src_a + OFF, kWidth, \ + dst_argb_opt + OFF, kStrideB, \ + kWidth, NEG kHeight, ATTEN); \ + } \ + int max_diff = 0; \ + for (int i = 0; i < kWidth * BPP_B * kHeight; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb_c[i + OFF]) - \ + static_cast<int>(dst_argb_opt[i + OFF])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(src_a); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ +} + +#define TESTQPLANARTOB(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, DIFF) \ + TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_ - 4, DIFF, _Any, +, 0, 0) \ + TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Unaligned, +, 1, 0) \ + TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Invert, -, 0, 0) \ + TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Opt, +, 0, 0) \ + TESTQPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Premult, +, 0, 1) + +TESTQPLANARTOB(I420Alpha, 2, 2, ARGB, 4, 4, 1, 2) +TESTQPLANARTOB(I420Alpha, 2, 2, ABGR, 4, 4, 1, 2) + +#define TESTBIPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, \ + W1280, DIFF, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kStrideB = kWidth * BPP_B; \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_uv, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y) * 2 + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeight); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeight); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kWidth; ++j) \ + src_y[i * kWidth + j + OFF] = (fastrand() & 0xff); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \ + for (int j = 0; j < kStrideUV * 2; ++j) { \ + src_uv[i * kStrideUV * 2 + j + OFF] = (fastrand() & 0xff); \ + } \ + } \ + memset(dst_argb_c, 1, kStrideB * kHeight); \ + memset(dst_argb_opt, 101, kStrideB * kHeight); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_uv + OFF, kStrideUV * 2, \ + dst_argb_c, kWidth * BPP_B, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_uv + OFF, kStrideUV * 2, \ + dst_argb_opt, kWidth * BPP_B, \ + kWidth, NEG kHeight); \ + } \ + /* Convert to ARGB so 565 is expanded to bytes that can be compared. */ \ + align_buffer_page_end(dst_argb32_c, kWidth * 4 * kHeight); \ + align_buffer_page_end(dst_argb32_opt, kWidth * 4 * kHeight); \ + memset(dst_argb32_c, 2, kWidth * 4 * kHeight); \ + memset(dst_argb32_opt, 102, kWidth * 4 * kHeight); \ + FMT_B##ToARGB(dst_argb_c, kStrideB, \ + dst_argb32_c, kWidth * 4, \ + kWidth, kHeight); \ + FMT_B##ToARGB(dst_argb_opt, kStrideB, \ + dst_argb32_opt, kWidth * 4, \ + kWidth, kHeight); \ + int max_diff = 0; \ + for (int i = 0; i < kHeight; ++i) { \ + for (int j = 0; j < kWidth * 4; ++j) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb32_c[i * kWidth * 4 + j]) - \ + static_cast<int>(dst_argb32_opt[i * kWidth * 4 + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_uv); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ + free_aligned_buffer_page_end(dst_argb32_c); \ + free_aligned_buffer_page_end(dst_argb32_opt); \ +} + +#define TESTBIPLANARTOB(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, DIFF) \ + TESTBIPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, \ + benchmark_width_ - 4, DIFF, _Any, +, 0) \ + TESTBIPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, \ + benchmark_width_, DIFF, _Unaligned, +, 1) \ + TESTBIPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, \ + benchmark_width_, DIFF, _Invert, -, 0) \ + TESTBIPLANARTOBI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, \ + benchmark_width_, DIFF, _Opt, +, 0) + +TESTBIPLANARTOB(NV12, 2, 2, ARGB, 4, 2) +TESTBIPLANARTOB(NV21, 2, 2, ARGB, 4, 2) +TESTBIPLANARTOB(NV12, 2, 2, RGB565, 2, 9) + +#define TESTATOPLANARI(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + W1280, DIFF, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_PLANAR##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = ALIGNINT(benchmark_height_, YALIGN); \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kStride = \ + (kStrideUV * SUBSAMP_X * 8 * BPP_A + 7) / 8; \ + align_buffer_page_end(src_argb, kStride * kHeight + OFF); \ + align_buffer_page_end(dst_y_c, kWidth * kHeight); \ + align_buffer_page_end(dst_u_c, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + align_buffer_page_end(dst_v_c, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + align_buffer_page_end(dst_y_opt, kWidth * kHeight); \ + align_buffer_page_end(dst_u_opt, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + align_buffer_page_end(dst_v_opt, \ + kStrideUV * \ + SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_y_c, 1, kWidth * kHeight); \ + memset(dst_u_c, 2, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_v_c, 3, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_y_opt, 101, kWidth * kHeight); \ + memset(dst_u_opt, 102, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_v_opt, 103, \ + kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kStride; ++j) \ + src_argb[(i * kStride) + j + OFF] = (fastrand() & 0xff); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_PLANAR(src_argb + OFF, kStride, \ + dst_y_c, kWidth, \ + dst_u_c, kStrideUV, \ + dst_v_c, kStrideUV, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_A##To##FMT_PLANAR(src_argb + OFF, kStride, \ + dst_y_opt, kWidth, \ + dst_u_opt, kStrideUV, \ + dst_v_opt, kStrideUV, \ + kWidth, NEG kHeight); \ + } \ + for (int i = 0; i < kHeight; ++i) { \ + for (int j = 0; j < kWidth; ++j) { \ + EXPECT_NEAR(static_cast<int>(dst_y_c[i * kWidth + j]), \ + static_cast<int>(dst_y_opt[i * kWidth + j]), DIFF); \ + } \ + } \ + for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \ + for (int j = 0; j < kStrideUV; ++j) { \ + EXPECT_NEAR(static_cast<int>(dst_u_c[i * kStrideUV + j]), \ + static_cast<int>(dst_u_opt[i * kStrideUV + j]), DIFF); \ + } \ + } \ + for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \ + for (int j = 0; j < kStrideUV; ++j) { \ + EXPECT_NEAR(static_cast<int>(dst_v_c[i * \ + kStrideUV + j]), \ + static_cast<int>(dst_v_opt[i * \ + kStrideUV + j]), DIFF); \ + } \ + } \ + 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_opt); \ + free_aligned_buffer_page_end(dst_u_opt); \ + free_aligned_buffer_page_end(dst_v_opt); \ + free_aligned_buffer_page_end(src_argb); \ +} + +#define TESTATOPLANAR(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + DIFF) \ + TESTATOPLANARI(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ - 4, DIFF, _Any, +, 0) \ + TESTATOPLANARI(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, DIFF, _Unaligned, +, 1) \ + TESTATOPLANARI(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, DIFF, _Invert, -, 0) \ + TESTATOPLANARI(FMT_A, BPP_A, YALIGN, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, DIFF, _Opt, +, 0) + +TESTATOPLANAR(ARGB, 4, 1, I420, 2, 2, 4) +#if defined(__arm__) || defined (__aarch64__) +// arm version subsamples by summing 4 pixels then multiplying by matrix with +// 4x smaller coefficients which are rounded to nearest integer. +TESTATOPLANAR(ARGB, 4, 1, J420, 2, 2, 4) +TESTATOPLANAR(ARGB, 4, 1, J422, 2, 1, 4) +#else +TESTATOPLANAR(ARGB, 4, 1, J420, 2, 2, 0) +TESTATOPLANAR(ARGB, 4, 1, J422, 2, 1, 0) +#endif +TESTATOPLANAR(BGRA, 4, 1, I420, 2, 2, 4) +TESTATOPLANAR(ABGR, 4, 1, I420, 2, 2, 4) +TESTATOPLANAR(RGBA, 4, 1, I420, 2, 2, 4) +TESTATOPLANAR(RAW, 3, 1, I420, 2, 2, 4) +TESTATOPLANAR(RGB24, 3, 1, I420, 2, 2, 4) +TESTATOPLANAR(RGB565, 2, 1, I420, 2, 2, 5) +// TODO(fbarchard): Make 1555 neon work same as C code, reduce to diff 9. +TESTATOPLANAR(ARGB1555, 2, 1, I420, 2, 2, 15) +TESTATOPLANAR(ARGB4444, 2, 1, I420, 2, 2, 17) +TESTATOPLANAR(ARGB, 4, 1, I411, 4, 1, 4) +TESTATOPLANAR(ARGB, 4, 1, I422, 2, 1, 2) +TESTATOPLANAR(ARGB, 4, 1, I444, 1, 1, 2) +TESTATOPLANAR(YUY2, 2, 1, I420, 2, 2, 2) +TESTATOPLANAR(UYVY, 2, 1, I420, 2, 2, 2) +TESTATOPLANAR(YUY2, 2, 1, I422, 2, 1, 2) +TESTATOPLANAR(UYVY, 2, 1, I422, 2, 1, 2) +TESTATOPLANAR(I400, 1, 1, I420, 2, 2, 2) +TESTATOPLANAR(J400, 1, 1, J420, 2, 2, 2) + +#define TESTATOBIPLANARI(FMT_A, SUB_A, BPP_A, FMT_PLANAR, \ + SUBSAMP_X, SUBSAMP_Y, W1280, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_PLANAR##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kStride = SUBSAMPLE(kWidth, SUB_A) * BPP_A; \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + align_buffer_page_end(src_argb, kStride * kHeight + OFF); \ + align_buffer_page_end(dst_y_c, kWidth * kHeight); \ + align_buffer_page_end(dst_uv_c, \ + kStrideUV * 2 * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + align_buffer_page_end(dst_y_opt, kWidth * kHeight); \ + align_buffer_page_end(dst_uv_opt, \ + kStrideUV * 2 * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + for (int i = 0; i < kHeight; ++i) \ + for (int j = 0; j < kStride; ++j) \ + src_argb[(i * kStride) + j + OFF] = (fastrand() & 0xff); \ + memset(dst_y_c, 1, kWidth * kHeight); \ + memset(dst_uv_c, 2, kStrideUV * 2 * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + memset(dst_y_opt, 101, kWidth * kHeight); \ + memset(dst_uv_opt, 102, kStrideUV * 2 * SUBSAMPLE(kHeight, SUBSAMP_Y)); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_PLANAR(src_argb + OFF, kStride, \ + dst_y_c, kWidth, dst_uv_c, kStrideUV * 2, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_A##To##FMT_PLANAR(src_argb + OFF, kStride, \ + dst_y_opt, kWidth, \ + dst_uv_opt, kStrideUV * 2, 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_opt[i * kWidth + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 4); \ + for (int i = 0; i < SUBSAMPLE(kHeight, SUBSAMP_Y); ++i) { \ + for (int j = 0; j < kStrideUV * 2; ++j) { \ + int abs_diff = \ + abs(static_cast<int>(dst_uv_c[i * kStrideUV * 2 + j]) - \ + static_cast<int>(dst_uv_opt[i * kStrideUV * 2 + j])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + } \ + EXPECT_LE(max_diff, 4); \ + free_aligned_buffer_page_end(dst_y_c); \ + free_aligned_buffer_page_end(dst_uv_c); \ + free_aligned_buffer_page_end(dst_y_opt); \ + free_aligned_buffer_page_end(dst_uv_opt); \ + free_aligned_buffer_page_end(src_argb); \ +} + +#define TESTATOBIPLANAR(FMT_A, SUB_A, BPP_A, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y) \ + TESTATOBIPLANARI(FMT_A, SUB_A, BPP_A, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_ - 4, _Any, +, 0) \ + TESTATOBIPLANARI(FMT_A, SUB_A, BPP_A, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Unaligned, +, 1) \ + TESTATOBIPLANARI(FMT_A, SUB_A, BPP_A, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Invert, -, 0) \ + TESTATOBIPLANARI(FMT_A, SUB_A, BPP_A, FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, \ + benchmark_width_, _Opt, +, 0) + +TESTATOBIPLANAR(ARGB, 1, 4, NV12, 2, 2) +TESTATOBIPLANAR(ARGB, 1, 4, NV21, 2, 2) +TESTATOBIPLANAR(YUY2, 2, 4, NV12, 2, 2) +TESTATOBIPLANAR(UYVY, 2, 4, NV12, 2, 2) + +#define TESTATOBI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + W1280, DIFF, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_B##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kHeightA = (kHeight + HEIGHT_A - 1) / HEIGHT_A * HEIGHT_A; \ + const int kHeightB = (kHeight + HEIGHT_B - 1) / HEIGHT_B * HEIGHT_B; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A; \ + const int kStrideB = (kWidth * BPP_B + STRIDE_B - 1) / STRIDE_B * STRIDE_B; \ + align_buffer_page_end(src_argb, kStrideA * kHeightA + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeightB); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeightB); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + src_argb[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c, 1, kStrideB * kHeightB); \ + memset(dst_argb_opt, 101, kStrideB * kHeightB); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_B(src_argb + OFF, kStrideA, \ + dst_argb_c, kStrideB, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_A##To##FMT_B(src_argb + OFF, kStrideA, \ + dst_argb_opt, kStrideB, \ + kWidth, NEG kHeight); \ + } \ + int max_diff = 0; \ + for (int i = 0; i < kStrideB * kHeightB; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb_c[i]) - \ + static_cast<int>(dst_argb_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ +} + +#define TESTATOBRANDOM(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_B##_Random) { \ + for (int times = 0; times < benchmark_iterations_; ++times) { \ + const int kWidth = (fastrand() & 63) + 1; \ + const int kHeight = (fastrand() & 31) + 1; \ + const int kHeightA = (kHeight + HEIGHT_A - 1) / HEIGHT_A * HEIGHT_A; \ + const int kHeightB = (kHeight + HEIGHT_B - 1) / HEIGHT_B * HEIGHT_B; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A;\ + const int kStrideB = (kWidth * BPP_B + STRIDE_B - 1) / STRIDE_B * STRIDE_B;\ + align_buffer_page_end(src_argb, kStrideA * kHeightA); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeightB); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeightB); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + src_argb[i] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c, 123, kStrideB * kHeightB); \ + memset(dst_argb_opt, 123, kStrideB * kHeightB); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_B(src_argb, kStrideA, \ + dst_argb_c, kStrideB, \ + kWidth, kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + FMT_A##To##FMT_B(src_argb, kStrideA, \ + dst_argb_opt, kStrideB, \ + kWidth, kHeight); \ + int max_diff = 0; \ + for (int i = 0; i < kStrideB * kHeightB; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb_c[i]) - \ + static_cast<int>(dst_argb_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ + } \ +} + +#define TESTATOB(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) \ + TESTATOBI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_ - 4, DIFF, _Any, +, 0) \ + TESTATOBI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Unaligned, +, 1) \ + TESTATOBI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Invert, -, 0) \ + TESTATOBI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Opt, +, 0) \ + TESTATOBRANDOM(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) + +TESTATOB(ARGB, 4, 4, 1, ARGB, 4, 4, 1, 0) +TESTATOB(ARGB, 4, 4, 1, BGRA, 4, 4, 1, 0) +TESTATOB(ARGB, 4, 4, 1, ABGR, 4, 4, 1, 0) +TESTATOB(ARGB, 4, 4, 1, RGBA, 4, 4, 1, 0) +TESTATOB(ARGB, 4, 4, 1, RAW, 3, 3, 1, 0) +TESTATOB(ARGB, 4, 4, 1, RGB24, 3, 3, 1, 0) +TESTATOB(ARGB, 4, 4, 1, RGB565, 2, 2, 1, 0) +TESTATOB(ARGB, 4, 4, 1, ARGB1555, 2, 2, 1, 0) +TESTATOB(ARGB, 4, 4, 1, ARGB4444, 2, 2, 1, 0) +TESTATOB(ARGB, 4, 4, 1, YUY2, 2, 4, 1, 4) +TESTATOB(ARGB, 4, 4, 1, UYVY, 2, 4, 1, 4) +TESTATOB(ARGB, 4, 4, 1, I400, 1, 1, 1, 2) +TESTATOB(ARGB, 4, 4, 1, J400, 1, 1, 1, 2) +TESTATOB(BGRA, 4, 4, 1, ARGB, 4, 4, 1, 0) +TESTATOB(ABGR, 4, 4, 1, ARGB, 4, 4, 1, 0) +TESTATOB(RGBA, 4, 4, 1, ARGB, 4, 4, 1, 0) +TESTATOB(RAW, 3, 3, 1, ARGB, 4, 4, 1, 0) +TESTATOB(RAW, 3, 3, 1, RGB24, 3, 3, 1, 0) +TESTATOB(RGB24, 3, 3, 1, ARGB, 4, 4, 1, 0) +TESTATOB(RGB565, 2, 2, 1, ARGB, 4, 4, 1, 0) +TESTATOB(ARGB1555, 2, 2, 1, ARGB, 4, 4, 1, 0) +TESTATOB(ARGB4444, 2, 2, 1, ARGB, 4, 4, 1, 0) +TESTATOB(YUY2, 2, 4, 1, ARGB, 4, 4, 1, 4) +TESTATOB(UYVY, 2, 4, 1, ARGB, 4, 4, 1, 4) +TESTATOB(I400, 1, 1, 1, ARGB, 4, 4, 1, 0) +TESTATOB(J400, 1, 1, 1, ARGB, 4, 4, 1, 0) +TESTATOB(I400, 1, 1, 1, I400, 1, 1, 1, 0) +TESTATOB(J400, 1, 1, 1, J400, 1, 1, 1, 0) +TESTATOB(I400, 1, 1, 1, I400Mirror, 1, 1, 1, 0) +TESTATOB(ARGB, 4, 4, 1, ARGBMirror, 4, 4, 1, 0) + +#define TESTATOBDI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + W1280, DIFF, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_B##Dither##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kHeightA = (kHeight + HEIGHT_A - 1) / HEIGHT_A * HEIGHT_A; \ + const int kHeightB = (kHeight + HEIGHT_B - 1) / HEIGHT_B * HEIGHT_B; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A; \ + const int kStrideB = (kWidth * BPP_B + STRIDE_B - 1) / STRIDE_B * STRIDE_B; \ + align_buffer_page_end(src_argb, kStrideA * kHeightA + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeightB); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeightB); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + src_argb[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c, 1, kStrideB * kHeightB); \ + memset(dst_argb_opt, 101, kStrideB * kHeightB); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_B##Dither(src_argb + OFF, kStrideA, \ + dst_argb_c, kStrideB, \ + NULL, kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_A##To##FMT_B##Dither(src_argb + OFF, kStrideA, \ + dst_argb_opt, kStrideB, \ + NULL, kWidth, NEG kHeight); \ + } \ + int max_diff = 0; \ + for (int i = 0; i < kStrideB * kHeightB; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb_c[i]) - \ + static_cast<int>(dst_argb_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ +} + +#define TESTATOBDRANDOM(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) \ +TEST_F(LibYUVConvertTest, FMT_A##To##FMT_B##Dither_Random) { \ + for (int times = 0; times < benchmark_iterations_; ++times) { \ + const int kWidth = (fastrand() & 63) + 1; \ + const int kHeight = (fastrand() & 31) + 1; \ + const int kHeightA = (kHeight + HEIGHT_A - 1) / HEIGHT_A * HEIGHT_A; \ + const int kHeightB = (kHeight + HEIGHT_B - 1) / HEIGHT_B * HEIGHT_B; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A;\ + const int kStrideB = (kWidth * BPP_B + STRIDE_B - 1) / STRIDE_B * STRIDE_B;\ + align_buffer_page_end(src_argb, kStrideA * kHeightA); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeightB); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeightB); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + src_argb[i] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c, 123, kStrideB * kHeightB); \ + memset(dst_argb_opt, 123, kStrideB * kHeightB); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_A##To##FMT_B##Dither(src_argb, kStrideA, \ + dst_argb_c, kStrideB, \ + NULL, kWidth, kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + FMT_A##To##FMT_B##Dither(src_argb, kStrideA, \ + dst_argb_opt, kStrideB, \ + NULL, kWidth, kHeight); \ + int max_diff = 0; \ + for (int i = 0; i < kStrideB * kHeightB; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb_c[i]) - \ + static_cast<int>(dst_argb_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ + } \ +} + +#define TESTATOBD(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) \ + TESTATOBDI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_ - 4, DIFF, _Any, +, 0) \ + TESTATOBDI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Unaligned, +, 1) \ + TESTATOBDI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Invert, -, 0) \ + TESTATOBDI(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, \ + benchmark_width_, DIFF, _Opt, +, 0) \ + TESTATOBDRANDOM(FMT_A, BPP_A, STRIDE_A, HEIGHT_A, \ + FMT_B, BPP_B, STRIDE_B, HEIGHT_B, DIFF) + +TESTATOBD(ARGB, 4, 4, 1, RGB565, 2, 2, 1, 0) + +#define TESTSYMI(FMT_ATOB, BPP_A, STRIDE_A, HEIGHT_A, \ + W1280, N, NEG, OFF) \ +TEST_F(LibYUVConvertTest, FMT_ATOB##_Symetric##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kHeightA = (kHeight + HEIGHT_A - 1) / HEIGHT_A * HEIGHT_A; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A; \ + align_buffer_page_end(src_argb, kStrideA * kHeightA + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideA * kHeightA); \ + align_buffer_page_end(dst_argb_opt, kStrideA * kHeightA); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + src_argb[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c, 1, kStrideA * kHeightA); \ + memset(dst_argb_opt, 101, kStrideA * kHeightA); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_ATOB(src_argb + OFF, kStrideA, \ + dst_argb_c, kStrideA, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_ATOB(src_argb + OFF, kStrideA, \ + dst_argb_opt, kStrideA, \ + kWidth, NEG kHeight); \ + } \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_ATOB(dst_argb_c, kStrideA, \ + dst_argb_c, kStrideA, \ + kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + FMT_ATOB(dst_argb_opt, kStrideA, \ + dst_argb_opt, kStrideA, \ + kWidth, NEG kHeight); \ + for (int i = 0; i < kStrideA * kHeightA; ++i) { \ + EXPECT_EQ(src_argb[i + OFF], dst_argb_opt[i]); \ + EXPECT_EQ(dst_argb_c[i], dst_argb_opt[i]); \ + } \ + free_aligned_buffer_page_end(src_argb); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ +} + +#define TESTSYM(FMT_ATOB, BPP_A, STRIDE_A, HEIGHT_A) \ + TESTSYMI(FMT_ATOB, BPP_A, STRIDE_A, HEIGHT_A, \ + benchmark_width_ - 4, _Any, +, 0) \ + TESTSYMI(FMT_ATOB, BPP_A, STRIDE_A, HEIGHT_A, \ + benchmark_width_, _Unaligned, +, 1) \ + TESTSYMI(FMT_ATOB, BPP_A, STRIDE_A, HEIGHT_A, \ + benchmark_width_, _Opt, +, 0) + +TESTSYM(ARGBToARGB, 4, 4, 1) +TESTSYM(ARGBToBGRA, 4, 4, 1) +TESTSYM(ARGBToABGR, 4, 4, 1) +TESTSYM(BGRAToARGB, 4, 4, 1) +TESTSYM(ABGRToARGB, 4, 4, 1) + +TEST_F(LibYUVConvertTest, Test565) { + SIMD_ALIGNED(uint8 orig_pixels[256][4]); + SIMD_ALIGNED(uint8 pixels565[256][2]); + + for (int i = 0; i < 256; ++i) { + for (int j = 0; j < 4; ++j) { + orig_pixels[i][j] = i; + } + } + ARGBToRGB565(&orig_pixels[0][0], 0, &pixels565[0][0], 0, 256, 1); + uint32 checksum = HashDjb2(&pixels565[0][0], sizeof(pixels565), 5381); + EXPECT_EQ(610919429u, checksum); +} + +#ifdef HAVE_JPEG +TEST_F(LibYUVConvertTest, ValidateJpeg) { + const int kOff = 10; + const int kMinJpeg = 64; + const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg ? + benchmark_width_ * benchmark_height_ : kMinJpeg; + const int kSize = kImageSize + kOff; + align_buffer_page_end(orig_pixels, kSize); + + // No SOI or EOI. Expect fail. + memset(orig_pixels, 0, kSize); + EXPECT_FALSE(ValidateJpeg(orig_pixels, kSize)); + + // Test special value that matches marker start. + memset(orig_pixels, 0xff, kSize); + EXPECT_FALSE(ValidateJpeg(orig_pixels, kSize)); + + // EOI, SOI. Expect pass. + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + orig_pixels[kSize - kOff + 0] = 0xff; + orig_pixels[kSize - kOff + 1] = 0xd9; // EOI. + for (int times = 0; times < benchmark_iterations_; ++times) { + EXPECT_TRUE(ValidateJpeg(orig_pixels, kSize)); + } + free_aligned_buffer_page_end(orig_pixels); +} + +TEST_F(LibYUVConvertTest, ValidateJpegLarge) { + const int kOff = 10; + const int kMinJpeg = 64; + const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg ? + benchmark_width_ * benchmark_height_ : kMinJpeg; + const int kSize = kImageSize + kOff; + const int kMultiple = 10; + const int kBufSize = kImageSize * kMultiple + kOff; + align_buffer_page_end(orig_pixels, kBufSize); + + // No SOI or EOI. Expect fail. + memset(orig_pixels, 0, kBufSize); + EXPECT_FALSE(ValidateJpeg(orig_pixels, kBufSize)); + + // EOI, SOI. Expect pass. + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + orig_pixels[kSize - kOff + 0] = 0xff; + orig_pixels[kSize - kOff + 1] = 0xd9; // EOI. + for (int times = 0; times < benchmark_iterations_; ++times) { + EXPECT_TRUE(ValidateJpeg(orig_pixels, kBufSize)); + } + free_aligned_buffer_page_end(orig_pixels); +} + +TEST_F(LibYUVConvertTest, InvalidateJpeg) { + const int kOff = 10; + const int kMinJpeg = 64; + const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg ? + benchmark_width_ * benchmark_height_ : kMinJpeg; + const int kSize = kImageSize + kOff; + align_buffer_page_end(orig_pixels, kSize); + + // NULL pointer. Expect fail. + EXPECT_FALSE(ValidateJpeg(NULL, kSize)); + + // Negative size. Expect fail. + EXPECT_FALSE(ValidateJpeg(orig_pixels, -1)); + + // Too large size. Expect fail. + EXPECT_FALSE(ValidateJpeg(orig_pixels, 0xfb000000ull)); + + // No SOI or EOI. Expect fail. + memset(orig_pixels, 0, kSize); + EXPECT_FALSE(ValidateJpeg(orig_pixels, kSize)); + + // SOI but no EOI. Expect fail. + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + for (int times = 0; times < benchmark_iterations_; ++times) { + EXPECT_FALSE(ValidateJpeg(orig_pixels, kSize)); + } + + // EOI but no SOI. Expect fail. + orig_pixels[0] = 0; + orig_pixels[1] = 0; + orig_pixels[kSize - kOff + 0] = 0xff; + orig_pixels[kSize - kOff + 1] = 0xd9; // EOI. + EXPECT_FALSE(ValidateJpeg(orig_pixels, kSize)); + + free_aligned_buffer_page_end(orig_pixels); +} + +TEST_F(LibYUVConvertTest, FuzzJpeg) { + // SOI but no EOI. Expect fail. + for (int times = 0; times < benchmark_iterations_; ++times) { + const int kSize = fastrand() % 5000 + 2; + align_buffer_page_end(orig_pixels, kSize); + MemRandomize(orig_pixels, kSize); + + // Add SOI so frame will be scanned. + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + orig_pixels[kSize - 1] = 0xff; + ValidateJpeg(orig_pixels, kSize); // Failure normally expected. + free_aligned_buffer_page_end(orig_pixels); + } +} + +TEST_F(LibYUVConvertTest, MJPGToI420) { + const int kOff = 10; + const int kMinJpeg = 64; + const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg ? + benchmark_width_ * benchmark_height_ : kMinJpeg; + const int kSize = kImageSize + kOff; + align_buffer_page_end(orig_pixels, kSize); + align_buffer_page_end(dst_y_opt, benchmark_width_ * benchmark_height_); + align_buffer_page_end(dst_u_opt, + SUBSAMPLE(benchmark_width_, 2) * + SUBSAMPLE(benchmark_height_, 2)); + align_buffer_page_end(dst_v_opt, + SUBSAMPLE(benchmark_width_, 2) * + SUBSAMPLE(benchmark_height_, 2)); + + // EOI, SOI to make MJPG appear valid. + memset(orig_pixels, 0, kSize); + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + orig_pixels[kSize - kOff + 0] = 0xff; + orig_pixels[kSize - kOff + 1] = 0xd9; // EOI. + + for (int times = 0; times < benchmark_iterations_; ++times) { + int ret = MJPGToI420(orig_pixels, kSize, + dst_y_opt, benchmark_width_, + dst_u_opt, SUBSAMPLE(benchmark_width_, 2), + dst_v_opt, SUBSAMPLE(benchmark_width_, 2), + benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_); + // Expect failure because image is not really valid. + EXPECT_EQ(1, ret); + } + + free_aligned_buffer_page_end(dst_y_opt); + free_aligned_buffer_page_end(dst_u_opt); + free_aligned_buffer_page_end(dst_v_opt); + free_aligned_buffer_page_end(orig_pixels); +} + +TEST_F(LibYUVConvertTest, MJPGToARGB) { + const int kOff = 10; + const int kMinJpeg = 64; + const int kImageSize = benchmark_width_ * benchmark_height_ >= kMinJpeg ? + benchmark_width_ * benchmark_height_ : kMinJpeg; + const int kSize = kImageSize + kOff; + align_buffer_page_end(orig_pixels, kSize); + align_buffer_page_end(dst_argb_opt, benchmark_width_ * benchmark_height_ * 4); + + // EOI, SOI to make MJPG appear valid. + memset(orig_pixels, 0, kSize); + orig_pixels[0] = 0xff; + orig_pixels[1] = 0xd8; // SOI. + orig_pixels[kSize - kOff + 0] = 0xff; + orig_pixels[kSize - kOff + 1] = 0xd9; // EOI. + + for (int times = 0; times < benchmark_iterations_; ++times) { + int ret = MJPGToARGB(orig_pixels, kSize, + dst_argb_opt, benchmark_width_ * 4, + benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_); + // Expect failure because image is not really valid. + EXPECT_EQ(1, ret); + } + + free_aligned_buffer_page_end(dst_argb_opt); + free_aligned_buffer_page_end(orig_pixels); +} + +#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* 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); +} + +TEST_F(LibYUVConvertTest, TestYToARGB) { + uint8 y[32]; + uint8 expectedg[32]; + for (int i = 0; i < 32; ++i) { + y[i] = i * 5 + 17; + expectedg[i] = static_cast<int>((y[i] - 16) * 1.164f + 0.5f); + } + uint8 argb[32 * 4]; + YToARGB(y, 0, argb, 0, 32, 1); + + for (int i = 0; i < 32; ++i) { + printf("%2d %d: %d <-> %d,%d,%d,%d\n", i, y[i], expectedg[i], + argb[i * 4 + 0], + argb[i * 4 + 1], + argb[i * 4 + 2], + argb[i * 4 + 3]); + } + for (int i = 0; i < 32; ++i) { + EXPECT_EQ(expectedg[i], argb[i * 4 + 0]); + } +} + +static const uint8 kNoDither4x4[16] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, +}; + +TEST_F(LibYUVConvertTest, TestNoDither) { + align_buffer_page_end(src_argb, benchmark_width_ * benchmark_height_ * 4); + align_buffer_page_end(dst_rgb565, benchmark_width_ * benchmark_height_ * 2); + align_buffer_page_end(dst_rgb565dither, + benchmark_width_ * benchmark_height_ * 2); + MemRandomize(src_argb, benchmark_width_ * benchmark_height_ * 4); + MemRandomize(dst_rgb565, benchmark_width_ * benchmark_height_ * 2); + MemRandomize(dst_rgb565dither, benchmark_width_ * benchmark_height_ * 2); + ARGBToRGB565(src_argb, benchmark_width_ * 4, + dst_rgb565, benchmark_width_ * 2, + benchmark_width_, benchmark_height_); + ARGBToRGB565Dither(src_argb, benchmark_width_ * 4, + dst_rgb565dither, benchmark_width_ * 2, + kNoDither4x4, benchmark_width_, benchmark_height_); + for (int i = 0; i < benchmark_width_ * benchmark_height_ * 2; ++i) { + EXPECT_EQ(dst_rgb565[i], dst_rgb565dither[i]); + } + + free_aligned_buffer_page_end(src_argb); + free_aligned_buffer_page_end(dst_rgb565); + free_aligned_buffer_page_end(dst_rgb565dither); +} + +// Ordered 4x4 dither for 888 to 565. Values from 0 to 7. +static const uint8 kDither565_4x4[16] = { + 0, 4, 1, 5, + 6, 2, 7, 3, + 1, 5, 0, 4, + 7, 3, 6, 2, +}; + +TEST_F(LibYUVConvertTest, TestDither) { + align_buffer_page_end(src_argb, benchmark_width_ * benchmark_height_ * 4); + align_buffer_page_end(dst_rgb565, benchmark_width_ * benchmark_height_ * 2); + align_buffer_page_end(dst_rgb565dither, + benchmark_width_ * benchmark_height_ * 2); + align_buffer_page_end(dst_argb, benchmark_width_ * benchmark_height_ * 4); + align_buffer_page_end(dst_argbdither, + benchmark_width_ * benchmark_height_ * 4); + MemRandomize(src_argb, benchmark_width_ * benchmark_height_ * 4); + MemRandomize(dst_rgb565, benchmark_width_ * benchmark_height_ * 2); + MemRandomize(dst_rgb565dither, benchmark_width_ * benchmark_height_ * 2); + MemRandomize(dst_argb, benchmark_width_ * benchmark_height_ * 4); + MemRandomize(dst_argbdither, benchmark_width_ * benchmark_height_ * 4); + ARGBToRGB565(src_argb, benchmark_width_ * 4, + dst_rgb565, benchmark_width_ * 2, + benchmark_width_, benchmark_height_); + ARGBToRGB565Dither(src_argb, benchmark_width_ * 4, + dst_rgb565dither, benchmark_width_ * 2, + kDither565_4x4, benchmark_width_, benchmark_height_); + RGB565ToARGB(dst_rgb565, benchmark_width_ * 2, + dst_argb, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + RGB565ToARGB(dst_rgb565dither, benchmark_width_ * 2, + dst_argbdither, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + + for (int i = 0; i < benchmark_width_ * benchmark_height_ * 4; ++i) { + EXPECT_NEAR(dst_argb[i], dst_argbdither[i], 9); + } + free_aligned_buffer_page_end(src_argb); + free_aligned_buffer_page_end(dst_rgb565); + free_aligned_buffer_page_end(dst_rgb565dither); + free_aligned_buffer_page_end(dst_argb); + free_aligned_buffer_page_end(dst_argbdither); +} + +#define TESTPLANARTOBID(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, W1280, DIFF, N, NEG, OFF, FMT_C, BPP_C) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##Dither##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = ALIGNINT(benchmark_height_, YALIGN); \ + const int kStrideB = ALIGNINT(kWidth * BPP_B, ALIGN); \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, kSizeUV + OFF); \ + align_buffer_page_end(src_v, kSizeUV + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeight + OFF); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeight + OFF); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + src_y[i + OFF] = (fastrand() & 0xff); \ + } \ + for (int i = 0; i < kSizeUV; ++i) { \ + src_u[i + OFF] = (fastrand() & 0xff); \ + src_v[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_c + OFF, 1, kStrideB * kHeight); \ + memset(dst_argb_opt + OFF, 101, kStrideB * kHeight); \ + MaskCpuFlags(disable_cpu_flags_); \ + FMT_PLANAR##To##FMT_B##Dither(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_c + OFF, kStrideB, \ + NULL, kWidth, NEG kHeight); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B##Dither(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_opt + OFF, kStrideB, \ + NULL, kWidth, NEG kHeight); \ + } \ + int max_diff = 0; \ + /* Convert to ARGB so 565 is expanded to bytes that can be compared. */ \ + align_buffer_page_end(dst_argb32_c, kWidth * BPP_C * kHeight); \ + align_buffer_page_end(dst_argb32_opt, kWidth * BPP_C * kHeight); \ + memset(dst_argb32_c, 2, kWidth * BPP_C * kHeight); \ + memset(dst_argb32_opt, 102, kWidth * BPP_C * kHeight); \ + FMT_B##To##FMT_C(dst_argb_c + OFF, kStrideB, \ + dst_argb32_c, kWidth * BPP_C , \ + kWidth, kHeight); \ + FMT_B##To##FMT_C(dst_argb_opt + OFF, kStrideB, \ + dst_argb32_opt, kWidth * BPP_C , \ + kWidth, kHeight); \ + for (int i = 0; i < kWidth * BPP_C * kHeight; ++i) { \ + int abs_diff = \ + abs(static_cast<int>(dst_argb32_c[i]) - \ + static_cast<int>(dst_argb32_opt[i])); \ + if (abs_diff > max_diff) { \ + max_diff = abs_diff; \ + } \ + } \ + EXPECT_LE(max_diff, DIFF); \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ + free_aligned_buffer_page_end(dst_argb32_c); \ + free_aligned_buffer_page_end(dst_argb32_opt); \ +} + +#define TESTPLANARTOBD(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, DIFF, FMT_C, BPP_C) \ + TESTPLANARTOBID(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_ - 4, DIFF, _Any, +, 0, FMT_C, BPP_C) \ + TESTPLANARTOBID(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Unaligned, +, 1, FMT_C, BPP_C) \ + TESTPLANARTOBID(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Invert, -, 0, FMT_C, BPP_C) \ + TESTPLANARTOBID(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, BPP_B, ALIGN, \ + YALIGN, benchmark_width_, DIFF, _Opt, +, 0, FMT_C, BPP_C) + +TESTPLANARTOBD(I420, 2, 2, RGB565, 2, 2, 1, 9, ARGB, 4) + +#define TESTPTOB(NAME, UYVYTOI420, UYVYTONV12) \ +TEST_F(LibYUVConvertTest, NAME) { \ + const int kWidth = benchmark_width_; \ + const int kHeight = benchmark_height_; \ + \ + align_buffer_page_end(orig_uyvy, \ + 4 * SUBSAMPLE(kWidth, 2) * kHeight); \ + align_buffer_page_end(orig_y, kWidth * kHeight); \ + align_buffer_page_end(orig_u, \ + SUBSAMPLE(kWidth, 2) * \ + SUBSAMPLE(kHeight, 2)); \ + align_buffer_page_end(orig_v, \ + SUBSAMPLE(kWidth, 2) * \ + SUBSAMPLE(kHeight, 2)); \ + \ + align_buffer_page_end(dst_y_orig, kWidth * kHeight); \ + align_buffer_page_end(dst_uv_orig, 2 * \ + SUBSAMPLE(kWidth, 2) * \ + SUBSAMPLE(kHeight, 2)); \ + \ + align_buffer_page_end(dst_y, kWidth * kHeight); \ + align_buffer_page_end(dst_uv, 2 * \ + SUBSAMPLE(kWidth, 2) * \ + SUBSAMPLE(kHeight, 2)); \ + \ + MemRandomize(orig_uyvy, 4 * SUBSAMPLE(kWidth, 2) * kHeight); \ + \ + /* Convert UYVY to NV12 in 2 steps for reference */ \ + libyuv::UYVYTOI420(orig_uyvy, 4 * SUBSAMPLE(kWidth, 2), \ + orig_y, kWidth, \ + orig_u, SUBSAMPLE(kWidth, 2), \ + orig_v, SUBSAMPLE(kWidth, 2), \ + kWidth, kHeight); \ + libyuv::I420ToNV12(orig_y, kWidth, \ + orig_u, SUBSAMPLE(kWidth, 2), \ + orig_v, SUBSAMPLE(kWidth, 2), \ + dst_y_orig, kWidth, \ + dst_uv_orig, 2 * SUBSAMPLE(kWidth, 2), \ + kWidth, kHeight); \ + \ + /* Convert to NV12 */ \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + libyuv::UYVYTONV12(orig_uyvy, 4 * SUBSAMPLE(kWidth, 2), \ + dst_y, kWidth, \ + dst_uv, 2 * SUBSAMPLE(kWidth, 2), \ + kWidth, kHeight); \ + } \ + \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + EXPECT_EQ(orig_y[i], dst_y[i]); \ + } \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + EXPECT_EQ(dst_y_orig[i], dst_y[i]); \ + } \ + for (int i = 0; i < 2 * SUBSAMPLE(kWidth, 2) * SUBSAMPLE(kHeight, 2); ++i) { \ + EXPECT_EQ(dst_uv_orig[i], dst_uv[i]); \ + } \ + \ + free_aligned_buffer_page_end(orig_uyvy); \ + free_aligned_buffer_page_end(orig_y); \ + free_aligned_buffer_page_end(orig_u); \ + free_aligned_buffer_page_end(orig_v); \ + free_aligned_buffer_page_end(dst_y_orig); \ + free_aligned_buffer_page_end(dst_uv_orig); \ + free_aligned_buffer_page_end(dst_y); \ + free_aligned_buffer_page_end(dst_uv); \ +} + +TESTPTOB(TestYUY2ToNV12, YUY2ToI420, YUY2ToNV12) +TESTPTOB(TestUYVYToNV12, UYVYToI420, UYVYToNV12) + +#define TESTPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + W1280, N, NEG, OFF, FMT_C, BPP_C) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##_##FMT_C##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kStrideB = SUBSAMPLE(kWidth, SUB_B) * BPP_B; \ + const int kStrideUV = SUBSAMPLE(kWidth, SUBSAMP_X); \ + const int kSizeUV = kStrideUV * SUBSAMPLE(kHeight, SUBSAMP_Y); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, kSizeUV + OFF); \ + align_buffer_page_end(src_v, kSizeUV + OFF); \ + align_buffer_page_end(dst_argb_b, kStrideB * kHeight + OFF); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + src_y[i + OFF] = (fastrand() & 0xff); \ + } \ + for (int i = 0; i < kSizeUV; ++i) { \ + src_u[i + OFF] = (fastrand() & 0xff); \ + src_v[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_b + OFF, 1, kStrideB * kHeight); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_b + OFF, kStrideB, \ + kWidth, NEG kHeight); \ + } \ + /* Convert to a 3rd format in 1 step and 2 steps and compare */ \ + const int kStrideC = kWidth * BPP_C; \ + align_buffer_page_end(dst_argb_c, kStrideC * kHeight + OFF); \ + align_buffer_page_end(dst_argb_bc, kStrideC * kHeight + OFF); \ + memset(dst_argb_c + OFF, 2, kStrideC * kHeight); \ + memset(dst_argb_bc + OFF, 3, kStrideC * kHeight); \ + FMT_PLANAR##To##FMT_C(src_y + OFF, kWidth, \ + src_u + OFF, kStrideUV, \ + src_v + OFF, kStrideUV, \ + dst_argb_c + OFF, kStrideC, \ + kWidth, NEG kHeight); \ + /* Convert B to C */ \ + FMT_B##To##FMT_C(dst_argb_b + OFF, kStrideB, \ + dst_argb_bc + OFF, kStrideC, \ + kWidth, kHeight); \ + for (int i = 0; i < kStrideC * kHeight; ++i) { \ + EXPECT_EQ(dst_argb_c[i + OFF], dst_argb_bc[i + OFF]); \ + } \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(dst_argb_b); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_bc); \ +} + +#define TESTPLANARTOE(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + FMT_C, BPP_C) \ + TESTPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_ - 4, _Any, +, 0, FMT_C, BPP_C) \ + TESTPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Unaligned, +, 1, FMT_C, BPP_C) \ + TESTPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Invert, -, 0, FMT_C, BPP_C) \ + TESTPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Opt, +, 0, FMT_C, BPP_C) + +TESTPLANARTOE(I420, 2, 2, ARGB, 1, 4, ABGR, 4) +TESTPLANARTOE(J420, 2, 2, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(J420, 2, 2, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(H420, 2, 2, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(H420, 2, 2, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, BGRA, 1, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, RGBA, 1, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, RGB24, 1, 3, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, RAW, 1, 3, RGB24, 3) +TESTPLANARTOE(I420, 2, 2, RGB24, 1, 3, RAW, 3) +TESTPLANARTOE(I420, 2, 2, ARGB, 1, 4, RAW, 3) +TESTPLANARTOE(I420, 2, 2, RAW, 1, 3, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, ARGB, 1, 4, RGB565, 2) +TESTPLANARTOE(I420, 2, 2, ARGB, 1, 4, ARGB1555, 2) +TESTPLANARTOE(I420, 2, 2, ARGB, 1, 4, ARGB4444, 2) +TESTPLANARTOE(I422, 2, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(J422, 2, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(J422, 2, 1, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(H422, 2, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(H422, 2, 1, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(I422, 2, 1, BGRA, 1, 4, ARGB, 4) +TESTPLANARTOE(I422, 2, 1, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(I422, 2, 1, RGBA, 1, 4, ARGB, 4) +TESTPLANARTOE(I411, 4, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(I444, 1, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(J444, 1, 1, ARGB, 1, 4, ARGB, 4) +TESTPLANARTOE(I444, 1, 1, ABGR, 1, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, YUY2, 2, 4, ARGB, 4) +TESTPLANARTOE(I420, 2, 2, UYVY, 2, 4, ARGB, 4) +TESTPLANARTOE(I422, 2, 1, YUY2, 2, 4, ARGB, 4) +TESTPLANARTOE(I422, 2, 1, UYVY, 2, 4, ARGB, 4) + +#define TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + W1280, N, NEG, OFF, FMT_C, BPP_C, ATTEN) \ +TEST_F(LibYUVConvertTest, FMT_PLANAR##To##FMT_B##_##FMT_C##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kStrideB = SUBSAMPLE(kWidth, SUB_B) * BPP_B; \ + const int kSizeUV = \ + SUBSAMPLE(kWidth, SUBSAMP_X) * SUBSAMPLE(kHeight, SUBSAMP_Y); \ + align_buffer_page_end(src_y, kWidth * kHeight + OFF); \ + align_buffer_page_end(src_u, kSizeUV + OFF); \ + align_buffer_page_end(src_v, kSizeUV + OFF); \ + align_buffer_page_end(src_a, kWidth * kHeight + OFF); \ + align_buffer_page_end(dst_argb_b, kStrideB * kHeight + OFF); \ + for (int i = 0; i < kWidth * kHeight; ++i) { \ + src_y[i + OFF] = (fastrand() & 0xff); \ + src_a[i + OFF] = (fastrand() & 0xff); \ + } \ + for (int i = 0; i < kSizeUV; ++i) { \ + src_u[i + OFF] = (fastrand() & 0xff); \ + src_v[i + OFF] = (fastrand() & 0xff); \ + } \ + memset(dst_argb_b + OFF, 1, kStrideB * kHeight); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + FMT_PLANAR##To##FMT_B(src_y + OFF, kWidth, \ + src_u + OFF, SUBSAMPLE(kWidth, SUBSAMP_X), \ + src_v + OFF, SUBSAMPLE(kWidth, SUBSAMP_X), \ + src_a + OFF, kWidth, \ + dst_argb_b + OFF, kStrideB, \ + kWidth, NEG kHeight, ATTEN); \ + } \ + int max_diff = 0; \ + /* Convert to a 3rd format in 1 step and 2 steps and compare */ \ + const int kStrideC = kWidth * BPP_C; \ + align_buffer_page_end(dst_argb_c, kStrideC * kHeight + OFF); \ + align_buffer_page_end(dst_argb_bc, kStrideC * kHeight + OFF); \ + memset(dst_argb_c + OFF, 2, kStrideC * kHeight); \ + memset(dst_argb_bc + OFF, 3, kStrideC * kHeight); \ + FMT_PLANAR##To##FMT_C(src_y + OFF, kWidth, \ + src_u + OFF, SUBSAMPLE(kWidth, SUBSAMP_X), \ + src_v + OFF, SUBSAMPLE(kWidth, SUBSAMP_X), \ + src_a + OFF, kWidth, \ + dst_argb_c + OFF, kStrideC, \ + kWidth, NEG kHeight, ATTEN); \ + /* Convert B to C */ \ + FMT_B##To##FMT_C(dst_argb_b + OFF, kStrideB, \ + dst_argb_bc + OFF, kStrideC, \ + kWidth, kHeight); \ + for (int i = 0; i < kStrideC * kHeight; ++i) { \ + EXPECT_EQ(dst_argb_c[i + OFF], dst_argb_bc[i + OFF]); \ + } \ + free_aligned_buffer_page_end(src_y); \ + free_aligned_buffer_page_end(src_u); \ + free_aligned_buffer_page_end(src_v); \ + free_aligned_buffer_page_end(src_a); \ + free_aligned_buffer_page_end(dst_argb_b); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_bc); \ +} + +#define TESTQPLANARTOE(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + FMT_C, BPP_C) \ + TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_ - 4, _Any, +, 0, FMT_C, BPP_C, 0) \ + TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Unaligned, +, 1, FMT_C, BPP_C, 0) \ + TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Invert, -, 0, FMT_C, BPP_C, 0) \ + TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Opt, +, 0, FMT_C, BPP_C, 0) \ + TESTQPLANARTOEI(FMT_PLANAR, SUBSAMP_X, SUBSAMP_Y, FMT_B, SUB_B, BPP_B, \ + benchmark_width_, _Premult, +, 0, FMT_C, BPP_C, 1) + +TESTQPLANARTOE(I420Alpha, 2, 2, ARGB, 1, 4, ABGR, 4) +TESTQPLANARTOE(I420Alpha, 2, 2, ABGR, 1, 4, ARGB, 4) + +} // namespace libyuv diff --git a/media/libyuv/unit_test/cpu_test.cc b/media/libyuv/unit_test/cpu_test.cc new file mode 100644 index 000000000..0cd06f9b7 --- /dev/null +++ b/media/libyuv/unit_test/cpu_test.cc @@ -0,0 +1,139 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <string.h> + +#include "libyuv/basic_types.h" +#include "libyuv/cpu_id.h" +#include "libyuv/version.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +TEST_F(LibYUVBaseTest, TestCpuHas) { + int cpu_flags = TestCpuFlag(-1); + printf("Cpu Flags %x\n", cpu_flags); + int has_arm = TestCpuFlag(kCpuHasARM); + printf("Has ARM %x\n", has_arm); + int has_neon = TestCpuFlag(kCpuHasNEON); + printf("Has NEON %x\n", has_neon); + int has_x86 = TestCpuFlag(kCpuHasX86); + printf("Has X86 %x\n", has_x86); + int has_sse2 = TestCpuFlag(kCpuHasSSE2); + printf("Has SSE2 %x\n", has_sse2); + int has_ssse3 = TestCpuFlag(kCpuHasSSSE3); + printf("Has SSSE3 %x\n", has_ssse3); + int has_sse41 = TestCpuFlag(kCpuHasSSE41); + printf("Has SSE4.1 %x\n", has_sse41); + int has_sse42 = TestCpuFlag(kCpuHasSSE42); + printf("Has SSE4.2 %x\n", has_sse42); + int has_avx = TestCpuFlag(kCpuHasAVX); + printf("Has AVX %x\n", has_avx); + int has_avx2 = TestCpuFlag(kCpuHasAVX2); + printf("Has AVX2 %x\n", has_avx2); + int has_erms = TestCpuFlag(kCpuHasERMS); + printf("Has ERMS %x\n", has_erms); + int has_fma3 = TestCpuFlag(kCpuHasFMA3); + printf("Has FMA3 %x\n", has_fma3); + int has_avx3 = TestCpuFlag(kCpuHasAVX3); + printf("Has AVX3 %x\n", has_avx3); + int has_mips = TestCpuFlag(kCpuHasMIPS); + printf("Has MIPS %x\n", has_mips); + int has_dspr2 = TestCpuFlag(kCpuHasDSPR2); + printf("Has DSPR2 %x\n", has_dspr2); +} + +TEST_F(LibYUVBaseTest, TestCpuCompilerEnabled) { +#if defined(__aarch64__) + printf("Arm64 build\n"); +#endif +#if defined(__aarch64__) || defined(__ARM_NEON__) || defined(LIBYUV_NEON) + printf("Neon build enabled\n"); +#endif +#if defined(__x86_64__) || defined(_M_X64) + printf("x64 build\n"); +#endif +#ifdef _MSC_VER +printf("_MSC_VER %d\n", _MSC_VER); +#endif +#if !defined(LIBYUV_DISABLE_X86) && (defined(GCC_HAS_AVX2) || \ + defined(CLANG_HAS_AVX2) || defined(VISUALC_HAS_AVX2)) + printf("Has AVX2 1\n"); +#else + printf("Has AVX2 0\n"); + // If compiler does not support AVX2, the following function not expected: +#endif +} + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64) +TEST_F(LibYUVBaseTest, TestCpuId) { + int has_x86 = TestCpuFlag(kCpuHasX86); + if (has_x86) { + uint32 cpu_info[4]; + // Vendor ID: + // AuthenticAMD AMD processor + // CentaurHauls Centaur processor + // CyrixInstead Cyrix processor + // GenuineIntel Intel processor + // GenuineTMx86 Transmeta processor + // Geode by NSC National Semiconductor processor + // NexGenDriven NexGen processor + // RiseRiseRise Rise Technology processor + // SiS SiS SiS SiS processor + // UMC UMC UMC UMC processor + CpuId(0, 0, cpu_info); + cpu_info[0] = cpu_info[1]; // Reorder output + cpu_info[1] = cpu_info[3]; + cpu_info[3] = 0; + printf("Cpu Vendor: %s %x %x %x\n", reinterpret_cast<char*>(&cpu_info[0]), + cpu_info[0], cpu_info[1], cpu_info[2]); + EXPECT_EQ(12, strlen(reinterpret_cast<char*>(&cpu_info[0]))); + + // CPU Family and Model + // 3:0 - Stepping + // 7:4 - Model + // 11:8 - Family + // 13:12 - Processor Type + // 19:16 - Extended Model + // 27:20 - Extended Family + CpuId(1, 0, cpu_info); + int family = ((cpu_info[0] >> 8) & 0x0f) | ((cpu_info[0] >> 16) & 0xff0); + int model = ((cpu_info[0] >> 4) & 0x0f) | ((cpu_info[0] >> 12) & 0xf0); + printf("Cpu Family %d (0x%x), Model %d (0x%x)\n", family, family, + model, model); + } +} +#endif + +static int FileExists(const char* file_name) { + FILE* f = fopen(file_name, "r"); + if (!f) { + return 0; + } + fclose(f); + return 1; +} + +TEST_F(LibYUVBaseTest, TestLinuxNeon) { + if (FileExists("../../unit_test/testdata/arm_v7.txt")) { + EXPECT_EQ(0, ArmCpuCaps("../../unit_test/testdata/arm_v7.txt")); + EXPECT_EQ(kCpuHasNEON, ArmCpuCaps("../../unit_test/testdata/tegra3.txt")); + EXPECT_EQ(kCpuHasNEON, ArmCpuCaps("../../unit_test/testdata/juno.txt")); + } else { + printf("WARNING: unable to load \"../../unit_test/testdata/arm_v7.txt\"\n"); + } +#if defined(__linux__) && defined(__ARM_NEON__) + EXPECT_EQ(kCpuHasNEON, ArmCpuCaps("/proc/cpuinfo")); +#endif +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/math_test.cc b/media/libyuv/unit_test/math_test.cc new file mode 100644 index 000000000..19af9f6b0 --- /dev/null +++ b/media/libyuv/unit_test/math_test.cc @@ -0,0 +1,155 @@ +/* + * Copyright 2013 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "libyuv/basic_types.h" +#include "libyuv/cpu_id.h" +#include "libyuv/scale.h" +#include "libyuv/scale_row.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +TEST_F(LibYUVBaseTest, TestFixedDiv) { + int num[1280]; + int div[1280]; + int result_opt[1280]; + int result_c[1280]; + + EXPECT_EQ(0x10000, libyuv::FixedDiv(1, 1)); + EXPECT_EQ(0x7fff0000, libyuv::FixedDiv(0x7fff, 1)); + // TODO(fbarchard): Avoid the following that throw exceptions. + // EXPECT_EQ(0x100000000, libyuv::FixedDiv(0x10000, 1)); + // EXPECT_EQ(0x80000000, libyuv::FixedDiv(0x8000, 1)); + + EXPECT_EQ(0x20000, libyuv::FixedDiv(640 * 2, 640)); + EXPECT_EQ(0x30000, libyuv::FixedDiv(640 * 3, 640)); + EXPECT_EQ(0x40000, libyuv::FixedDiv(640 * 4, 640)); + EXPECT_EQ(0x50000, libyuv::FixedDiv(640 * 5, 640)); + EXPECT_EQ(0x60000, libyuv::FixedDiv(640 * 6, 640)); + EXPECT_EQ(0x70000, libyuv::FixedDiv(640 * 7, 640)); + EXPECT_EQ(0x80000, libyuv::FixedDiv(640 * 8, 640)); + EXPECT_EQ(0xa0000, libyuv::FixedDiv(640 * 10, 640)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(960 * 2, 960)); + EXPECT_EQ(0x08000, libyuv::FixedDiv(640 / 2, 640)); + EXPECT_EQ(0x04000, libyuv::FixedDiv(640 / 4, 640)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(1080 * 2, 1080)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(200000, 100000)); + EXPECT_EQ(0x18000, libyuv::FixedDiv(150000, 100000)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(40000, 20000)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(-40000, -20000)); + EXPECT_EQ(-0x20000, libyuv::FixedDiv(40000, -20000)); + EXPECT_EQ(-0x20000, libyuv::FixedDiv(-40000, 20000)); + EXPECT_EQ(0x10000, libyuv::FixedDiv(4095, 4095)); + EXPECT_EQ(0x10000, libyuv::FixedDiv(4096, 4096)); + EXPECT_EQ(0x10000, libyuv::FixedDiv(4097, 4097)); + EXPECT_EQ(123 * 65536, libyuv::FixedDiv(123, 1)); + + for (int i = 1; i < 4100; ++i) { + EXPECT_EQ(0x10000, libyuv::FixedDiv(i, i)); + EXPECT_EQ(0x20000, libyuv::FixedDiv(i * 2, i)); + EXPECT_EQ(0x30000, libyuv::FixedDiv(i * 3, i)); + EXPECT_EQ(0x40000, libyuv::FixedDiv(i * 4, i)); + EXPECT_EQ(0x08000, libyuv::FixedDiv(i, i * 2)); + EXPECT_NEAR(16384 * 65536 / i, libyuv::FixedDiv(16384, i), 1); + } + EXPECT_EQ(123 * 65536, libyuv::FixedDiv(123, 1)); + + MemRandomize(reinterpret_cast<uint8*>(&num[0]), sizeof(num)); + MemRandomize(reinterpret_cast<uint8*>(&div[0]), sizeof(div)); + for (int j = 0; j < 1280; ++j) { + if (div[j] == 0) { + div[j] = 1280; + } + num[j] &= 0xffff; // Clamp to avoid divide overflow. + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + for (int j = 0; j < 1280; ++j) { + result_opt[j] = libyuv::FixedDiv(num[j], div[j]); + } + } + for (int j = 0; j < 1280; ++j) { + result_c[j] = libyuv::FixedDiv_C(num[j], div[j]); + EXPECT_NEAR(result_c[j], result_opt[j], 1); + } +} + +TEST_F(LibYUVBaseTest, TestFixedDiv_Opt) { + int num[1280]; + int div[1280]; + int result_opt[1280]; + int result_c[1280]; + + MemRandomize(reinterpret_cast<uint8*>(&num[0]), sizeof(num)); + MemRandomize(reinterpret_cast<uint8*>(&div[0]), sizeof(div)); + for (int j = 0; j < 1280; ++j) { + num[j] &= 4095; // Make numerator smaller. + div[j] &= 4095; // Make divisor smaller. + if (div[j] == 0) { + div[j] = 1280; + } + } + + int has_x86 = TestCpuFlag(kCpuHasX86); + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + if (has_x86) { + for (int j = 0; j < 1280; ++j) { + result_opt[j] = libyuv::FixedDiv(num[j], div[j]); + } + } else { + for (int j = 0; j < 1280; ++j) { + result_opt[j] = libyuv::FixedDiv_C(num[j], div[j]); + } + } + } + for (int j = 0; j < 1280; ++j) { + result_c[j] = libyuv::FixedDiv_C(num[j], div[j]); + EXPECT_NEAR(result_c[j], result_opt[j], 1); + } +} + +TEST_F(LibYUVBaseTest, TestFixedDiv1_Opt) { + int num[1280]; + int div[1280]; + int result_opt[1280]; + int result_c[1280]; + + MemRandomize(reinterpret_cast<uint8*>(&num[0]), sizeof(num)); + MemRandomize(reinterpret_cast<uint8*>(&div[0]), sizeof(div)); + for (int j = 0; j < 1280; ++j) { + num[j] &= 4095; // Make numerator smaller. + div[j] &= 4095; // Make divisor smaller. + if (div[j] <= 1) { + div[j] = 1280; + } + } + + int has_x86 = TestCpuFlag(kCpuHasX86); + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + if (has_x86) { + for (int j = 0; j < 1280; ++j) { + result_opt[j] = libyuv::FixedDiv1(num[j], div[j]); + } + } else { + for (int j = 0; j < 1280; ++j) { + result_opt[j] = libyuv::FixedDiv1_C(num[j], div[j]); + } + } + } + for (int j = 0; j < 1280; ++j) { + result_c[j] = libyuv::FixedDiv1_C(num[j], div[j]); + EXPECT_NEAR(result_c[j], result_opt[j], 1); + } +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/planar_test.cc b/media/libyuv/unit_test/planar_test.cc new file mode 100644 index 000000000..bc0eebb52 --- /dev/null +++ b/media/libyuv/unit_test/planar_test.cc @@ -0,0 +1,2361 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <time.h> + +#include "libyuv/compare.h" +#include "libyuv/convert.h" +#include "libyuv/convert_argb.h" +#include "libyuv/convert_from.h" +#include "libyuv/convert_from_argb.h" +#include "libyuv/cpu_id.h" +#include "libyuv/planar_functions.h" +#include "libyuv/rotate.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +TEST_F(LibYUVPlanarTest, TestAttenuate) { + const int kSize = 1280 * 4; + align_buffer_page_end(orig_pixels, kSize); + align_buffer_page_end(atten_pixels, kSize); + align_buffer_page_end(unatten_pixels, kSize); + align_buffer_page_end(atten2_pixels, kSize); + + // Test unattenuation clamps + orig_pixels[0 * 4 + 0] = 200u; + orig_pixels[0 * 4 + 1] = 129u; + orig_pixels[0 * 4 + 2] = 127u; + orig_pixels[0 * 4 + 3] = 128u; + // Test unattenuation transparent and opaque are unaffected + orig_pixels[1 * 4 + 0] = 16u; + orig_pixels[1 * 4 + 1] = 64u; + orig_pixels[1 * 4 + 2] = 192u; + orig_pixels[1 * 4 + 3] = 0u; + orig_pixels[2 * 4 + 0] = 16u; + orig_pixels[2 * 4 + 1] = 64u; + orig_pixels[2 * 4 + 2] = 192u; + orig_pixels[2 * 4 + 3] = 255u; + orig_pixels[3 * 4 + 0] = 16u; + orig_pixels[3 * 4 + 1] = 64u; + orig_pixels[3 * 4 + 2] = 192u; + orig_pixels[3 * 4 + 3] = 128u; + ARGBUnattenuate(orig_pixels, 0, unatten_pixels, 0, 4, 1); + EXPECT_EQ(255u, unatten_pixels[0 * 4 + 0]); + EXPECT_EQ(255u, unatten_pixels[0 * 4 + 1]); + EXPECT_EQ(254u, unatten_pixels[0 * 4 + 2]); + EXPECT_EQ(128u, unatten_pixels[0 * 4 + 3]); + EXPECT_EQ(0u, unatten_pixels[1 * 4 + 0]); + EXPECT_EQ(0u, unatten_pixels[1 * 4 + 1]); + EXPECT_EQ(0u, unatten_pixels[1 * 4 + 2]); + EXPECT_EQ(0u, unatten_pixels[1 * 4 + 3]); + EXPECT_EQ(16u, unatten_pixels[2 * 4 + 0]); + EXPECT_EQ(64u, unatten_pixels[2 * 4 + 1]); + EXPECT_EQ(192u, unatten_pixels[2 * 4 + 2]); + EXPECT_EQ(255u, unatten_pixels[2 * 4 + 3]); + EXPECT_EQ(32u, unatten_pixels[3 * 4 + 0]); + EXPECT_EQ(128u, unatten_pixels[3 * 4 + 1]); + EXPECT_EQ(255u, unatten_pixels[3 * 4 + 2]); + EXPECT_EQ(128u, unatten_pixels[3 * 4 + 3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i * 4 + 0] = i; + orig_pixels[i * 4 + 1] = i / 2; + orig_pixels[i * 4 + 2] = i / 3; + orig_pixels[i * 4 + 3] = i; + } + ARGBAttenuate(orig_pixels, 0, atten_pixels, 0, 1280, 1); + ARGBUnattenuate(atten_pixels, 0, unatten_pixels, 0, 1280, 1); + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBAttenuate(unatten_pixels, 0, atten2_pixels, 0, 1280, 1); + } + for (int i = 0; i < 1280; ++i) { + EXPECT_NEAR(atten_pixels[i * 4 + 0], atten2_pixels[i * 4 + 0], 2); + EXPECT_NEAR(atten_pixels[i * 4 + 1], atten2_pixels[i * 4 + 1], 2); + EXPECT_NEAR(atten_pixels[i * 4 + 2], atten2_pixels[i * 4 + 2], 2); + EXPECT_NEAR(atten_pixels[i * 4 + 3], atten2_pixels[i * 4 + 3], 2); + } + // Make sure transparent, 50% and opaque are fully accurate. + EXPECT_EQ(0, atten_pixels[0 * 4 + 0]); + EXPECT_EQ(0, atten_pixels[0 * 4 + 1]); + EXPECT_EQ(0, atten_pixels[0 * 4 + 2]); + EXPECT_EQ(0, atten_pixels[0 * 4 + 3]); + EXPECT_EQ(64, atten_pixels[128 * 4 + 0]); + EXPECT_EQ(32, atten_pixels[128 * 4 + 1]); + EXPECT_EQ(21, atten_pixels[128 * 4 + 2]); + EXPECT_EQ(128, atten_pixels[128 * 4 + 3]); + EXPECT_NEAR(255, atten_pixels[255 * 4 + 0], 1); + EXPECT_NEAR(127, atten_pixels[255 * 4 + 1], 1); + EXPECT_NEAR(85, atten_pixels[255 * 4 + 2], 1); + EXPECT_EQ(255, atten_pixels[255 * 4 + 3]); + + free_aligned_buffer_page_end(atten2_pixels); + free_aligned_buffer_page_end(unatten_pixels); + free_aligned_buffer_page_end(atten_pixels); + free_aligned_buffer_page_end(orig_pixels); +} + +static int TestAttenuateI(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBAttenuate(src_argb + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBAttenuate(src_argb + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBAttenuate_Any) { + int max_diff = TestAttenuateI(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBAttenuate_Unaligned) { + int max_diff = TestAttenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBAttenuate_Invert) { + int max_diff = TestAttenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBAttenuate_Opt) { + int max_diff = TestAttenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_LE(max_diff, 2); +} + +static int TestUnattenuateI(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb[i + off] = (fastrand() & 0xff); + } + ARGBAttenuate(src_argb + off, kStride, + src_argb + off, kStride, + width, height); + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBUnattenuate(src_argb + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBUnattenuate(src_argb + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBUnattenuate_Any) { + int max_diff = TestUnattenuateI(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBUnattenuate_Unaligned) { + int max_diff = TestUnattenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBUnattenuate_Invert) { + int max_diff = TestUnattenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, ARGBUnattenuate_Opt) { + int max_diff = TestUnattenuateI(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_LE(max_diff, 2); +} + +TEST_F(LibYUVPlanarTest, TestARGBComputeCumulativeSum) { + SIMD_ALIGNED(uint8 orig_pixels[16][16][4]); + SIMD_ALIGNED(int32 added_pixels[16][16][4]); + + for (int y = 0; y < 16; ++y) { + for (int x = 0; x < 16; ++x) { + orig_pixels[y][x][0] = 1u; + orig_pixels[y][x][1] = 2u; + orig_pixels[y][x][2] = 3u; + orig_pixels[y][x][3] = 255u; + } + } + + ARGBComputeCumulativeSum(&orig_pixels[0][0][0], 16 * 4, + &added_pixels[0][0][0], 16 * 4, + 16, 16); + + for (int y = 0; y < 16; ++y) { + for (int x = 0; x < 16; ++x) { + EXPECT_EQ((x + 1) * (y + 1), added_pixels[y][x][0]); + EXPECT_EQ((x + 1) * (y + 1) * 2, added_pixels[y][x][1]); + EXPECT_EQ((x + 1) * (y + 1) * 3, added_pixels[y][x][2]); + EXPECT_EQ((x + 1) * (y + 1) * 255, added_pixels[y][x][3]); + } + } +} + +TEST_F(LibYUVPlanarTest, TestARGBGray) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test black + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 0u; + orig_pixels[3][2] = 0u; + orig_pixels[3][3] = 255u; + // Test white + orig_pixels[4][0] = 255u; + orig_pixels[4][1] = 255u; + orig_pixels[4][2] = 255u; + orig_pixels[4][3] = 255u; + // Test color + orig_pixels[5][0] = 16u; + orig_pixels[5][1] = 64u; + orig_pixels[5][2] = 192u; + orig_pixels[5][3] = 224u; + // Do 16 to test asm version. + ARGBGray(&orig_pixels[0][0], 0, 0, 0, 16, 1); + EXPECT_EQ(30u, orig_pixels[0][0]); + EXPECT_EQ(30u, orig_pixels[0][1]); + EXPECT_EQ(30u, orig_pixels[0][2]); + EXPECT_EQ(128u, orig_pixels[0][3]); + EXPECT_EQ(149u, orig_pixels[1][0]); + EXPECT_EQ(149u, orig_pixels[1][1]); + EXPECT_EQ(149u, orig_pixels[1][2]); + EXPECT_EQ(0u, orig_pixels[1][3]); + EXPECT_EQ(76u, orig_pixels[2][0]); + EXPECT_EQ(76u, orig_pixels[2][1]); + EXPECT_EQ(76u, orig_pixels[2][2]); + EXPECT_EQ(255u, orig_pixels[2][3]); + EXPECT_EQ(0u, orig_pixels[3][0]); + EXPECT_EQ(0u, orig_pixels[3][1]); + EXPECT_EQ(0u, orig_pixels[3][2]); + EXPECT_EQ(255u, orig_pixels[3][3]); + EXPECT_EQ(255u, orig_pixels[4][0]); + EXPECT_EQ(255u, orig_pixels[4][1]); + EXPECT_EQ(255u, orig_pixels[4][2]); + EXPECT_EQ(255u, orig_pixels[4][3]); + EXPECT_EQ(96u, orig_pixels[5][0]); + EXPECT_EQ(96u, orig_pixels[5][1]); + EXPECT_EQ(96u, orig_pixels[5][2]); + EXPECT_EQ(224u, orig_pixels[5][3]); + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBGray(&orig_pixels[0][0], 0, 0, 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBGrayTo) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 gray_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test black + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 0u; + orig_pixels[3][2] = 0u; + orig_pixels[3][3] = 255u; + // Test white + orig_pixels[4][0] = 255u; + orig_pixels[4][1] = 255u; + orig_pixels[4][2] = 255u; + orig_pixels[4][3] = 255u; + // Test color + orig_pixels[5][0] = 16u; + orig_pixels[5][1] = 64u; + orig_pixels[5][2] = 192u; + orig_pixels[5][3] = 224u; + // Do 16 to test asm version. + ARGBGrayTo(&orig_pixels[0][0], 0, &gray_pixels[0][0], 0, 16, 1); + EXPECT_EQ(30u, gray_pixels[0][0]); + EXPECT_EQ(30u, gray_pixels[0][1]); + EXPECT_EQ(30u, gray_pixels[0][2]); + EXPECT_EQ(128u, gray_pixels[0][3]); + EXPECT_EQ(149u, gray_pixels[1][0]); + EXPECT_EQ(149u, gray_pixels[1][1]); + EXPECT_EQ(149u, gray_pixels[1][2]); + EXPECT_EQ(0u, gray_pixels[1][3]); + EXPECT_EQ(76u, gray_pixels[2][0]); + EXPECT_EQ(76u, gray_pixels[2][1]); + EXPECT_EQ(76u, gray_pixels[2][2]); + EXPECT_EQ(255u, gray_pixels[2][3]); + EXPECT_EQ(0u, gray_pixels[3][0]); + EXPECT_EQ(0u, gray_pixels[3][1]); + EXPECT_EQ(0u, gray_pixels[3][2]); + EXPECT_EQ(255u, gray_pixels[3][3]); + EXPECT_EQ(255u, gray_pixels[4][0]); + EXPECT_EQ(255u, gray_pixels[4][1]); + EXPECT_EQ(255u, gray_pixels[4][2]); + EXPECT_EQ(255u, gray_pixels[4][3]); + EXPECT_EQ(96u, gray_pixels[5][0]); + EXPECT_EQ(96u, gray_pixels[5][1]); + EXPECT_EQ(96u, gray_pixels[5][2]); + EXPECT_EQ(224u, gray_pixels[5][3]); + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBGrayTo(&orig_pixels[0][0], 0, &gray_pixels[0][0], 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBSepia) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test black + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 0u; + orig_pixels[3][2] = 0u; + orig_pixels[3][3] = 255u; + // Test white + orig_pixels[4][0] = 255u; + orig_pixels[4][1] = 255u; + orig_pixels[4][2] = 255u; + orig_pixels[4][3] = 255u; + // Test color + orig_pixels[5][0] = 16u; + orig_pixels[5][1] = 64u; + orig_pixels[5][2] = 192u; + orig_pixels[5][3] = 224u; + // Do 16 to test asm version. + ARGBSepia(&orig_pixels[0][0], 0, 0, 0, 16, 1); + EXPECT_EQ(33u, orig_pixels[0][0]); + EXPECT_EQ(43u, orig_pixels[0][1]); + EXPECT_EQ(47u, orig_pixels[0][2]); + EXPECT_EQ(128u, orig_pixels[0][3]); + EXPECT_EQ(135u, orig_pixels[1][0]); + EXPECT_EQ(175u, orig_pixels[1][1]); + EXPECT_EQ(195u, orig_pixels[1][2]); + EXPECT_EQ(0u, orig_pixels[1][3]); + EXPECT_EQ(69u, orig_pixels[2][0]); + EXPECT_EQ(89u, orig_pixels[2][1]); + EXPECT_EQ(99u, orig_pixels[2][2]); + EXPECT_EQ(255u, orig_pixels[2][3]); + EXPECT_EQ(0u, orig_pixels[3][0]); + EXPECT_EQ(0u, orig_pixels[3][1]); + EXPECT_EQ(0u, orig_pixels[3][2]); + EXPECT_EQ(255u, orig_pixels[3][3]); + EXPECT_EQ(239u, orig_pixels[4][0]); + EXPECT_EQ(255u, orig_pixels[4][1]); + EXPECT_EQ(255u, orig_pixels[4][2]); + EXPECT_EQ(255u, orig_pixels[4][3]); + EXPECT_EQ(88u, orig_pixels[5][0]); + EXPECT_EQ(114u, orig_pixels[5][1]); + EXPECT_EQ(127u, orig_pixels[5][2]); + EXPECT_EQ(224u, orig_pixels[5][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBSepia(&orig_pixels[0][0], 0, 0, 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBColorMatrix) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_opt[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_c[1280][4]); + + // Matrix for Sepia. + SIMD_ALIGNED(static const int8 kRGBToSepia[]) = { + 17 / 2, 68 / 2, 35 / 2, 0, + 22 / 2, 88 / 2, 45 / 2, 0, + 24 / 2, 98 / 2, 50 / 2, 0, + 0, 0, 0, 64, // Copy alpha. + }; + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test color + orig_pixels[3][0] = 16u; + orig_pixels[3][1] = 64u; + orig_pixels[3][2] = 192u; + orig_pixels[3][3] = 224u; + // Do 16 to test asm version. + ARGBColorMatrix(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + &kRGBToSepia[0], 16, 1); + EXPECT_EQ(31u, dst_pixels_opt[0][0]); + EXPECT_EQ(43u, dst_pixels_opt[0][1]); + EXPECT_EQ(47u, dst_pixels_opt[0][2]); + EXPECT_EQ(128u, dst_pixels_opt[0][3]); + EXPECT_EQ(135u, dst_pixels_opt[1][0]); + EXPECT_EQ(175u, dst_pixels_opt[1][1]); + EXPECT_EQ(195u, dst_pixels_opt[1][2]); + EXPECT_EQ(0u, dst_pixels_opt[1][3]); + EXPECT_EQ(67u, dst_pixels_opt[2][0]); + EXPECT_EQ(87u, dst_pixels_opt[2][1]); + EXPECT_EQ(99u, dst_pixels_opt[2][2]); + EXPECT_EQ(255u, dst_pixels_opt[2][3]); + EXPECT_EQ(87u, dst_pixels_opt[3][0]); + EXPECT_EQ(112u, dst_pixels_opt[3][1]); + EXPECT_EQ(127u, dst_pixels_opt[3][2]); + EXPECT_EQ(224u, dst_pixels_opt[3][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + MaskCpuFlags(disable_cpu_flags_); + ARGBColorMatrix(&orig_pixels[0][0], 0, &dst_pixels_c[0][0], 0, + &kRGBToSepia[0], 1280, 1); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBColorMatrix(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + &kRGBToSepia[0], 1280, 1); + } + + for (int i = 0; i < 1280; ++i) { + EXPECT_EQ(dst_pixels_c[i][0], dst_pixels_opt[i][0]); + EXPECT_EQ(dst_pixels_c[i][1], dst_pixels_opt[i][1]); + EXPECT_EQ(dst_pixels_c[i][2], dst_pixels_opt[i][2]); + EXPECT_EQ(dst_pixels_c[i][3], dst_pixels_opt[i][3]); + } +} + +TEST_F(LibYUVPlanarTest, TestRGBColorMatrix) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + + // Matrix for Sepia. + SIMD_ALIGNED(static const int8 kRGBToSepia[]) = { + 17, 68, 35, 0, + 22, 88, 45, 0, + 24, 98, 50, 0, + 0, 0, 0, 0, // Unused but makes matrix 16 bytes. + }; + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test color + orig_pixels[3][0] = 16u; + orig_pixels[3][1] = 64u; + orig_pixels[3][2] = 192u; + orig_pixels[3][3] = 224u; + // Do 16 to test asm version. + RGBColorMatrix(&orig_pixels[0][0], 0, &kRGBToSepia[0], 0, 0, 16, 1); + EXPECT_EQ(31u, orig_pixels[0][0]); + EXPECT_EQ(43u, orig_pixels[0][1]); + EXPECT_EQ(47u, orig_pixels[0][2]); + EXPECT_EQ(128u, orig_pixels[0][3]); + EXPECT_EQ(135u, orig_pixels[1][0]); + EXPECT_EQ(175u, orig_pixels[1][1]); + EXPECT_EQ(195u, orig_pixels[1][2]); + EXPECT_EQ(0u, orig_pixels[1][3]); + EXPECT_EQ(67u, orig_pixels[2][0]); + EXPECT_EQ(87u, orig_pixels[2][1]); + EXPECT_EQ(99u, orig_pixels[2][2]); + EXPECT_EQ(255u, orig_pixels[2][3]); + EXPECT_EQ(87u, orig_pixels[3][0]); + EXPECT_EQ(112u, orig_pixels[3][1]); + EXPECT_EQ(127u, orig_pixels[3][2]); + EXPECT_EQ(224u, orig_pixels[3][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + RGBColorMatrix(&orig_pixels[0][0], 0, &kRGBToSepia[0], 0, 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBColorTable) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Matrix for Sepia. + static const uint8 kARGBTable[256 * 4] = { + 1u, 2u, 3u, 4u, + 5u, 6u, 7u, 8u, + 9u, 10u, 11u, 12u, + 13u, 14u, 15u, 16u, + }; + + orig_pixels[0][0] = 0u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 0u; + orig_pixels[1][0] = 1u; + orig_pixels[1][1] = 1u; + orig_pixels[1][2] = 1u; + orig_pixels[1][3] = 1u; + orig_pixels[2][0] = 2u; + orig_pixels[2][1] = 2u; + orig_pixels[2][2] = 2u; + orig_pixels[2][3] = 2u; + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 1u; + orig_pixels[3][2] = 2u; + orig_pixels[3][3] = 3u; + // Do 16 to test asm version. + ARGBColorTable(&orig_pixels[0][0], 0, &kARGBTable[0], 0, 0, 16, 1); + EXPECT_EQ(1u, orig_pixels[0][0]); + EXPECT_EQ(2u, orig_pixels[0][1]); + EXPECT_EQ(3u, orig_pixels[0][2]); + EXPECT_EQ(4u, orig_pixels[0][3]); + EXPECT_EQ(5u, orig_pixels[1][0]); + EXPECT_EQ(6u, orig_pixels[1][1]); + EXPECT_EQ(7u, orig_pixels[1][2]); + EXPECT_EQ(8u, orig_pixels[1][3]); + EXPECT_EQ(9u, orig_pixels[2][0]); + EXPECT_EQ(10u, orig_pixels[2][1]); + EXPECT_EQ(11u, orig_pixels[2][2]); + EXPECT_EQ(12u, orig_pixels[2][3]); + EXPECT_EQ(1u, orig_pixels[3][0]); + EXPECT_EQ(6u, orig_pixels[3][1]); + EXPECT_EQ(11u, orig_pixels[3][2]); + EXPECT_EQ(16u, orig_pixels[3][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBColorTable(&orig_pixels[0][0], 0, &kARGBTable[0], 0, 0, 1280, 1); + } +} + +// Same as TestARGBColorTable except alpha does not change. +TEST_F(LibYUVPlanarTest, TestRGBColorTable) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + // Matrix for Sepia. + static const uint8 kARGBTable[256 * 4] = { + 1u, 2u, 3u, 4u, + 5u, 6u, 7u, 8u, + 9u, 10u, 11u, 12u, + 13u, 14u, 15u, 16u, + }; + + orig_pixels[0][0] = 0u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 0u; + orig_pixels[1][0] = 1u; + orig_pixels[1][1] = 1u; + orig_pixels[1][2] = 1u; + orig_pixels[1][3] = 1u; + orig_pixels[2][0] = 2u; + orig_pixels[2][1] = 2u; + orig_pixels[2][2] = 2u; + orig_pixels[2][3] = 2u; + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 1u; + orig_pixels[3][2] = 2u; + orig_pixels[3][3] = 3u; + // Do 16 to test asm version. + RGBColorTable(&orig_pixels[0][0], 0, &kARGBTable[0], 0, 0, 16, 1); + EXPECT_EQ(1u, orig_pixels[0][0]); + EXPECT_EQ(2u, orig_pixels[0][1]); + EXPECT_EQ(3u, orig_pixels[0][2]); + EXPECT_EQ(0u, orig_pixels[0][3]); // Alpha unchanged. + EXPECT_EQ(5u, orig_pixels[1][0]); + EXPECT_EQ(6u, orig_pixels[1][1]); + EXPECT_EQ(7u, orig_pixels[1][2]); + EXPECT_EQ(1u, orig_pixels[1][3]); // Alpha unchanged. + EXPECT_EQ(9u, orig_pixels[2][0]); + EXPECT_EQ(10u, orig_pixels[2][1]); + EXPECT_EQ(11u, orig_pixels[2][2]); + EXPECT_EQ(2u, orig_pixels[2][3]); // Alpha unchanged. + EXPECT_EQ(1u, orig_pixels[3][0]); + EXPECT_EQ(6u, orig_pixels[3][1]); + EXPECT_EQ(11u, orig_pixels[3][2]); + EXPECT_EQ(3u, orig_pixels[3][3]); // Alpha unchanged. + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + RGBColorTable(&orig_pixels[0][0], 0, &kARGBTable[0], 0, 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBQuantize) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + ARGBQuantize(&orig_pixels[0][0], 0, + (65536 + (8 / 2)) / 8, 8, 8 / 2, 0, 0, 1280, 1); + + for (int i = 0; i < 1280; ++i) { + EXPECT_EQ((i / 8 * 8 + 8 / 2) & 255, orig_pixels[i][0]); + EXPECT_EQ((i / 2 / 8 * 8 + 8 / 2) & 255, orig_pixels[i][1]); + EXPECT_EQ((i / 3 / 8 * 8 + 8 / 2) & 255, orig_pixels[i][2]); + EXPECT_EQ(i & 255, orig_pixels[i][3]); + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBQuantize(&orig_pixels[0][0], 0, + (65536 + (8 / 2)) / 8, 8, 8 / 2, 0, 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBMirror) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels[1280][4]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i / 4; + } + ARGBMirror(&orig_pixels[0][0], 0, &dst_pixels[0][0], 0, 1280, 1); + + for (int i = 0; i < 1280; ++i) { + EXPECT_EQ(i & 255, dst_pixels[1280 - 1 - i][0]); + EXPECT_EQ((i / 2) & 255, dst_pixels[1280 - 1 - i][1]); + EXPECT_EQ((i / 3) & 255, dst_pixels[1280 - 1 - i][2]); + EXPECT_EQ((i / 4) & 255, dst_pixels[1280 - 1 - i][3]); + } + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBMirror(&orig_pixels[0][0], 0, &dst_pixels[0][0], 0, 1280, 1); + } +} + +TEST_F(LibYUVPlanarTest, TestShade) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 shade_pixels[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + orig_pixels[0][0] = 10u; + orig_pixels[0][1] = 20u; + orig_pixels[0][2] = 40u; + orig_pixels[0][3] = 80u; + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 0u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 255u; + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 0u; + orig_pixels[2][3] = 0u; + orig_pixels[3][0] = 0u; + orig_pixels[3][1] = 0u; + orig_pixels[3][2] = 0u; + orig_pixels[3][3] = 0u; + // Do 8 pixels to allow opt version to be used. + ARGBShade(&orig_pixels[0][0], 0, &shade_pixels[0][0], 0, 8, 1, 0x80ffffff); + EXPECT_EQ(10u, shade_pixels[0][0]); + EXPECT_EQ(20u, shade_pixels[0][1]); + EXPECT_EQ(40u, shade_pixels[0][2]); + EXPECT_EQ(40u, shade_pixels[0][3]); + EXPECT_EQ(0u, shade_pixels[1][0]); + EXPECT_EQ(0u, shade_pixels[1][1]); + EXPECT_EQ(0u, shade_pixels[1][2]); + EXPECT_EQ(128u, shade_pixels[1][3]); + EXPECT_EQ(0u, shade_pixels[2][0]); + EXPECT_EQ(0u, shade_pixels[2][1]); + EXPECT_EQ(0u, shade_pixels[2][2]); + EXPECT_EQ(0u, shade_pixels[2][3]); + EXPECT_EQ(0u, shade_pixels[3][0]); + EXPECT_EQ(0u, shade_pixels[3][1]); + EXPECT_EQ(0u, shade_pixels[3][2]); + EXPECT_EQ(0u, shade_pixels[3][3]); + + ARGBShade(&orig_pixels[0][0], 0, &shade_pixels[0][0], 0, 8, 1, 0x80808080); + EXPECT_EQ(5u, shade_pixels[0][0]); + EXPECT_EQ(10u, shade_pixels[0][1]); + EXPECT_EQ(20u, shade_pixels[0][2]); + EXPECT_EQ(40u, shade_pixels[0][3]); + + ARGBShade(&orig_pixels[0][0], 0, &shade_pixels[0][0], 0, 8, 1, 0x10204080); + EXPECT_EQ(5u, shade_pixels[0][0]); + EXPECT_EQ(5u, shade_pixels[0][1]); + EXPECT_EQ(5u, shade_pixels[0][2]); + EXPECT_EQ(5u, shade_pixels[0][3]); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBShade(&orig_pixels[0][0], 0, &shade_pixels[0][0], 0, 1280, 1, + 0x80808080); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBInterpolate) { + SIMD_ALIGNED(uint8 orig_pixels_0[1280][4]); + SIMD_ALIGNED(uint8 orig_pixels_1[1280][4]); + SIMD_ALIGNED(uint8 interpolate_pixels[1280][4]); + memset(orig_pixels_0, 0, sizeof(orig_pixels_0)); + memset(orig_pixels_1, 0, sizeof(orig_pixels_1)); + + orig_pixels_0[0][0] = 16u; + orig_pixels_0[0][1] = 32u; + orig_pixels_0[0][2] = 64u; + orig_pixels_0[0][3] = 128u; + orig_pixels_0[1][0] = 0u; + orig_pixels_0[1][1] = 0u; + orig_pixels_0[1][2] = 0u; + orig_pixels_0[1][3] = 255u; + orig_pixels_0[2][0] = 0u; + orig_pixels_0[2][1] = 0u; + orig_pixels_0[2][2] = 0u; + orig_pixels_0[2][3] = 0u; + orig_pixels_0[3][0] = 0u; + orig_pixels_0[3][1] = 0u; + orig_pixels_0[3][2] = 0u; + orig_pixels_0[3][3] = 0u; + + orig_pixels_1[0][0] = 0u; + orig_pixels_1[0][1] = 0u; + orig_pixels_1[0][2] = 0u; + orig_pixels_1[0][3] = 0u; + orig_pixels_1[1][0] = 0u; + orig_pixels_1[1][1] = 0u; + orig_pixels_1[1][2] = 0u; + orig_pixels_1[1][3] = 0u; + orig_pixels_1[2][0] = 0u; + orig_pixels_1[2][1] = 0u; + orig_pixels_1[2][2] = 0u; + orig_pixels_1[2][3] = 0u; + orig_pixels_1[3][0] = 255u; + orig_pixels_1[3][1] = 255u; + orig_pixels_1[3][2] = 255u; + orig_pixels_1[3][3] = 255u; + + ARGBInterpolate(&orig_pixels_0[0][0], 0, &orig_pixels_1[0][0], 0, + &interpolate_pixels[0][0], 0, 4, 1, 128); + EXPECT_EQ(8u, interpolate_pixels[0][0]); + EXPECT_EQ(16u, interpolate_pixels[0][1]); + EXPECT_EQ(32u, interpolate_pixels[0][2]); + EXPECT_EQ(64u, interpolate_pixels[0][3]); + EXPECT_EQ(0u, interpolate_pixels[1][0]); + EXPECT_EQ(0u, interpolate_pixels[1][1]); + EXPECT_EQ(0u, interpolate_pixels[1][2]); + EXPECT_EQ(128u, interpolate_pixels[1][3]); + EXPECT_EQ(0u, interpolate_pixels[2][0]); + EXPECT_EQ(0u, interpolate_pixels[2][1]); + EXPECT_EQ(0u, interpolate_pixels[2][2]); + EXPECT_EQ(0u, interpolate_pixels[2][3]); + EXPECT_EQ(128u, interpolate_pixels[3][0]); + EXPECT_EQ(128u, interpolate_pixels[3][1]); + EXPECT_EQ(128u, interpolate_pixels[3][2]); + EXPECT_EQ(128u, interpolate_pixels[3][3]); + + ARGBInterpolate(&orig_pixels_0[0][0], 0, &orig_pixels_1[0][0], 0, + &interpolate_pixels[0][0], 0, 4, 1, 0); + EXPECT_EQ(16u, interpolate_pixels[0][0]); + EXPECT_EQ(32u, interpolate_pixels[0][1]); + EXPECT_EQ(64u, interpolate_pixels[0][2]); + EXPECT_EQ(128u, interpolate_pixels[0][3]); + + ARGBInterpolate(&orig_pixels_0[0][0], 0, &orig_pixels_1[0][0], 0, + &interpolate_pixels[0][0], 0, 4, 1, 192); + + EXPECT_EQ(4u, interpolate_pixels[0][0]); + EXPECT_EQ(8u, interpolate_pixels[0][1]); + EXPECT_EQ(16u, interpolate_pixels[0][2]); + EXPECT_EQ(32u, interpolate_pixels[0][3]); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBInterpolate(&orig_pixels_0[0][0], 0, &orig_pixels_1[0][0], 0, + &interpolate_pixels[0][0], 0, 1280, 1, 128); + } +} + +TEST_F(LibYUVPlanarTest, TestInterpolatePlane) { + SIMD_ALIGNED(uint8 orig_pixels_0[1280]); + SIMD_ALIGNED(uint8 orig_pixels_1[1280]); + SIMD_ALIGNED(uint8 interpolate_pixels[1280]); + memset(orig_pixels_0, 0, sizeof(orig_pixels_0)); + memset(orig_pixels_1, 0, sizeof(orig_pixels_1)); + + orig_pixels_0[0] = 16u; + orig_pixels_0[1] = 32u; + orig_pixels_0[2] = 64u; + orig_pixels_0[3] = 128u; + orig_pixels_0[4] = 0u; + orig_pixels_0[5] = 0u; + orig_pixels_0[6] = 0u; + orig_pixels_0[7] = 255u; + orig_pixels_0[8] = 0u; + orig_pixels_0[9] = 0u; + orig_pixels_0[10] = 0u; + orig_pixels_0[11] = 0u; + orig_pixels_0[12] = 0u; + orig_pixels_0[13] = 0u; + orig_pixels_0[14] = 0u; + orig_pixels_0[15] = 0u; + + orig_pixels_1[0] = 0u; + orig_pixels_1[1] = 0u; + orig_pixels_1[2] = 0u; + orig_pixels_1[3] = 0u; + orig_pixels_1[4] = 0u; + orig_pixels_1[5] = 0u; + orig_pixels_1[6] = 0u; + orig_pixels_1[7] = 0u; + orig_pixels_1[8] = 0u; + orig_pixels_1[9] = 0u; + orig_pixels_1[10] = 0u; + orig_pixels_1[11] = 0u; + orig_pixels_1[12] = 255u; + orig_pixels_1[13] = 255u; + orig_pixels_1[14] = 255u; + orig_pixels_1[15] = 255u; + + InterpolatePlane(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0, + &interpolate_pixels[0], 0, 16, 1, 128); + EXPECT_EQ(8u, interpolate_pixels[0]); + EXPECT_EQ(16u, interpolate_pixels[1]); + EXPECT_EQ(32u, interpolate_pixels[2]); + EXPECT_EQ(64u, interpolate_pixels[3]); + EXPECT_EQ(0u, interpolate_pixels[4]); + EXPECT_EQ(0u, interpolate_pixels[5]); + EXPECT_EQ(0u, interpolate_pixels[6]); + EXPECT_EQ(128u, interpolate_pixels[7]); + EXPECT_EQ(0u, interpolate_pixels[8]); + EXPECT_EQ(0u, interpolate_pixels[9]); + EXPECT_EQ(0u, interpolate_pixels[10]); + EXPECT_EQ(0u, interpolate_pixels[11]); + EXPECT_EQ(128u, interpolate_pixels[12]); + EXPECT_EQ(128u, interpolate_pixels[13]); + EXPECT_EQ(128u, interpolate_pixels[14]); + EXPECT_EQ(128u, interpolate_pixels[15]); + + InterpolatePlane(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0, + &interpolate_pixels[0], 0, 16, 1, 0); + EXPECT_EQ(16u, interpolate_pixels[0]); + EXPECT_EQ(32u, interpolate_pixels[1]); + EXPECT_EQ(64u, interpolate_pixels[2]); + EXPECT_EQ(128u, interpolate_pixels[3]); + + InterpolatePlane(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0, + &interpolate_pixels[0], 0, 16, 1, 192); + + EXPECT_EQ(4u, interpolate_pixels[0]); + EXPECT_EQ(8u, interpolate_pixels[1]); + EXPECT_EQ(16u, interpolate_pixels[2]); + EXPECT_EQ(32u, interpolate_pixels[3]); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + InterpolatePlane(&orig_pixels_0[0], 0, &orig_pixels_1[0], 0, + &interpolate_pixels[0], 0, 1280, 1, 123); + } +} + +#define TESTTERP(FMT_A, BPP_A, STRIDE_A, \ + FMT_B, BPP_B, STRIDE_B, \ + W1280, TERP, N, NEG, OFF) \ +TEST_F(LibYUVPlanarTest, ARGBInterpolate##TERP##N) { \ + const int kWidth = ((W1280) > 0) ? (W1280) : 1; \ + const int kHeight = benchmark_height_; \ + const int kStrideA = (kWidth * BPP_A + STRIDE_A - 1) / STRIDE_A * STRIDE_A; \ + const int kStrideB = (kWidth * BPP_B + STRIDE_B - 1) / STRIDE_B * STRIDE_B; \ + align_buffer_page_end(src_argb_a, kStrideA * kHeight + OFF); \ + align_buffer_page_end(src_argb_b, kStrideA * kHeight + OFF); \ + align_buffer_page_end(dst_argb_c, kStrideB * kHeight); \ + align_buffer_page_end(dst_argb_opt, kStrideB * kHeight); \ + for (int i = 0; i < kStrideA * kHeight; ++i) { \ + src_argb_a[i + OFF] = (fastrand() & 0xff); \ + src_argb_b[i + OFF] = (fastrand() & 0xff); \ + } \ + MaskCpuFlags(disable_cpu_flags_); \ + ARGBInterpolate(src_argb_a + OFF, kStrideA, \ + src_argb_b + OFF, kStrideA, \ + dst_argb_c, kStrideB, \ + kWidth, NEG kHeight, TERP); \ + MaskCpuFlags(benchmark_cpu_info_); \ + for (int i = 0; i < benchmark_iterations_; ++i) { \ + ARGBInterpolate(src_argb_a + OFF, kStrideA, \ + src_argb_b + OFF, kStrideA, \ + dst_argb_opt, kStrideB, \ + kWidth, NEG kHeight, TERP); \ + } \ + for (int i = 0; i < kStrideB * kHeight; ++i) { \ + EXPECT_EQ(dst_argb_c[i], dst_argb_opt[i]); \ + } \ + free_aligned_buffer_page_end(src_argb_a); \ + free_aligned_buffer_page_end(src_argb_b); \ + free_aligned_buffer_page_end(dst_argb_c); \ + free_aligned_buffer_page_end(dst_argb_opt); \ +} + +#define TESTINTERPOLATE(TERP) \ + TESTTERP(ARGB, 4, 1, ARGB, 4, 1, benchmark_width_ - 1, TERP, _Any, +, 0) \ + TESTTERP(ARGB, 4, 1, ARGB, 4, 1, benchmark_width_, TERP, _Unaligned, +, 1) \ + TESTTERP(ARGB, 4, 1, ARGB, 4, 1, benchmark_width_, TERP, _Invert, -, 0) \ + TESTTERP(ARGB, 4, 1, ARGB, 4, 1, benchmark_width_, TERP, _Opt, +, 0) + +TESTINTERPOLATE(0) +TESTINTERPOLATE(64) +TESTINTERPOLATE(128) +TESTINTERPOLATE(192) +TESTINTERPOLATE(255) + +static int TestBlend(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(src_argb_b, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + src_argb_b[i + off] = (fastrand() & 0xff); + } + ARGBAttenuate(src_argb_a + off, kStride, src_argb_a + off, kStride, width, + height); + ARGBAttenuate(src_argb_b + off, kStride, src_argb_b + off, kStride, width, + height); + memset(dst_argb_c, 255, kStride * height); + memset(dst_argb_opt, 255, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBBlend(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBBlend(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(src_argb_b); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBBlend_Any) { + int max_diff = TestBlend(benchmark_width_ - 4, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlend_Unaligned) { + int max_diff = TestBlend(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlend_Invert) { + int max_diff = TestBlend(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlend_Opt) { + int max_diff = TestBlend(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +static void TestBlendPlane(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 1; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(src_argb_b, kStride * height + off); + align_buffer_page_end(src_argb_alpha, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height + off); + align_buffer_page_end(dst_argb_opt, kStride * height + off); + memset(dst_argb_c, 255, kStride * height + off); + memset(dst_argb_opt, 255, kStride * height + off); + + // Test source is maintained exactly if alpha is 255. + for (int i = 0; i < width; ++i) { + src_argb_a[i + off] = i & 255; + src_argb_b[i + off] = 255 - (i & 255); + } + memset(src_argb_alpha + off, 255, width); + BlendPlane(src_argb_a + off, width, + src_argb_b + off, width, + src_argb_alpha + off, width, + dst_argb_opt + off, width, + width, 1); + for (int i = 0; i < width; ++i) { + EXPECT_EQ(src_argb_a[i + off], dst_argb_opt[i + off]); + } + // Test destination is maintained exactly if alpha is 0. + memset(src_argb_alpha + off, 0, width); + BlendPlane(src_argb_a + off, width, + src_argb_b + off, width, + src_argb_alpha + off, width, + dst_argb_opt + off, width, + width, 1); + for (int i = 0; i < width; ++i) { + EXPECT_EQ(src_argb_b[i + off], dst_argb_opt[i + off]); + } + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + src_argb_b[i + off] = (fastrand() & 0xff); + src_argb_alpha[i + off] = (fastrand() & 0xff); + } + + MaskCpuFlags(disable_cpu_flags); + BlendPlane(src_argb_a + off, width, + src_argb_b + off, width, + src_argb_alpha + off, width, + dst_argb_c + off, width, + width, height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + BlendPlane(src_argb_a + off, width, + src_argb_b + off, width, + src_argb_alpha + off, width, + dst_argb_opt + off, width, + width, height); + } + for (int i = 0; i < kStride * height; ++i) { + EXPECT_EQ(dst_argb_c[i + off], dst_argb_opt[i + off]); + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(src_argb_b); + free_aligned_buffer_page_end(src_argb_alpha); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return; +} + +TEST_F(LibYUVPlanarTest, BlendPlane_Opt) { + TestBlendPlane(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); +} +TEST_F(LibYUVPlanarTest, BlendPlane_Unaligned) { + TestBlendPlane(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); +} +TEST_F(LibYUVPlanarTest, BlendPlane_Any) { + TestBlendPlane(benchmark_width_ - 4, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); +} +TEST_F(LibYUVPlanarTest, BlendPlane_Invert) { + TestBlendPlane(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 1); +} + +#define SUBSAMPLE(v, a) ((((v) + (a) - 1)) / (a)) + +static void TestI420Blend(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + width = ((width) > 0) ? (width) : 1; + const int kStrideUV = SUBSAMPLE(width, 2); + const int kSizeUV = kStrideUV * SUBSAMPLE(height, 2); + align_buffer_page_end(src_y0, width * height + off); + align_buffer_page_end(src_u0, kSizeUV + off); + align_buffer_page_end(src_v0, kSizeUV + off); + align_buffer_page_end(src_y1, width * height + off); + align_buffer_page_end(src_u1, kSizeUV + off); + align_buffer_page_end(src_v1, kSizeUV + off); + align_buffer_page_end(src_a, width * height + off); + align_buffer_page_end(dst_y_c, width * height + off); + align_buffer_page_end(dst_u_c, kSizeUV + off); + align_buffer_page_end(dst_v_c, kSizeUV + off); + align_buffer_page_end(dst_y_opt, width * height + off); + align_buffer_page_end(dst_u_opt, kSizeUV + off); + align_buffer_page_end(dst_v_opt, kSizeUV + off); + + MemRandomize(src_y0, width * height + off); + MemRandomize(src_u0, kSizeUV + off); + MemRandomize(src_v0, kSizeUV + off); + MemRandomize(src_y1, width * height + off); + MemRandomize(src_u1, kSizeUV + off); + MemRandomize(src_v1, kSizeUV + off); + MemRandomize(src_a, width * height + off); + memset(dst_y_c, 255, width * height + off); + memset(dst_u_c, 255, kSizeUV + off); + memset(dst_v_c, 255, kSizeUV + off); + memset(dst_y_opt, 255, width * height + off); + memset(dst_u_opt, 255, kSizeUV + off); + memset(dst_v_opt, 255, kSizeUV + off); + + MaskCpuFlags(disable_cpu_flags); + I420Blend(src_y0 + off, width, + src_u0 + off, kStrideUV, + src_v0 + off, kStrideUV, + src_y1 + off, width, + src_u1 + off, kStrideUV, + src_v1 + off, kStrideUV, + src_a + off, width, + dst_y_c + off, width, + dst_u_c + off, kStrideUV, + dst_v_c + off, kStrideUV, + width, height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + I420Blend(src_y0 + off, width, + src_u0 + off, kStrideUV, + src_v0 + off, kStrideUV, + src_y1 + off, width, + src_u1 + off, kStrideUV, + src_v1 + off, kStrideUV, + src_a + off, width, + dst_y_opt + off, width, + dst_u_opt + off, kStrideUV, + dst_v_opt + off, kStrideUV, + width, height); + } + for (int i = 0; i < width * height; ++i) { + EXPECT_EQ(dst_y_c[i + off], dst_y_opt[i + off]); + } + for (int i = 0; i < kSizeUV; ++i) { + EXPECT_EQ(dst_u_c[i + off], dst_u_opt[i + off]); + EXPECT_EQ(dst_v_c[i + off], dst_v_opt[i + off]); + } + free_aligned_buffer_page_end(src_y0); + free_aligned_buffer_page_end(src_u0); + free_aligned_buffer_page_end(src_v0); + free_aligned_buffer_page_end(src_y1); + free_aligned_buffer_page_end(src_u1); + free_aligned_buffer_page_end(src_v1); + free_aligned_buffer_page_end(src_a); + 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_opt); + free_aligned_buffer_page_end(dst_u_opt); + free_aligned_buffer_page_end(dst_v_opt); + return; +} + +TEST_F(LibYUVPlanarTest, I420Blend_Opt) { + TestI420Blend(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); +} +TEST_F(LibYUVPlanarTest, I420Blend_Unaligned) { + TestI420Blend(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); +} + +// TODO(fbarchard): DISABLED because _Any uses C. Avoid C and re-enable. +TEST_F(LibYUVPlanarTest, DISABLED_I420Blend_Any) { + TestI420Blend(benchmark_width_ - 4, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); +} +TEST_F(LibYUVPlanarTest, I420Blend_Invert) { + TestI420Blend(benchmark_width_, benchmark_height_, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); +} + +TEST_F(LibYUVPlanarTest, TestAffine) { + SIMD_ALIGNED(uint8 orig_pixels_0[1280][4]); + SIMD_ALIGNED(uint8 interpolate_pixels_C[1280][4]); + + for (int i = 0; i < 1280; ++i) { + for (int j = 0; j < 4; ++j) { + orig_pixels_0[i][j] = i; + } + } + + float uv_step[4] = { 0.f, 0.f, 0.75f, 0.f }; + + ARGBAffineRow_C(&orig_pixels_0[0][0], 0, &interpolate_pixels_C[0][0], + uv_step, 1280); + EXPECT_EQ(0u, interpolate_pixels_C[0][0]); + EXPECT_EQ(96u, interpolate_pixels_C[128][0]); + EXPECT_EQ(191u, interpolate_pixels_C[255][3]); + +#if defined(HAS_ARGBAFFINEROW_SSE2) + SIMD_ALIGNED(uint8 interpolate_pixels_Opt[1280][4]); + ARGBAffineRow_SSE2(&orig_pixels_0[0][0], 0, &interpolate_pixels_Opt[0][0], + uv_step, 1280); + EXPECT_EQ(0, memcmp(interpolate_pixels_Opt, interpolate_pixels_C, 1280 * 4)); + + int has_sse2 = TestCpuFlag(kCpuHasSSE2); + if (has_sse2) { + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBAffineRow_SSE2(&orig_pixels_0[0][0], 0, &interpolate_pixels_Opt[0][0], + uv_step, 1280); + } + } +#endif +} + +TEST_F(LibYUVPlanarTest, TestCopyPlane) { + int err = 0; + int yw = benchmark_width_; + int yh = benchmark_height_; + int b = 12; + int i, j; + + int y_plane_size = (yw + b * 2) * (yh + b * 2); + align_buffer_page_end(orig_y, y_plane_size); + align_buffer_page_end(dst_c, y_plane_size); + align_buffer_page_end(dst_opt, y_plane_size); + + memset(orig_y, 0, y_plane_size); + memset(dst_c, 0, y_plane_size); + memset(dst_opt, 0, y_plane_size); + + // Fill image buffers with random data. + for (i = b; i < (yh + b); ++i) { + for (j = b; j < (yw + b); ++j) { + orig_y[i * (yw + b * 2) + j] = fastrand() & 0xff; + } + } + + // Fill destination buffers with random data. + for (i = 0; i < y_plane_size; ++i) { + uint8 random_number = fastrand() & 0x7f; + dst_c[i] = random_number; + dst_opt[i] = dst_c[i]; + } + + int y_off = b * (yw + b * 2) + b; + + int y_st = yw + b * 2; + int stride = 8; + + // Disable all optimizations. + MaskCpuFlags(disable_cpu_flags_); + double c_time = get_time(); + for (j = 0; j < benchmark_iterations_; j++) { + CopyPlane(orig_y + y_off, y_st, dst_c + y_off, stride, yw, yh); + } + c_time = (get_time() - c_time) / benchmark_iterations_; + + // Enable optimizations. + MaskCpuFlags(benchmark_cpu_info_); + double opt_time = get_time(); + for (j = 0; j < benchmark_iterations_; j++) { + CopyPlane(orig_y + y_off, y_st, dst_opt + y_off, stride, yw, yh); + } + opt_time = (get_time() - opt_time) / benchmark_iterations_; + + for (i = 0; i < y_plane_size; ++i) { + if (dst_c[i] != dst_opt[i]) + ++err; + } + + free_aligned_buffer_page_end(orig_y); + free_aligned_buffer_page_end(dst_c); + free_aligned_buffer_page_end(dst_opt); + + EXPECT_EQ(0, err); +} + +static int TestMultiply(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(src_argb_b, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + src_argb_b[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBMultiply(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBMultiply(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(src_argb_b); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBMultiply_Any) { + int max_diff = TestMultiply(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBMultiply_Unaligned) { + int max_diff = TestMultiply(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBMultiply_Invert) { + int max_diff = TestMultiply(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBMultiply_Opt) { + int max_diff = TestMultiply(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +static int TestAdd(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(src_argb_b, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + src_argb_b[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBAdd(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBAdd(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(src_argb_b); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBAdd_Any) { + int max_diff = TestAdd(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBAdd_Unaligned) { + int max_diff = TestAdd(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBAdd_Invert) { + int max_diff = TestAdd(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBAdd_Opt) { + int max_diff = TestAdd(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +static int TestSubtract(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(src_argb_b, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + src_argb_b[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBSubtract(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBSubtract(src_argb_a + off, kStride, + src_argb_b + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(src_argb_b); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBSubtract_Any) { + int max_diff = TestSubtract(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBSubtract_Unaligned) { + int max_diff = TestSubtract(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBSubtract_Invert) { + int max_diff = TestSubtract(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBSubtract_Opt) { + int max_diff = TestSubtract(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_LE(max_diff, 1); +} + +static int TestSobel(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + memset(src_argb_a, 0, kStride * height + off); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBSobel(src_argb_a + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBSobel(src_argb_a + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBSobel_Any) { + int max_diff = TestSobel(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobel_Unaligned) { + int max_diff = TestSobel(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobel_Invert) { + int max_diff = TestSobel(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobel_Opt) { + int max_diff = TestSobel(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_EQ(0, max_diff); +} + +static int TestSobelToPlane(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kSrcBpp = 4; + const int kDstBpp = 1; + const int kSrcStride = (width * kSrcBpp + 15) & ~15; + const int kDstStride = (width * kDstBpp + 15) & ~15; + align_buffer_page_end(src_argb_a, kSrcStride * height + off); + align_buffer_page_end(dst_argb_c, kDstStride * height); + align_buffer_page_end(dst_argb_opt, kDstStride * height); + memset(src_argb_a, 0, kSrcStride * height + off); + for (int i = 0; i < kSrcStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kDstStride * height); + memset(dst_argb_opt, 0, kDstStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBSobelToPlane(src_argb_a + off, kSrcStride, + dst_argb_c, kDstStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBSobelToPlane(src_argb_a + off, kSrcStride, + dst_argb_opt, kDstStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kDstStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBSobelToPlane_Any) { + int max_diff = TestSobelToPlane(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelToPlane_Unaligned) { + int max_diff = TestSobelToPlane(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelToPlane_Invert) { + int max_diff = TestSobelToPlane(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelToPlane_Opt) { + int max_diff = TestSobelToPlane(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0); + EXPECT_EQ(0, max_diff); +} + +static int TestSobelXY(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + memset(src_argb_a, 0, kStride * height + off); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + } + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBSobelXY(src_argb_a + off, kStride, + dst_argb_c, kStride, + width, invert * height); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBSobelXY(src_argb_a + off, kStride, + dst_argb_opt, kStride, + width, invert * height); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBSobelXY_Any) { + int max_diff = TestSobelXY(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelXY_Unaligned) { + int max_diff = TestSobelXY(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelXY_Invert) { + int max_diff = TestSobelXY(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, -1, 0); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBSobelXY_Opt) { + int max_diff = TestSobelXY(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, +1, 0); + EXPECT_EQ(0, max_diff); +} + +static int TestBlur(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off, int radius) { + if (width < 1) { + width = 1; + } + const int kBpp = 4; + const int kStride = width * kBpp; + align_buffer_page_end(src_argb_a, kStride * height + off); + align_buffer_page_end(dst_cumsum, width * height * 16); + align_buffer_page_end(dst_argb_c, kStride * height); + align_buffer_page_end(dst_argb_opt, kStride * height); + for (int i = 0; i < kStride * height; ++i) { + src_argb_a[i + off] = (fastrand() & 0xff); + } + memset(dst_cumsum, 0, width * height * 16); + memset(dst_argb_c, 0, kStride * height); + memset(dst_argb_opt, 0, kStride * height); + + MaskCpuFlags(disable_cpu_flags); + ARGBBlur(src_argb_a + off, kStride, + dst_argb_c, kStride, + reinterpret_cast<int32*>(dst_cumsum), width * 4, + width, invert * height, radius); + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBBlur(src_argb_a + off, kStride, + dst_argb_opt, kStride, + reinterpret_cast<int32*>(dst_cumsum), width * 4, + width, invert * height, radius); + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i]) - + static_cast<int>(dst_argb_opt[i])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(src_argb_a); + free_aligned_buffer_page_end(dst_cumsum); + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +static const int kBlurSize = 55; +TEST_F(LibYUVPlanarTest, ARGBBlur_Any) { + int max_diff = TestBlur(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, kBlurSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlur_Unaligned) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1, kBlurSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlur_Invert) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0, kBlurSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlur_Opt) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, kBlurSize); + EXPECT_LE(max_diff, 1); +} + +static const int kBlurSmallSize = 5; +TEST_F(LibYUVPlanarTest, ARGBBlurSmall_Any) { + int max_diff = TestBlur(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, kBlurSmallSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlurSmall_Unaligned) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1, kBlurSmallSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlurSmall_Invert) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0, kBlurSmallSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, ARGBBlurSmall_Opt) { + int max_diff = TestBlur(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, kBlurSmallSize); + EXPECT_LE(max_diff, 1); +} + +TEST_F(LibYUVPlanarTest, TestARGBPolynomial) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_opt[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_c[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + SIMD_ALIGNED(static const float kWarmifyPolynomial[16]) = { + 0.94230f, -3.03300f, -2.92500f, 0.f, // C0 + 0.584500f, 1.112000f, 1.535000f, 1.f, // C1 x + 0.001313f, -0.002503f, -0.004496f, 0.f, // C2 x * x + 0.0f, 0.000006965f, 0.000008781f, 0.f, // C3 x * x * x + }; + + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test white + orig_pixels[3][0] = 255u; + orig_pixels[3][1] = 255u; + orig_pixels[3][2] = 255u; + orig_pixels[3][3] = 255u; + // Test color + orig_pixels[4][0] = 16u; + orig_pixels[4][1] = 64u; + orig_pixels[4][2] = 192u; + orig_pixels[4][3] = 224u; + // Do 16 to test asm version. + ARGBPolynomial(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + &kWarmifyPolynomial[0], 16, 1); + EXPECT_EQ(235u, dst_pixels_opt[0][0]); + EXPECT_EQ(0u, dst_pixels_opt[0][1]); + EXPECT_EQ(0u, dst_pixels_opt[0][2]); + EXPECT_EQ(128u, dst_pixels_opt[0][3]); + EXPECT_EQ(0u, dst_pixels_opt[1][0]); + EXPECT_EQ(233u, dst_pixels_opt[1][1]); + EXPECT_EQ(0u, dst_pixels_opt[1][2]); + EXPECT_EQ(0u, dst_pixels_opt[1][3]); + EXPECT_EQ(0u, dst_pixels_opt[2][0]); + EXPECT_EQ(0u, dst_pixels_opt[2][1]); + EXPECT_EQ(241u, dst_pixels_opt[2][2]); + EXPECT_EQ(255u, dst_pixels_opt[2][3]); + EXPECT_EQ(235u, dst_pixels_opt[3][0]); + EXPECT_EQ(233u, dst_pixels_opt[3][1]); + EXPECT_EQ(241u, dst_pixels_opt[3][2]); + EXPECT_EQ(255u, dst_pixels_opt[3][3]); + EXPECT_EQ(10u, dst_pixels_opt[4][0]); + EXPECT_EQ(59u, dst_pixels_opt[4][1]); + EXPECT_EQ(188u, dst_pixels_opt[4][2]); + EXPECT_EQ(224u, dst_pixels_opt[4][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + + MaskCpuFlags(disable_cpu_flags_); + ARGBPolynomial(&orig_pixels[0][0], 0, &dst_pixels_c[0][0], 0, + &kWarmifyPolynomial[0], 1280, 1); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBPolynomial(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + &kWarmifyPolynomial[0], 1280, 1); + } + + for (int i = 0; i < 1280; ++i) { + EXPECT_EQ(dst_pixels_c[i][0], dst_pixels_opt[i][0]); + EXPECT_EQ(dst_pixels_c[i][1], dst_pixels_opt[i][1]); + EXPECT_EQ(dst_pixels_c[i][2], dst_pixels_opt[i][2]); + EXPECT_EQ(dst_pixels_c[i][3], dst_pixels_opt[i][3]); + } +} + +TEST_F(LibYUVPlanarTest, TestARGBLumaColorTable) { + SIMD_ALIGNED(uint8 orig_pixels[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_opt[1280][4]); + SIMD_ALIGNED(uint8 dst_pixels_c[1280][4]); + memset(orig_pixels, 0, sizeof(orig_pixels)); + + align_buffer_page_end(lumacolortable, 32768); + int v = 0; + for (int i = 0; i < 32768; ++i) { + lumacolortable[i] = v; + v += 3; + } + // Test blue + orig_pixels[0][0] = 255u; + orig_pixels[0][1] = 0u; + orig_pixels[0][2] = 0u; + orig_pixels[0][3] = 128u; + // Test green + orig_pixels[1][0] = 0u; + orig_pixels[1][1] = 255u; + orig_pixels[1][2] = 0u; + orig_pixels[1][3] = 0u; + // Test red + orig_pixels[2][0] = 0u; + orig_pixels[2][1] = 0u; + orig_pixels[2][2] = 255u; + orig_pixels[2][3] = 255u; + // Test color + orig_pixels[3][0] = 16u; + orig_pixels[3][1] = 64u; + orig_pixels[3][2] = 192u; + orig_pixels[3][3] = 224u; + // Do 16 to test asm version. + ARGBLumaColorTable(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + &lumacolortable[0], 16, 1); + EXPECT_EQ(253u, dst_pixels_opt[0][0]); + EXPECT_EQ(0u, dst_pixels_opt[0][1]); + EXPECT_EQ(0u, dst_pixels_opt[0][2]); + EXPECT_EQ(128u, dst_pixels_opt[0][3]); + EXPECT_EQ(0u, dst_pixels_opt[1][0]); + EXPECT_EQ(253u, dst_pixels_opt[1][1]); + EXPECT_EQ(0u, dst_pixels_opt[1][2]); + EXPECT_EQ(0u, dst_pixels_opt[1][3]); + EXPECT_EQ(0u, dst_pixels_opt[2][0]); + EXPECT_EQ(0u, dst_pixels_opt[2][1]); + EXPECT_EQ(253u, dst_pixels_opt[2][2]); + EXPECT_EQ(255u, dst_pixels_opt[2][3]); + EXPECT_EQ(48u, dst_pixels_opt[3][0]); + EXPECT_EQ(192u, dst_pixels_opt[3][1]); + EXPECT_EQ(64u, dst_pixels_opt[3][2]); + EXPECT_EQ(224u, dst_pixels_opt[3][3]); + + for (int i = 0; i < 1280; ++i) { + orig_pixels[i][0] = i; + orig_pixels[i][1] = i / 2; + orig_pixels[i][2] = i / 3; + orig_pixels[i][3] = i; + } + + MaskCpuFlags(disable_cpu_flags_); + ARGBLumaColorTable(&orig_pixels[0][0], 0, &dst_pixels_c[0][0], 0, + lumacolortable, 1280, 1); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_pixels_div1280_; ++i) { + ARGBLumaColorTable(&orig_pixels[0][0], 0, &dst_pixels_opt[0][0], 0, + lumacolortable, 1280, 1); + } + for (int i = 0; i < 1280; ++i) { + EXPECT_EQ(dst_pixels_c[i][0], dst_pixels_opt[i][0]); + EXPECT_EQ(dst_pixels_c[i][1], dst_pixels_opt[i][1]); + EXPECT_EQ(dst_pixels_c[i][2], dst_pixels_opt[i][2]); + EXPECT_EQ(dst_pixels_c[i][3], dst_pixels_opt[i][3]); + } + + free_aligned_buffer_page_end(lumacolortable); +} + +TEST_F(LibYUVPlanarTest, TestARGBCopyAlpha) { + const int kSize = benchmark_width_ * benchmark_height_ * 4; + align_buffer_page_end(orig_pixels, kSize); + align_buffer_page_end(dst_pixels_opt, kSize); + align_buffer_page_end(dst_pixels_c, kSize); + + MemRandomize(orig_pixels, kSize); + MemRandomize(dst_pixels_opt, kSize); + memcpy(dst_pixels_c, dst_pixels_opt, kSize); + + MaskCpuFlags(disable_cpu_flags_); + ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, + dst_pixels_c, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_iterations_; ++i) { + ARGBCopyAlpha(orig_pixels, benchmark_width_ * 4, + dst_pixels_opt, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + } + for (int i = 0; i < kSize; ++i) { + EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]); + } + + free_aligned_buffer_page_end(dst_pixels_c); + free_aligned_buffer_page_end(dst_pixels_opt); + free_aligned_buffer_page_end(orig_pixels); +} + +TEST_F(LibYUVPlanarTest, TestARGBExtractAlpha) { + const int kPixels = benchmark_width_ * benchmark_height_; + align_buffer_page_end(src_pixels, kPixels * 4); + align_buffer_page_end(dst_pixels_opt, kPixels); + align_buffer_page_end(dst_pixels_c, kPixels); + + MemRandomize(src_pixels, kPixels * 4); + MemRandomize(dst_pixels_opt, kPixels); + memcpy(dst_pixels_c, dst_pixels_opt, kPixels); + + MaskCpuFlags(disable_cpu_flags_); + ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, + dst_pixels_c, benchmark_width_, + benchmark_width_, benchmark_height_); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_iterations_; ++i) { + ARGBExtractAlpha(src_pixels, benchmark_width_ * 4, + dst_pixels_opt, benchmark_width_, + benchmark_width_, benchmark_height_); + } + for (int i = 0; i < kPixels; ++i) { + EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]); + } + + free_aligned_buffer_page_end(dst_pixels_c); + free_aligned_buffer_page_end(dst_pixels_opt); + free_aligned_buffer_page_end(src_pixels); +} + +TEST_F(LibYUVPlanarTest, TestARGBCopyYToAlpha) { + const int kPixels = benchmark_width_ * benchmark_height_; + align_buffer_page_end(orig_pixels, kPixels); + align_buffer_page_end(dst_pixels_opt, kPixels * 4); + align_buffer_page_end(dst_pixels_c, kPixels * 4); + + MemRandomize(orig_pixels, kPixels); + MemRandomize(dst_pixels_opt, kPixels * 4); + memcpy(dst_pixels_c, dst_pixels_opt, kPixels * 4); + + MaskCpuFlags(disable_cpu_flags_); + ARGBCopyYToAlpha(orig_pixels, benchmark_width_, + dst_pixels_c, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + MaskCpuFlags(benchmark_cpu_info_); + + for (int i = 0; i < benchmark_iterations_; ++i) { + ARGBCopyYToAlpha(orig_pixels, benchmark_width_, + dst_pixels_opt, benchmark_width_ * 4, + benchmark_width_, benchmark_height_); + } + for (int i = 0; i < kPixels * 4; ++i) { + EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]); + } + + free_aligned_buffer_page_end(dst_pixels_c); + free_aligned_buffer_page_end(dst_pixels_opt); + free_aligned_buffer_page_end(orig_pixels); +} + +static int TestARGBRect(int width, int height, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info, + int invert, int off, int bpp) { + if (width < 1) { + width = 1; + } + const int kStride = width * bpp; + const int kSize = kStride * height; + const uint32 v32 = fastrand() & (bpp == 4 ? 0xffffffff : 0xff); + + align_buffer_page_end(dst_argb_c, kSize + off); + align_buffer_page_end(dst_argb_opt, kSize + off); + + MemRandomize(dst_argb_c + off, kSize); + memcpy(dst_argb_opt + off, dst_argb_c + off, kSize); + + MaskCpuFlags(disable_cpu_flags); + if (bpp == 4) { + ARGBRect(dst_argb_c + off, kStride, 0, 0, width, invert * height, v32); + } else { + SetPlane(dst_argb_c + off, kStride, width, invert * height, v32); + } + + MaskCpuFlags(benchmark_cpu_info); + for (int i = 0; i < benchmark_iterations; ++i) { + if (bpp == 4) { + ARGBRect(dst_argb_opt + off, kStride, 0, 0, width, invert * height, v32); + } else { + SetPlane(dst_argb_opt + off, kStride, width, invert * height, v32); + } + } + int max_diff = 0; + for (int i = 0; i < kStride * height; ++i) { + int abs_diff = + abs(static_cast<int>(dst_argb_c[i + off]) - + static_cast<int>(dst_argb_opt[i + off])); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + return max_diff; +} + +TEST_F(LibYUVPlanarTest, ARGBRect_Any) { + int max_diff = TestARGBRect(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, 4); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBRect_Unaligned) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1, 4); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBRect_Invert) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0, 4); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, ARGBRect_Opt) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, 4); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, SetPlane_Any) { + int max_diff = TestARGBRect(benchmark_width_ - 1, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, SetPlane_Unaligned) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 1, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, SetPlane_Invert) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + -1, 0, 1); + EXPECT_EQ(0, max_diff); +} + +TEST_F(LibYUVPlanarTest, SetPlane_Opt) { + int max_diff = TestARGBRect(benchmark_width_, benchmark_height_, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_, + +1, 0, 1); + EXPECT_EQ(0, max_diff); +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/rotate_argb_test.cc b/media/libyuv/unit_test/rotate_argb_test.cc new file mode 100644 index 000000000..9c83c3563 --- /dev/null +++ b/media/libyuv/unit_test/rotate_argb_test.cc @@ -0,0 +1,196 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> + +#include "libyuv/cpu_id.h" +#include "libyuv/rotate_argb.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +void TestRotateBpp(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, + const int kBpp) { + if (src_width < 1) { + src_width = 1; + } + if (src_height < 1) { + src_height = 1; + } + if (dst_width < 1) { + dst_width = 1; + } + if (dst_height < 1) { + dst_height = 1; + } + int src_stride_argb = src_width * kBpp; + int src_argb_plane_size = src_stride_argb * abs(src_height); + align_buffer_page_end(src_argb, src_argb_plane_size); + for (int i = 0; i < src_argb_plane_size; ++i) { + src_argb[i] = fastrand() & 0xff; + } + + int dst_stride_argb = dst_width * kBpp; + int dst_argb_plane_size = dst_stride_argb * dst_height; + align_buffer_page_end(dst_argb_c, dst_argb_plane_size); + align_buffer_page_end(dst_argb_opt, dst_argb_plane_size); + memset(dst_argb_c, 2, dst_argb_plane_size); + memset(dst_argb_opt, 3, dst_argb_plane_size); + + if (kBpp == 1) { + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + RotatePlane(src_argb, src_stride_argb, + dst_argb_c, dst_stride_argb, + src_width, src_height, mode); + + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + for (int i = 0; i < benchmark_iterations; ++i) { + RotatePlane(src_argb, src_stride_argb, + dst_argb_opt, dst_stride_argb, + src_width, src_height, mode); + } + } else if (kBpp == 4) { + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + ARGBRotate(src_argb, src_stride_argb, + dst_argb_c, dst_stride_argb, + src_width, src_height, mode); + + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + for (int i = 0; i < benchmark_iterations; ++i) { + ARGBRotate(src_argb, src_stride_argb, + dst_argb_opt, dst_stride_argb, + src_width, src_height, mode); + } + } + + // Rotation should be exact. + for (int i = 0; i < dst_argb_plane_size; ++i) { + EXPECT_EQ(dst_argb_c[i], dst_argb_opt[i]); + } + + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + free_aligned_buffer_page_end(src_argb); +} + +static void ARGBTestRotate(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) { + TestRotateBpp(src_width, src_height, + dst_width, dst_height, + mode, benchmark_iterations, + disable_cpu_flags, benchmark_cpu_info, 4); +} + +TEST_F(LibYUVRotateTest, ARGBRotate0_Opt) { + ARGBTestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, ARGBRotate90_Opt) { + ARGBTestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, ARGBRotate180_Opt) { + ARGBTestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, ARGBRotate270_Opt) { + ARGBTestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +static void TestRotatePlane(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) { + TestRotateBpp(src_width, src_height, + dst_width, dst_height, + mode, benchmark_iterations, + disable_cpu_flags, benchmark_cpu_info, 1); +} + +TEST_F(LibYUVRotateTest, RotatePlane0_Opt) { + TestRotatePlane(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, RotatePlane90_Opt) { + TestRotatePlane(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, RotatePlane180_Opt) { + TestRotatePlane(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, RotatePlane270_Opt) { + TestRotatePlane(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_RotatePlane0_Odd) { + TestRotatePlane(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_RotatePlane90_Odd) { + TestRotatePlane(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_RotatePlane180_Odd) { + TestRotatePlane(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_RotatePlane270_Odd) { + TestRotatePlane(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +} // namespace libyuv diff --git a/media/libyuv/unit_test/rotate_test.cc b/media/libyuv/unit_test/rotate_test.cc new file mode 100644 index 000000000..07e2f73a0 --- /dev/null +++ b/media/libyuv/unit_test/rotate_test.cc @@ -0,0 +1,296 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> + +#include "libyuv/cpu_id.h" +#include "libyuv/rotate.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +static void I420TestRotate(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_i420_y_size = src_width * Abs(src_height); + int src_i420_uv_size = ((src_width + 1) / 2) * ((Abs(src_height) + 1) / 2); + int src_i420_size = src_i420_y_size + src_i420_uv_size * 2; + align_buffer_page_end(src_i420, src_i420_size); + for (int i = 0; i < src_i420_size; ++i) { + src_i420[i] = fastrand() & 0xff; + } + + int dst_i420_y_size = dst_width * dst_height; + int dst_i420_uv_size = ((dst_width + 1) / 2) * ((dst_height + 1) / 2); + int dst_i420_size = dst_i420_y_size + dst_i420_uv_size * 2; + align_buffer_page_end(dst_i420_c, dst_i420_size); + align_buffer_page_end(dst_i420_opt, dst_i420_size); + memset(dst_i420_c, 2, dst_i420_size); + memset(dst_i420_opt, 3, dst_i420_size); + + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + I420Rotate(src_i420, src_width, + src_i420 + src_i420_y_size, (src_width + 1) / 2, + src_i420 + src_i420_y_size + src_i420_uv_size, (src_width + 1) / 2, + dst_i420_c, dst_width, + dst_i420_c + dst_i420_y_size, (dst_width + 1) / 2, + dst_i420_c + dst_i420_y_size + dst_i420_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) { + I420Rotate(src_i420, src_width, + src_i420 + src_i420_y_size, (src_width + 1) / 2, + src_i420 + src_i420_y_size + src_i420_uv_size, + (src_width + 1) / 2, + dst_i420_opt, dst_width, + dst_i420_opt + dst_i420_y_size, (dst_width + 1) / 2, + dst_i420_opt + dst_i420_y_size + dst_i420_uv_size, + (dst_width + 1) / 2, + src_width, src_height, mode); + } + + // Rotation should be exact. + for (int i = 0; i < dst_i420_size; ++i) { + EXPECT_EQ(dst_i420_c[i], dst_i420_opt[i]); + } + + free_aligned_buffer_page_end(dst_i420_c); + free_aligned_buffer_page_end(dst_i420_opt); + free_aligned_buffer_page_end(src_i420); +} + +TEST_F(LibYUVRotateTest, I420Rotate0_Opt) { + I420TestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, I420Rotate90_Opt) { + I420TestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, I420Rotate180_Opt) { + I420TestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, I420Rotate270_Opt) { + I420TestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +// TODO(fbarchard): Remove odd width tests. +// Odd width tests work but disabled because they use C code and can be +// tested by passing an odd width command line or environment variable. +TEST_F(LibYUVRotateTest, DISABLED_I420Rotate0_Odd) { + I420TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_I420Rotate90_Odd) { + I420TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_I420Rotate180_Odd) { + I420TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_I420Rotate270_Odd) { + I420TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +static void NV12TestRotate(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) { // allow negative for inversion test. + src_height = 1; + } + if (dst_width < 1) { + dst_width = 1; + } + if (dst_height < 1) { + dst_height = 1; + } + int src_nv12_y_size = src_width * Abs(src_height); + int src_nv12_uv_size = + ((src_width + 1) / 2) * ((Abs(src_height) + 1) / 2) * 2; + int src_nv12_size = src_nv12_y_size + src_nv12_uv_size; + align_buffer_page_end(src_nv12, src_nv12_size); + for (int i = 0; i < src_nv12_size; ++i) { + src_nv12[i] = fastrand() & 0xff; + } + + int dst_i420_y_size = dst_width * dst_height; + int dst_i420_uv_size = ((dst_width + 1) / 2) * ((dst_height + 1) / 2); + int dst_i420_size = dst_i420_y_size + dst_i420_uv_size * 2; + align_buffer_page_end(dst_i420_c, dst_i420_size); + align_buffer_page_end(dst_i420_opt, dst_i420_size); + memset(dst_i420_c, 2, dst_i420_size); + memset(dst_i420_opt, 3, dst_i420_size); + + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + NV12ToI420Rotate(src_nv12, src_width, + src_nv12 + src_nv12_y_size, (src_width + 1) & ~1, + dst_i420_c, dst_width, + dst_i420_c + dst_i420_y_size, (dst_width + 1) / 2, + dst_i420_c + dst_i420_y_size + dst_i420_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) { + NV12ToI420Rotate(src_nv12, src_width, + src_nv12 + src_nv12_y_size, (src_width + 1) & ~1, + dst_i420_opt, dst_width, + dst_i420_opt + dst_i420_y_size, (dst_width + 1) / 2, + dst_i420_opt + dst_i420_y_size + dst_i420_uv_size, + (dst_width + 1) / 2, + src_width, src_height, mode); + } + + // Rotation should be exact. + for (int i = 0; i < dst_i420_size; ++i) { + EXPECT_EQ(dst_i420_c[i], dst_i420_opt[i]); + } + + free_aligned_buffer_page_end(dst_i420_c); + free_aligned_buffer_page_end(dst_i420_opt); + free_aligned_buffer_page_end(src_nv12); +} + +TEST_F(LibYUVRotateTest, NV12Rotate0_Opt) { + NV12TestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate90_Opt) { + NV12TestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate180_Opt) { + NV12TestRotate(benchmark_width_, benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate270_Opt) { + NV12TestRotate(benchmark_width_, benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_NV12Rotate0_Odd) { + NV12TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_NV12Rotate90_Odd) { + NV12TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_NV12Rotate180_Odd) { + NV12TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_width_ - 3, benchmark_height_ - 1, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, DISABLED_NV12Rotate270_Odd) { + NV12TestRotate(benchmark_width_ - 3, benchmark_height_ - 1, + benchmark_height_ - 1, benchmark_width_ - 3, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate0_Invert) { + NV12TestRotate(benchmark_width_, -benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate0, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate90_Invert) { + NV12TestRotate(benchmark_width_, -benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate90, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate180_Invert) { + NV12TestRotate(benchmark_width_, -benchmark_height_, + benchmark_width_, benchmark_height_, + kRotate180, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + +TEST_F(LibYUVRotateTest, NV12Rotate270_Invert) { + NV12TestRotate(benchmark_width_, -benchmark_height_, + benchmark_height_, benchmark_width_, + kRotate270, benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); +} + + + + + +} // namespace libyuv diff --git a/media/libyuv/unit_test/scale_argb_test.cc b/media/libyuv/unit_test/scale_argb_test.cc new file mode 100644 index 000000000..f99782f73 --- /dev/null +++ b/media/libyuv/unit_test/scale_argb_test.cc @@ -0,0 +1,462 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <time.h> + +#include "libyuv/convert_argb.h" +#include "libyuv/cpu_id.h" +#include "libyuv/scale_argb.h" +#include "libyuv/video_common.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +#define STRINGIZE(line) #line +#define FILELINESTR(file, line) file ":" STRINGIZE(line) + +// Test scaling with C vs Opt and return maximum pixel difference. 0 = exact. +static int ARGBTestFilter(int src_width, int src_height, + int dst_width, int dst_height, + FilterMode f, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info) { + if (!SizeValid(src_width, src_height, dst_width, dst_height)) { + return 0; + } + + int i, j; + const int b = 0; // 128 to test for padding/stride. + int64 src_argb_plane_size = (Abs(src_width) + b * 2) * + (Abs(src_height) + b * 2) * 4LL; + int src_stride_argb = (b * 2 + Abs(src_width)) * 4; + + align_buffer_page_end(src_argb, src_argb_plane_size); + if (!src_argb) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + MemRandomize(src_argb, src_argb_plane_size); + + int64 dst_argb_plane_size = (dst_width + b * 2) * (dst_height + b * 2) * 4LL; + int dst_stride_argb = (b * 2 + dst_width) * 4; + + align_buffer_page_end(dst_argb_c, dst_argb_plane_size); + align_buffer_page_end(dst_argb_opt, dst_argb_plane_size); + if (!dst_argb_c || !dst_argb_opt) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + memset(dst_argb_c, 2, dst_argb_plane_size); + memset(dst_argb_opt, 3, dst_argb_plane_size); + + // Warm up both versions for consistent benchmarks. + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + double c_time = get_time(); + ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + + c_time = (get_time() - c_time); + + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + double opt_time = get_time(); + for (i = 0; i < benchmark_iterations; ++i) { + ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + } + opt_time = (get_time() - opt_time) / benchmark_iterations; + + // Report performance of C vs OPT + printf("filter %d - %8d us C - %8d us OPT\n", + f, static_cast<int>(c_time * 1e6), static_cast<int>(opt_time * 1e6)); + + // C version may be a little off from the optimized. Order of + // operations may introduce rounding somewhere. So do a difference + // of the buffers and look to see that the max difference isn't + // over 2. + int max_diff = 0; + for (i = b; i < (dst_height + b); ++i) { + for (j = b * 4; j < (dst_width + b) * 4; ++j) { + int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] - + dst_argb_opt[(i * dst_stride_argb) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + free_aligned_buffer_page_end(src_argb); + return max_diff; +} + +static const int kTileX = 8; +static const int kTileY = 8; + +static int TileARGBScale(const uint8* src_argb, int src_stride_argb, + int src_width, int src_height, + uint8* dst_argb, int dst_stride_argb, + int dst_width, int dst_height, + FilterMode filtering) { + for (int y = 0; y < dst_height; y += kTileY) { + for (int x = 0; x < dst_width; x += kTileX) { + int clip_width = kTileX; + if (x + clip_width > dst_width) { + clip_width = dst_width - x; + } + int clip_height = kTileY; + if (y + clip_height > dst_height) { + clip_height = dst_height - y; + } + int r = ARGBScaleClip(src_argb, src_stride_argb, + src_width, src_height, + dst_argb, dst_stride_argb, + dst_width, dst_height, + x, y, clip_width, clip_height, filtering); + if (r) { + return r; + } + } + } + return 0; +} + +static int ARGBClipTestFilter(int src_width, int src_height, + int dst_width, int dst_height, + FilterMode f, int benchmark_iterations) { + if (!SizeValid(src_width, src_height, dst_width, dst_height)) { + return 0; + } + + const int b = 128; + int64 src_argb_plane_size = (Abs(src_width) + b * 2) * + (Abs(src_height) + b * 2) * 4; + int src_stride_argb = (b * 2 + Abs(src_width)) * 4; + + align_buffer_page_end(src_argb, src_argb_plane_size); + if (!src_argb) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + memset(src_argb, 1, src_argb_plane_size); + + int64 dst_argb_plane_size = (dst_width + b * 2) * (dst_height + b * 2) * 4; + int dst_stride_argb = (b * 2 + dst_width) * 4; + + int i, j; + for (i = b; i < (Abs(src_height) + b); ++i) { + for (j = b; j < (Abs(src_width) + b) * 4; ++j) { + src_argb[(i * src_stride_argb) + j] = (fastrand() & 0xff); + } + } + + align_buffer_page_end(dst_argb_c, dst_argb_plane_size); + align_buffer_page_end(dst_argb_opt, dst_argb_plane_size); + if (!dst_argb_c || !dst_argb_opt) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + memset(dst_argb_c, 2, dst_argb_plane_size); + memset(dst_argb_opt, 3, dst_argb_plane_size); + + // Do full image, no clipping. + double c_time = get_time(); + ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + c_time = (get_time() - c_time); + + // Do tiled image, clipping scale to a tile at a time. + double opt_time = get_time(); + for (i = 0; i < benchmark_iterations; ++i) { + TileARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb, + src_width, src_height, + dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb, + dst_width, dst_height, f); + } + opt_time = (get_time() - opt_time) / benchmark_iterations; + + // Report performance of Full vs Tiled. + printf("filter %d - %8d us Full - %8d us Tiled\n", + f, static_cast<int>(c_time * 1e6), static_cast<int>(opt_time * 1e6)); + + // Compare full scaled image vs tiled image. + int max_diff = 0; + for (i = b; i < (dst_height + b); ++i) { + for (j = b * 4; j < (dst_width + b) * 4; ++j) { + int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] - + dst_argb_opt[(i * dst_stride_argb) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + free_aligned_buffer_page_end(src_argb); + return max_diff; +} + +// The following adjustments in dimensions ensure the scale factor will be +// exactly achieved. +#define DX(x, nom, denom) static_cast<int>((Abs(x) / nom) * nom) +#define SX(x, nom, denom) static_cast<int>((x / nom) * denom) + +#define TEST_FACTOR1(name, filter, nom, denom, max_diff) \ + TEST_F(LibYUVScaleTest, ARGBScaleDownBy##name##_##filter) { \ + int diff = ARGBTestFilter(SX(benchmark_width_, nom, denom), \ + SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), \ + DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, ARGBScaleDownClipBy##name##_##filter) { \ + int diff = ARGBClipTestFilter(SX(benchmark_width_, nom, denom), \ + SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), \ + DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } + +// Test a scale factor with all 4 filters. Expect unfiltered to be exact, but +// filtering is different fixed point implementations for SSSE3, Neon and C. +#define TEST_FACTOR(name, nom, denom) \ + TEST_FACTOR1(name, None, nom, denom, 0) \ + TEST_FACTOR1(name, Linear, nom, denom, 3) \ + TEST_FACTOR1(name, Bilinear, nom, denom, 3) \ + TEST_FACTOR1(name, Box, nom, denom, 3) + +TEST_FACTOR(2, 1, 2) +TEST_FACTOR(4, 1, 4) +TEST_FACTOR(8, 1, 8) +TEST_FACTOR(3by4, 3, 4) +TEST_FACTOR(3by8, 3, 8) +TEST_FACTOR(3, 1, 3) +#undef TEST_FACTOR1 +#undef TEST_FACTOR +#undef SX +#undef DX + +#define TEST_SCALETO1(name, width, height, filter, max_diff) \ + TEST_F(LibYUVScaleTest, name##To##width##x##height##_##filter) { \ + int diff = ARGBTestFilter(benchmark_width_, benchmark_height_, \ + width, height, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, name##From##width##x##height##_##filter) { \ + int diff = ARGBTestFilter(width, height, \ + Abs(benchmark_width_), Abs(benchmark_height_), \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, name##ClipTo##width##x##height##_##filter) { \ + int diff = ARGBClipTestFilter(benchmark_width_, benchmark_height_, \ + width, height, \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, name##ClipFrom##width##x##height##_##filter) { \ + int diff = ARGBClipTestFilter(width, height, \ + Abs(benchmark_width_), \ + Abs(benchmark_height_), \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } + +/// Test scale to a specified size with all 4 filters. +#define TEST_SCALETO(name, width, height) \ + TEST_SCALETO1(name, width, height, None, 0) \ + TEST_SCALETO1(name, width, height, Linear, 3) \ + TEST_SCALETO1(name, width, height, Bilinear, 3) + +TEST_SCALETO(ARGBScale, 1, 1) +TEST_SCALETO(ARGBScale, 320, 240) +TEST_SCALETO(ARGBScale, 352, 288) +TEST_SCALETO(ARGBScale, 569, 480) +TEST_SCALETO(ARGBScale, 640, 360) +TEST_SCALETO(ARGBScale, 1280, 720) +#undef TEST_SCALETO1 +#undef TEST_SCALETO + +// Scale with YUV conversion to ARGB and clipping. +LIBYUV_API +int YUVToARGBScaleReference2(const uint8* src_y, int src_stride_y, + const uint8* src_u, int src_stride_u, + const uint8* src_v, int src_stride_v, + uint32 src_fourcc, + int src_width, int src_height, + uint8* dst_argb, int dst_stride_argb, + uint32 dst_fourcc, + int dst_width, int dst_height, + int clip_x, int clip_y, + int clip_width, int clip_height, + enum FilterMode filtering) { + uint8* argb_buffer = static_cast<uint8*>(malloc(src_width * src_height * 4)); + int r; + I420ToARGB(src_y, src_stride_y, + src_u, src_stride_u, + src_v, src_stride_v, + argb_buffer, src_width * 4, + src_width, src_height); + + r = ARGBScaleClip(argb_buffer, src_width * 4, + src_width, src_height, + dst_argb, dst_stride_argb, + dst_width, dst_height, + clip_x, clip_y, clip_width, clip_height, + filtering); + free(argb_buffer); + return r; +} + +static void FillRamp(uint8* buf, int width, int height, int v, int dx, int dy) { + int rv = v; + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + *buf++ = v; + v += dx; + if (v < 0 || v > 255) { + dx = -dx; + v += dx; + } + } + v = rv + dy; + if (v < 0 || v > 255) { + dy = -dy; + v += dy; + } + rv = v; + } +} + +// Test scaling with C vs Opt and return maximum pixel difference. 0 = exact. +static int YUVToARGBTestFilter(int src_width, int src_height, + int dst_width, int dst_height, + FilterMode f, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info) { + int64 src_y_plane_size = Abs(src_width) * Abs(src_height); + int64 src_uv_plane_size = ((Abs(src_width) + 1) / 2) * + ((Abs(src_height) + 1) / 2); + int src_stride_y = Abs(src_width); + int src_stride_uv = (Abs(src_width) + 1) / 2; + + align_buffer_page_end(src_y, src_y_plane_size); + align_buffer_page_end(src_u, src_uv_plane_size); + align_buffer_page_end(src_v, src_uv_plane_size); + + int64 dst_argb_plane_size = (dst_width) * (dst_height) * 4LL; + int dst_stride_argb = (dst_width) * 4; + align_buffer_page_end(dst_argb_c, dst_argb_plane_size); + align_buffer_page_end(dst_argb_opt, dst_argb_plane_size); + if (!dst_argb_c || !dst_argb_opt || !src_y || !src_u || !src_v) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + // Fill YUV image with continuous ramp, which is less sensitive to + // subsampling and filtering differences for test purposes. + FillRamp(src_y, Abs(src_width), Abs(src_height), 128, 1, 1); + FillRamp(src_u, (Abs(src_width) + 1) / 2, (Abs(src_height) + 1) / 2, 3, 1, 1); + FillRamp(src_v, (Abs(src_width) + 1) / 2, (Abs(src_height) + 1) / 2, 4, 1, 1); + memset(dst_argb_c, 2, dst_argb_plane_size); + memset(dst_argb_opt, 3, dst_argb_plane_size); + + YUVToARGBScaleReference2(src_y, src_stride_y, + src_u, src_stride_uv, + src_v, src_stride_uv, + libyuv::FOURCC_I420, + src_width, src_height, + dst_argb_c, dst_stride_argb, + libyuv::FOURCC_I420, + dst_width, dst_height, + 0, 0, dst_width, dst_height, + f); + + for (int i = 0; i < benchmark_iterations; ++i) { + YUVToARGBScaleClip(src_y, src_stride_y, + src_u, src_stride_uv, + src_v, src_stride_uv, + libyuv::FOURCC_I420, + src_width, src_height, + dst_argb_opt, dst_stride_argb, + libyuv::FOURCC_I420, + dst_width, dst_height, + 0, 0, dst_width, dst_height, + f); + } + int max_diff = 0; + for (int i = 0; i < dst_height; ++i) { + for (int j = 0; j < dst_width * 4; ++j) { + int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] - + dst_argb_opt[(i * dst_stride_argb) + j]); + if (abs_diff > max_diff) { + printf("error %d at %d,%d c %d opt %d", + abs_diff, + j, i, + dst_argb_c[(i * dst_stride_argb) + j], + dst_argb_opt[(i * dst_stride_argb) + j]); + EXPECT_LE(abs_diff, 40); + max_diff = abs_diff; + } + } + } + + free_aligned_buffer_page_end(dst_argb_c); + free_aligned_buffer_page_end(dst_argb_opt); + free_aligned_buffer_page_end(src_y); + free_aligned_buffer_page_end(src_u); + free_aligned_buffer_page_end(src_v); + return max_diff; +} + +TEST_F(LibYUVScaleTest, YUVToRGBScaleUp) { + int diff = YUVToARGBTestFilter(benchmark_width_, benchmark_height_, + benchmark_width_ * 3 / 2, + benchmark_height_ * 3 / 2, + libyuv::kFilterBilinear, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); + EXPECT_LE(diff, 10); +} + +TEST_F(LibYUVScaleTest, YUVToRGBScaleDown) { + int diff = YUVToARGBTestFilter(benchmark_width_ * 3 / 2, + benchmark_height_ * 3 / 2, + benchmark_width_, benchmark_height_, + libyuv::kFilterBilinear, + benchmark_iterations_, + disable_cpu_flags_, benchmark_cpu_info_); + EXPECT_LE(diff, 10); +} + + +} // namespace libyuv diff --git a/media/libyuv/unit_test/scale_test.cc b/media/libyuv/unit_test/scale_test.cc new file mode 100644 index 000000000..f40443e29 --- /dev/null +++ b/media/libyuv/unit_test/scale_test.cc @@ -0,0 +1,372 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <time.h> + +#include "libyuv/cpu_id.h" +#include "libyuv/scale.h" +#include "../unit_test/unit_test.h" + +#define STRINGIZE(line) #line +#define FILELINESTR(file, line) file ":" STRINGIZE(line) + +namespace libyuv { + +// Test scaling with C vs Opt and return maximum pixel difference. 0 = exact. +static int TestFilter(int src_width, int src_height, + int dst_width, int dst_height, + FilterMode f, int benchmark_iterations, + int disable_cpu_flags, int benchmark_cpu_info) { + if (!SizeValid(src_width, src_height, dst_width, dst_height)) { + return 0; + } + + int i, j; + const int b = 0; // 128 to test for padding/stride. + int src_width_uv = (Abs(src_width) + 1) >> 1; + int src_height_uv = (Abs(src_height) + 1) >> 1; + + int64 src_y_plane_size = (Abs(src_width) + b * 2) * (Abs(src_height) + b * 2); + int64 src_uv_plane_size = (src_width_uv + b * 2) * (src_height_uv + b * 2); + + int src_stride_y = b * 2 + Abs(src_width); + int src_stride_uv = b * 2 + src_width_uv; + + align_buffer_page_end(src_y, src_y_plane_size) + align_buffer_page_end(src_u, src_uv_plane_size) + align_buffer_page_end(src_v, src_uv_plane_size) + if (!src_y || !src_u || !src_v) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + MemRandomize(src_y, src_y_plane_size); + MemRandomize(src_u, src_uv_plane_size); + MemRandomize(src_v, src_uv_plane_size); + + int dst_width_uv = (dst_width + 1) >> 1; + int dst_height_uv = (dst_height + 1) >> 1; + + int64 dst_y_plane_size = (dst_width + b * 2) * (dst_height + b * 2); + int64 dst_uv_plane_size = (dst_width_uv + b * 2) * (dst_height_uv + b * 2); + + int dst_stride_y = b * 2 + dst_width; + int dst_stride_uv = b * 2 + dst_width_uv; + + align_buffer_page_end(dst_y_c, dst_y_plane_size) + align_buffer_page_end(dst_u_c, dst_uv_plane_size) + align_buffer_page_end(dst_v_c, dst_uv_plane_size) + align_buffer_page_end(dst_y_opt, dst_y_plane_size) + align_buffer_page_end(dst_u_opt, dst_uv_plane_size) + align_buffer_page_end(dst_v_opt, dst_uv_plane_size) + if (!dst_y_c || !dst_u_c || !dst_v_c || + !dst_y_opt|| !dst_u_opt|| !dst_v_opt) { + printf("Skipped. Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n"); + return 0; + } + + MaskCpuFlags(disable_cpu_flags); // Disable all CPU optimization. + double c_time = get_time(); + I420Scale(src_y + (src_stride_y * b) + b, src_stride_y, + src_u + (src_stride_uv * b) + b, src_stride_uv, + src_v + (src_stride_uv * b) + b, src_stride_uv, + src_width, src_height, + dst_y_c + (dst_stride_y * b) + b, dst_stride_y, + dst_u_c + (dst_stride_uv * b) + b, dst_stride_uv, + dst_v_c + (dst_stride_uv * b) + b, dst_stride_uv, + dst_width, dst_height, f); + c_time = (get_time() - c_time); + + MaskCpuFlags(benchmark_cpu_info); // Enable all CPU optimization. + double opt_time = get_time(); + for (i = 0; i < benchmark_iterations; ++i) { + I420Scale(src_y + (src_stride_y * b) + b, src_stride_y, + src_u + (src_stride_uv * b) + b, src_stride_uv, + src_v + (src_stride_uv * b) + b, src_stride_uv, + src_width, src_height, + dst_y_opt + (dst_stride_y * b) + b, dst_stride_y, + dst_u_opt + (dst_stride_uv * b) + b, dst_stride_uv, + dst_v_opt + (dst_stride_uv * b) + b, dst_stride_uv, + dst_width, dst_height, f); + } + opt_time = (get_time() - opt_time) / benchmark_iterations; + // Report performance of C vs OPT + printf("filter %d - %8d us C - %8d us OPT\n", + f, + static_cast<int>(c_time * 1e6), + static_cast<int>(opt_time * 1e6)); + + // C version may be a little off from the optimized. Order of + // operations may introduce rounding somewhere. So do a difference + // of the buffers and look to see that the max difference isn't + // over 2. + int max_diff = 0; + for (i = b; i < (dst_height + b); ++i) { + for (j = b; j < (dst_width + b); ++j) { + int abs_diff = Abs(dst_y_c[(i * dst_stride_y) + j] - + dst_y_opt[(i * dst_stride_y) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + for (i = b; i < (dst_height_uv + b); ++i) { + for (j = b; j < (dst_width_uv + b); ++j) { + int abs_diff = Abs(dst_u_c[(i * dst_stride_uv) + j] - + dst_u_opt[(i * dst_stride_uv) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + abs_diff = Abs(dst_v_c[(i * dst_stride_uv) + j] - + dst_v_opt[(i * dst_stride_uv) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + 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_opt) + free_aligned_buffer_page_end(dst_u_opt) + free_aligned_buffer_page_end(dst_v_opt) + + free_aligned_buffer_page_end(src_y) + free_aligned_buffer_page_end(src_u) + free_aligned_buffer_page_end(src_v) + + return max_diff; +} + +// Test scaling with 8 bit C vs 16 bit C and return maximum pixel difference. +// 0 = exact. +static int TestFilter_16(int src_width, int src_height, + int dst_width, int dst_height, + FilterMode f, int benchmark_iterations) { + if (!SizeValid(src_width, src_height, dst_width, dst_height)) { + return 0; + } + + int i, j; + const int b = 0; // 128 to test for padding/stride. + int src_width_uv = (Abs(src_width) + 1) >> 1; + int src_height_uv = (Abs(src_height) + 1) >> 1; + + int64 src_y_plane_size = (Abs(src_width) + b * 2) * + (Abs(src_height) + b * 2); + int64 src_uv_plane_size = (src_width_uv + b * 2) * (src_height_uv + b * 2); + + int src_stride_y = b * 2 + Abs(src_width); + int src_stride_uv = b * 2 + src_width_uv; + + align_buffer_page_end(src_y, src_y_plane_size) + align_buffer_page_end(src_u, src_uv_plane_size) + align_buffer_page_end(src_v, src_uv_plane_size) + align_buffer_page_end(src_y_16, src_y_plane_size * 2) + align_buffer_page_end(src_u_16, src_uv_plane_size * 2) + align_buffer_page_end(src_v_16, src_uv_plane_size * 2) + uint16* p_src_y_16 = reinterpret_cast<uint16*>(src_y_16); + uint16* p_src_u_16 = reinterpret_cast<uint16*>(src_u_16); + uint16* p_src_v_16 = reinterpret_cast<uint16*>(src_v_16); + + MemRandomize(src_y, src_y_plane_size); + MemRandomize(src_u, src_uv_plane_size); + MemRandomize(src_v, src_uv_plane_size); + + for (i = b; i < src_height + b; ++i) { + for (j = b; j < src_width + b; ++j) { + p_src_y_16[(i * src_stride_y) + j] = src_y[(i * src_stride_y) + j]; + } + } + + for (i = b; i < (src_height_uv + b); ++i) { + for (j = b; j < (src_width_uv + b); ++j) { + p_src_u_16[(i * src_stride_uv) + j] = src_u[(i * src_stride_uv) + j]; + p_src_v_16[(i * src_stride_uv) + j] = src_v[(i * src_stride_uv) + j]; + } + } + + int dst_width_uv = (dst_width + 1) >> 1; + int dst_height_uv = (dst_height + 1) >> 1; + + int dst_y_plane_size = (dst_width + b * 2) * (dst_height + b * 2); + int dst_uv_plane_size = (dst_width_uv + b * 2) * (dst_height_uv + b * 2); + + int dst_stride_y = b * 2 + dst_width; + int dst_stride_uv = b * 2 + dst_width_uv; + + align_buffer_page_end(dst_y_8, dst_y_plane_size) + align_buffer_page_end(dst_u_8, dst_uv_plane_size) + align_buffer_page_end(dst_v_8, dst_uv_plane_size) + align_buffer_page_end(dst_y_16, dst_y_plane_size * 2) + align_buffer_page_end(dst_u_16, dst_uv_plane_size * 2) + align_buffer_page_end(dst_v_16, dst_uv_plane_size * 2) + + uint16* p_dst_y_16 = reinterpret_cast<uint16*>(dst_y_16); + uint16* p_dst_u_16 = reinterpret_cast<uint16*>(dst_u_16); + uint16* p_dst_v_16 = reinterpret_cast<uint16*>(dst_v_16); + + I420Scale(src_y + (src_stride_y * b) + b, src_stride_y, + src_u + (src_stride_uv * b) + b, src_stride_uv, + src_v + (src_stride_uv * b) + b, src_stride_uv, + src_width, src_height, + dst_y_8 + (dst_stride_y * b) + b, dst_stride_y, + dst_u_8 + (dst_stride_uv * b) + b, dst_stride_uv, + dst_v_8 + (dst_stride_uv * b) + b, dst_stride_uv, + dst_width, dst_height, f); + + for (i = 0; i < benchmark_iterations; ++i) { + I420Scale_16(p_src_y_16 + (src_stride_y * b) + b, src_stride_y, + p_src_u_16 + (src_stride_uv * b) + b, src_stride_uv, + p_src_v_16 + (src_stride_uv * b) + b, src_stride_uv, + src_width, src_height, + p_dst_y_16 + (dst_stride_y * b) + b, dst_stride_y, + p_dst_u_16 + (dst_stride_uv * b) + b, dst_stride_uv, + p_dst_v_16 + (dst_stride_uv * b) + b, dst_stride_uv, + dst_width, dst_height, f); + } + + // Expect an exact match + int max_diff = 0; + for (i = b; i < (dst_height + b); ++i) { + for (j = b; j < (dst_width + b); ++j) { + int abs_diff = Abs(dst_y_8[(i * dst_stride_y) + j] - + p_dst_y_16[(i * dst_stride_y) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + for (i = b; i < (dst_height_uv + b); ++i) { + for (j = b; j < (dst_width_uv + b); ++j) { + int abs_diff = Abs(dst_u_8[(i * dst_stride_uv) + j] - + p_dst_u_16[(i * dst_stride_uv) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + abs_diff = Abs(dst_v_8[(i * dst_stride_uv) + j] - + p_dst_v_16[(i * dst_stride_uv) + j]); + if (abs_diff > max_diff) { + max_diff = abs_diff; + } + } + } + + free_aligned_buffer_page_end(dst_y_8) + free_aligned_buffer_page_end(dst_u_8) + free_aligned_buffer_page_end(dst_v_8) + free_aligned_buffer_page_end(dst_y_16) + free_aligned_buffer_page_end(dst_u_16) + free_aligned_buffer_page_end(dst_v_16) + + free_aligned_buffer_page_end(src_y) + free_aligned_buffer_page_end(src_u) + free_aligned_buffer_page_end(src_v) + free_aligned_buffer_page_end(src_y_16) + free_aligned_buffer_page_end(src_u_16) + free_aligned_buffer_page_end(src_v_16) + + return max_diff; +} + +// The following adjustments in dimensions ensure the scale factor will be +// exactly achieved. +// 2 is chroma subsample +#define DX(x, nom, denom) static_cast<int>(((Abs(x) / nom + 1) / 2) * nom * 2) +#define SX(x, nom, denom) static_cast<int>(((x / nom + 1) / 2) * denom * 2) + +#define TEST_FACTOR1(name, filter, nom, denom, max_diff) \ + TEST_F(LibYUVScaleTest, ScaleDownBy##name##_##filter) { \ + int diff = TestFilter(SX(benchmark_width_, nom, denom), \ + SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), \ + DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, DISABLED_ScaleDownBy##name##_##filter##_16) { \ + int diff = TestFilter_16(SX(benchmark_width_, nom, denom), \ + SX(benchmark_height_, nom, denom), \ + DX(benchmark_width_, nom, denom), \ + DX(benchmark_height_, nom, denom), \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } + +// Test a scale factor with all 4 filters. Expect unfiltered to be exact, but +// filtering is different fixed point implementations for SSSE3, Neon and C. +#define TEST_FACTOR(name, nom, denom, boxdiff) \ + TEST_FACTOR1(name, None, nom, denom, 0) \ + TEST_FACTOR1(name, Linear, nom, denom, 3) \ + TEST_FACTOR1(name, Bilinear, nom, denom, 3) \ + TEST_FACTOR1(name, Box, nom, denom, boxdiff) + +TEST_FACTOR(2, 1, 2, 0) +TEST_FACTOR(4, 1, 4, 0) +TEST_FACTOR(8, 1, 8, 0) +TEST_FACTOR(3by4, 3, 4, 1) +TEST_FACTOR(3by8, 3, 8, 1) +TEST_FACTOR(3, 1, 3, 0) +#undef TEST_FACTOR1 +#undef TEST_FACTOR +#undef SX +#undef DX + +#define TEST_SCALETO1(name, width, height, filter, max_diff) \ + TEST_F(LibYUVScaleTest, name##To##width##x##height##_##filter) { \ + int diff = TestFilter(benchmark_width_, benchmark_height_, \ + width, height, \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, name##From##width##x##height##_##filter) { \ + int diff = TestFilter(width, height, \ + Abs(benchmark_width_), Abs(benchmark_height_), \ + kFilter##filter, benchmark_iterations_, \ + disable_cpu_flags_, benchmark_cpu_info_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, \ + DISABLED_##name##To##width##x##height##_##filter##_16) { \ + int diff = TestFilter_16(benchmark_width_, benchmark_height_, \ + width, height, \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } \ + TEST_F(LibYUVScaleTest, \ + DISABLED_##name##From##width##x##height##_##filter##_16) { \ + int diff = TestFilter_16(width, height, \ + Abs(benchmark_width_), Abs(benchmark_height_), \ + kFilter##filter, benchmark_iterations_); \ + EXPECT_LE(diff, max_diff); \ + } + +// Test scale to a specified size with all 4 filters. +#define TEST_SCALETO(name, width, height) \ + TEST_SCALETO1(name, width, height, None, 0) \ + TEST_SCALETO1(name, width, height, Linear, 0) \ + TEST_SCALETO1(name, width, height, Bilinear, 0) \ + TEST_SCALETO1(name, width, height, Box, 0) + +TEST_SCALETO(Scale, 1, 1) +TEST_SCALETO(Scale, 320, 240) +TEST_SCALETO(Scale, 352, 288) +TEST_SCALETO(Scale, 569, 480) +TEST_SCALETO(Scale, 640, 360) +TEST_SCALETO(Scale, 1280, 720) +#undef TEST_SCALETO1 +#undef TEST_SCALETO + +} // namespace libyuv diff --git a/media/libyuv/unit_test/testdata/arm_v7.txt b/media/libyuv/unit_test/testdata/arm_v7.txt new file mode 100644 index 000000000..5d7dbd048 --- /dev/null +++ b/media/libyuv/unit_test/testdata/arm_v7.txt @@ -0,0 +1,12 @@ +Processor : ARMv7 Processor rev 5 (v7l) +BogoMIPS : 795.44 +Features : swp half thumb fastmult vfp edsp iwmmxt thumbee vfpv3 vfpv3d16 +CPU implementer : 0x56 +CPU architecture: 7 +CPU variant : 0x0 +CPU part : 0x581 +CPU revision : 5 + +Hardware : OLPC XO-1.75 +Revision : 0000 +Serial : 0000000000000000 diff --git a/media/libyuv/unit_test/testdata/juno.txt b/media/libyuv/unit_test/testdata/juno.txt new file mode 100644 index 000000000..c275be742 --- /dev/null +++ b/media/libyuv/unit_test/testdata/juno.txt @@ -0,0 +1,15 @@ +Processor : AArch64 Processor rev 0 (aarch64)
+processor : 0
+processor : 1
+processor : 2
+processor : 3
+processor : 4
+processor : 5
+Features : fp asimd evtstrm aes pmull sha1 sha2 crc32
+CPU implementer : 0x41
+CPU architecture: AArch64
+CPU variant : 0x0
+CPU part : 0xd07
+CPU revision : 0
+
+Hardware : Juno
diff --git a/media/libyuv/unit_test/testdata/tegra3.txt b/media/libyuv/unit_test/testdata/tegra3.txt new file mode 100644 index 000000000..d1b09f6b7 --- /dev/null +++ b/media/libyuv/unit_test/testdata/tegra3.txt @@ -0,0 +1,23 @@ +Processor : ARMv7 Processor rev 9 (v7l) +processor : 0 +BogoMIPS : 1992.29 + +processor : 1 +BogoMIPS : 1992.29 + +processor : 2 +BogoMIPS : 1992.29 + +processor : 3 +BogoMIPS : 1992.29 + +Features : swp half thumb fastmult vfp edsp neon vfpv3 +CPU implementer : 0×41 +CPU architecture: 7 +CPU variant : 0×2 +CPU part : 0xc09 +CPU revision : 9 + +Hardware : cardhu +Revision : 0000 + diff --git a/media/libyuv/unit_test/unit_test.cc b/media/libyuv/unit_test/unit_test.cc new file mode 100644 index 000000000..e75510fd7 --- /dev/null +++ b/media/libyuv/unit_test/unit_test.cc @@ -0,0 +1,356 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "../unit_test/unit_test.h" + +#include <stdlib.h> // For getenv() + +#include <cstring> + +#include "gflags/gflags.h" + +// Change this to 1000 for benchmarking. +// TODO(fbarchard): Add command line parsing to pass this as option. +#define BENCHMARK_ITERATIONS 1 + +unsigned int fastrand_seed = 0xfb; + +DEFINE_int32(libyuv_width, 0, "width of test image."); +DEFINE_int32(libyuv_height, 0, "height of test image."); +DEFINE_int32(libyuv_repeat, 0, "number of times to repeat test."); +DEFINE_int32(libyuv_flags, 0, + "cpu flags for reference code. 1 = C, -1 = SIMD"); +DEFINE_int32(libyuv_cpu_info, 0, + "cpu flags for benchmark code. 1 = C, -1 = SIMD"); + +// For quicker unittests, default is 128 x 72. But when benchmarking, +// default to 720p. Allow size to specify. +// Set flags to -1 for benchmarking to avoid slower C code. + +LibYUVConvertTest::LibYUVConvertTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +LibYUVColorTest::LibYUVColorTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +LibYUVScaleTest::LibYUVScaleTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +LibYUVRotateTest::LibYUVRotateTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +LibYUVPlanarTest::LibYUVPlanarTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +LibYUVBaseTest::LibYUVBaseTest() : + benchmark_iterations_(BENCHMARK_ITERATIONS), benchmark_width_(128), + benchmark_height_(72), disable_cpu_flags_(1), benchmark_cpu_info_(-1) { + const char* repeat = getenv("LIBYUV_REPEAT"); + if (repeat) { + benchmark_iterations_ = atoi(repeat); // NOLINT + } + if (FLAGS_libyuv_repeat) { + benchmark_iterations_ = FLAGS_libyuv_repeat; + } + if (benchmark_iterations_ > 1) { + benchmark_width_ = 1280; + benchmark_height_ = 720; + } + const char* width = getenv("LIBYUV_WIDTH"); + if (width) { + benchmark_width_ = atoi(width); // NOLINT + } + if (FLAGS_libyuv_width) { + benchmark_width_ = FLAGS_libyuv_width; + } + const char* height = getenv("LIBYUV_HEIGHT"); + if (height) { + benchmark_height_ = atoi(height); // NOLINT + } + if (FLAGS_libyuv_height) { + benchmark_height_ = FLAGS_libyuv_height; + } + const char* cpu_flags = getenv("LIBYUV_FLAGS"); + if (cpu_flags) { + disable_cpu_flags_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_flags) { + disable_cpu_flags_ = FLAGS_libyuv_flags; + } + const char* cpu_info = getenv("LIBYUV_CPU_INFO"); + if (cpu_info) { + benchmark_cpu_info_ = atoi(cpu_flags); // NOLINT + } + if (FLAGS_libyuv_cpu_info) { + benchmark_cpu_info_ = FLAGS_libyuv_cpu_info; + } + benchmark_pixels_div256_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 255.0) / 256.0); + benchmark_pixels_div1280_ = static_cast<int>(( + static_cast<double>(Abs(benchmark_width_)) * + static_cast<double>(Abs(benchmark_height_)) * + static_cast<double>(benchmark_iterations_) + 1279.0) / 1280.0); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + // AllowCommandLineParsing allows us to ignore flags passed on to us by + // Chromium build bots without having to explicitly disable them. + google::AllowCommandLineReparsing(); + google::ParseCommandLineFlags(&argc, &argv, true); + return RUN_ALL_TESTS(); +} diff --git a/media/libyuv/unit_test/unit_test.h b/media/libyuv/unit_test/unit_test.h new file mode 100644 index 000000000..f2c4bef00 --- /dev/null +++ b/media/libyuv/unit_test/unit_test.h @@ -0,0 +1,199 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef UNIT_TEST_UNIT_TEST_H_ // NOLINT +#define UNIT_TEST_UNIT_TEST_H_ + +#ifdef WIN32 +#include <windows.h> +#else +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#include <gtest/gtest.h> + +#include "libyuv/basic_types.h" + +#ifndef SIMD_ALIGNED +#if defined(_MSC_VER) && !defined(__CLR_VER) +#define SIMD_ALIGNED(var) __declspec(align(16)) var +#elif defined(__GNUC__) && !defined(__pnacl__) +#define SIMD_ALIGNED(var) var __attribute__((aligned(16))) +#else +#define SIMD_ALIGNED(var) var +#endif +#endif + +static __inline int Abs(int v) { + return v >= 0 ? v : -v; +} + +#define OFFBY 0 + +// Scaling uses 16.16 fixed point to step thru the source image, so a +// maximum size of 32767.999 can be expressed. 32768 is valid because +// the step is 1 beyond the image but not used. +// Destination size is mainly constrained by valid scale step not the +// absolute size, so it may be possible to relax the destination size +// constraint. +// Source size is unconstrained for most specialized scalers. e.g. +// An image of 65536 scaled to half size would be valid. The test +// could be relaxed for special scale factors. +// If this test is removed, the scaling function should gracefully +// fail with a return code. The test could be changed to know that +// libyuv failed in a controlled way. + +static const int kMaxWidth = 32768; +static const int kMaxHeight = 32768; + +static inline bool SizeValid(int src_width, int src_height, + int dst_width, int dst_height) { + if (src_width > kMaxWidth || src_height > kMaxHeight || + dst_width > kMaxWidth || dst_height > kMaxHeight) { + printf("Warning - size too large to test. Skipping\n"); + return false; + } + return true; +} + +#define align_buffer_page_end(var, size) \ + uint8* var; \ + uint8* var##_mem; \ + var##_mem = reinterpret_cast<uint8*>(malloc(((size) + 4095 + 63) & ~4095)); \ + var = (uint8*)((intptr_t)(var##_mem + (((size) + 4095 + 63) & ~4095) - \ + (size)) & ~63); + +#define free_aligned_buffer_page_end(var) \ + free(var##_mem); \ + var = 0; + +#ifdef WIN32 +static inline double get_time() { + LARGE_INTEGER t, f; + QueryPerformanceCounter(&t); + QueryPerformanceFrequency(&f); + return static_cast<double>(t.QuadPart) / static_cast<double>(f.QuadPart); +} +#else +static inline double get_time() { + struct timeval t; + struct timezone tzp; + gettimeofday(&t, &tzp); + return t.tv_sec + t.tv_usec * 1e-6; +} +#endif + +#ifndef SIMD_ALIGNED +#if defined(_MSC_VER) && !defined(__CLR_VER) +#define SIMD_ALIGNED(var) __declspec(align(16)) var +#elif defined(__GNUC__) && !defined(__pnacl__) +#define SIMD_ALIGNED(var) var __attribute__((aligned(16))) +#else +#define SIMD_ALIGNED(var) var +#endif +#endif + +extern unsigned int fastrand_seed; +inline int fastrand() { + fastrand_seed = fastrand_seed * 214013u + 2531011u; + return static_cast<int>((fastrand_seed >> 16) & 0xffff); +} + +static inline void MemRandomize(uint8* dst, int64 len) { + int64 i; + for (i = 0; i < len - 1; i += 2) { + *reinterpret_cast<uint16*>(dst) = fastrand(); + dst += 2; + } + for (; i < len; ++i) { + *dst++ = fastrand(); + } +} + +class LibYUVColorTest : public ::testing::Test { + protected: + LibYUVColorTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +class LibYUVConvertTest : public ::testing::Test { + protected: + LibYUVConvertTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +class LibYUVScaleTest : public ::testing::Test { + protected: + LibYUVScaleTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +class LibYUVRotateTest : public ::testing::Test { + protected: + LibYUVRotateTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +class LibYUVPlanarTest : public ::testing::Test { + protected: + LibYUVPlanarTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +class LibYUVBaseTest : public ::testing::Test { + protected: + LibYUVBaseTest(); + + int benchmark_iterations_; // Default 1. Use 1000 for benchmarking. + int benchmark_width_; // Default 1280. Use 640 for benchmarking VGA. + int benchmark_height_; // Default 720. Use 360 for benchmarking VGA. + int benchmark_pixels_div256_; // Total pixels to benchmark / 256. + int benchmark_pixels_div1280_; // Total pixels to benchmark / 1280. + int disable_cpu_flags_; // Default 1. Use -1 for benchmarking. + int benchmark_cpu_info_; // Default -1. Use 1 to disable SIMD. +}; + +#endif // UNIT_TEST_UNIT_TEST_H_ NOLINT diff --git a/media/libyuv/unit_test/video_common_test.cc b/media/libyuv/unit_test/video_common_test.cc new file mode 100644 index 000000000..ac97d0f34 --- /dev/null +++ b/media/libyuv/unit_test/video_common_test.cc @@ -0,0 +1,107 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include <stdlib.h> +#include <string.h> + +#include "libyuv/video_common.h" +#include "../unit_test/unit_test.h" + +namespace libyuv { + +// Tests FourCC codes in video common, which are used for ConvertToI420(). + +static bool TestValidChar(uint32 onecc) { + if ((onecc >= '0' && onecc <= '9') || + (onecc >= 'A' && onecc <= 'Z') || + (onecc >= 'a' && onecc <= 'z') || + (onecc == ' ') || (onecc == 0xff)) { + return true; + } + return false; +} + +static bool TestValidFourCC(uint32 fourcc, int bpp) { + if (!TestValidChar(fourcc & 0xff) || + !TestValidChar((fourcc >> 8) & 0xff) || + !TestValidChar((fourcc >> 16) & 0xff) || + !TestValidChar((fourcc >> 24) & 0xff)) { + return false; + } + if (bpp < 0 || bpp > 32) { + return false; + } + return true; +} + +TEST_F(LibYUVBaseTest, TestCanonicalFourCC) { + EXPECT_EQ(FOURCC_I420, CanonicalFourCC(FOURCC_IYUV)); + EXPECT_EQ(FOURCC_I420, CanonicalFourCC(FOURCC_YU12)); + EXPECT_EQ(FOURCC_I422, CanonicalFourCC(FOURCC_YU16)); + EXPECT_EQ(FOURCC_I444, CanonicalFourCC(FOURCC_YU24)); + EXPECT_EQ(FOURCC_YUY2, CanonicalFourCC(FOURCC_YUYV)); + EXPECT_EQ(FOURCC_YUY2, CanonicalFourCC(FOURCC_YUVS)); + EXPECT_EQ(FOURCC_UYVY, CanonicalFourCC(FOURCC_HDYC)); + EXPECT_EQ(FOURCC_UYVY, CanonicalFourCC(FOURCC_2VUY)); + EXPECT_EQ(FOURCC_MJPG, CanonicalFourCC(FOURCC_JPEG)); + EXPECT_EQ(FOURCC_MJPG, CanonicalFourCC(FOURCC_DMB1)); + EXPECT_EQ(FOURCC_RAW, CanonicalFourCC(FOURCC_RGB3)); + EXPECT_EQ(FOURCC_24BG, CanonicalFourCC(FOURCC_BGR3)); + EXPECT_EQ(FOURCC_BGRA, CanonicalFourCC(FOURCC_CM32)); + EXPECT_EQ(FOURCC_RAW, CanonicalFourCC(FOURCC_CM24)); + EXPECT_EQ(FOURCC_RGBO, CanonicalFourCC(FOURCC_L555)); + EXPECT_EQ(FOURCC_RGBP, CanonicalFourCC(FOURCC_L565)); + EXPECT_EQ(FOURCC_RGBO, CanonicalFourCC(FOURCC_5551)); +} + +TEST_F(LibYUVBaseTest, TestFourCC) { + EXPECT_TRUE(TestValidFourCC(FOURCC_I420, FOURCC_BPP_I420)); + EXPECT_TRUE(TestValidFourCC(FOURCC_I420, FOURCC_BPP_I420)); + EXPECT_TRUE(TestValidFourCC(FOURCC_I422, FOURCC_BPP_I422)); + EXPECT_TRUE(TestValidFourCC(FOURCC_I444, FOURCC_BPP_I444)); + EXPECT_TRUE(TestValidFourCC(FOURCC_I411, FOURCC_BPP_I411)); + EXPECT_TRUE(TestValidFourCC(FOURCC_I400, FOURCC_BPP_I400)); + EXPECT_TRUE(TestValidFourCC(FOURCC_NV21, FOURCC_BPP_NV21)); + EXPECT_TRUE(TestValidFourCC(FOURCC_NV12, FOURCC_BPP_NV12)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YUY2, FOURCC_BPP_YUY2)); + EXPECT_TRUE(TestValidFourCC(FOURCC_UYVY, FOURCC_BPP_UYVY)); + EXPECT_TRUE(TestValidFourCC(FOURCC_M420, FOURCC_BPP_M420)); + EXPECT_TRUE(TestValidFourCC(FOURCC_Q420, FOURCC_BPP_Q420)); // deprecated. + EXPECT_TRUE(TestValidFourCC(FOURCC_ARGB, FOURCC_BPP_ARGB)); + EXPECT_TRUE(TestValidFourCC(FOURCC_BGRA, FOURCC_BPP_BGRA)); + EXPECT_TRUE(TestValidFourCC(FOURCC_ABGR, FOURCC_BPP_ABGR)); + EXPECT_TRUE(TestValidFourCC(FOURCC_24BG, FOURCC_BPP_24BG)); + EXPECT_TRUE(TestValidFourCC(FOURCC_RAW, FOURCC_BPP_RAW)); + EXPECT_TRUE(TestValidFourCC(FOURCC_RGBA, FOURCC_BPP_RGBA)); + EXPECT_TRUE(TestValidFourCC(FOURCC_RGBP, FOURCC_BPP_RGBP)); + EXPECT_TRUE(TestValidFourCC(FOURCC_RGBO, FOURCC_BPP_RGBO)); + EXPECT_TRUE(TestValidFourCC(FOURCC_R444, FOURCC_BPP_R444)); + EXPECT_TRUE(TestValidFourCC(FOURCC_MJPG, FOURCC_BPP_MJPG)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YV12, FOURCC_BPP_YV12)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YV16, FOURCC_BPP_YV16)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YV24, FOURCC_BPP_YV24)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YU12, FOURCC_BPP_YU12)); + EXPECT_TRUE(TestValidFourCC(FOURCC_IYUV, FOURCC_BPP_IYUV)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YU16, FOURCC_BPP_YU16)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YU24, FOURCC_BPP_YU24)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YUYV, FOURCC_BPP_YUYV)); + EXPECT_TRUE(TestValidFourCC(FOURCC_YUVS, FOURCC_BPP_YUVS)); + EXPECT_TRUE(TestValidFourCC(FOURCC_HDYC, FOURCC_BPP_HDYC)); + EXPECT_TRUE(TestValidFourCC(FOURCC_2VUY, FOURCC_BPP_2VUY)); + EXPECT_TRUE(TestValidFourCC(FOURCC_JPEG, FOURCC_BPP_JPEG)); + EXPECT_TRUE(TestValidFourCC(FOURCC_DMB1, FOURCC_BPP_DMB1)); + EXPECT_TRUE(TestValidFourCC(FOURCC_BA81, FOURCC_BPP_BA81)); + EXPECT_TRUE(TestValidFourCC(FOURCC_RGB3, FOURCC_BPP_RGB3)); + EXPECT_TRUE(TestValidFourCC(FOURCC_BGR3, FOURCC_BPP_BGR3)); + EXPECT_TRUE(TestValidFourCC(FOURCC_H264, FOURCC_BPP_H264)); + EXPECT_TRUE(TestValidFourCC(FOURCC_ANY, FOURCC_BPP_ANY)); +} + +} // namespace libyuv |