diff options
author | Torne (Richard Coles) <torne@google.com> | 2012-11-14 11:43:16 +0000 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2012-11-14 11:43:16 +0000 |
commit | 5821806d5e7f356e8fa4b058a389a808ea183019 (patch) | |
tree | e19f4793aac92e2c0d9a01087019a60d6657d838 /media/base/yuv_convert_unittest.cc | |
parent | 8e79a8efe247f109aafd917a69e8a392961b3687 (diff) | |
download | chromium_org-5821806d5e7f356e8fa4b058a389a808ea183019.tar.gz |
Merge from Chromium at DEPS revision r167172
This commit was generated by merge_to_master.py.
Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
Diffstat (limited to 'media/base/yuv_convert_unittest.cc')
-rw-r--r-- | media/base/yuv_convert_unittest.cc | 937 |
1 files changed, 937 insertions, 0 deletions
diff --git a/media/base/yuv_convert_unittest.cc b/media/base/yuv_convert_unittest.cc new file mode 100644 index 0000000000..c57f715215 --- /dev/null +++ b/media/base/yuv_convert_unittest.cc @@ -0,0 +1,937 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_paths.h" +#include "base/cpu.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "media/base/djb2.h" +#include "media/base/simd/convert_rgb_to_yuv.h" +#include "media/base/simd/convert_yuv_to_rgb.h" +#include "media/base/simd/filter_yuv.h" +#include "media/base/yuv_convert.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/gfx/rect.h" + +// Size of raw image. +static const int kSourceWidth = 640; +static const int kSourceHeight = 360; +static const int kSourceYSize = kSourceWidth * kSourceHeight; +static const int kSourceUOffset = kSourceYSize; +static const int kSourceVOffset = kSourceYSize * 5 / 4; +static const int kScaledWidth = 1024; +static const int kScaledHeight = 768; +static const int kDownScaledWidth = 512; +static const int kDownScaledHeight = 320; +static const int kBpp = 4; + +// Surface sizes for various test files. +static const int kYUV12Size = kSourceYSize * 12 / 8; +static const int kYUV16Size = kSourceYSize * 16 / 8; +static const int kYUY2Size = kSourceYSize * 16 / 8; +static const int kRGBSize = kSourceYSize * kBpp; +static const int kRGBSizeScaled = kScaledWidth * kScaledHeight * kBpp; +static const int kRGB24Size = kSourceYSize * 3; +static const int kRGBSizeConverted = kSourceYSize * kBpp; + +// Helper for reading test data into a scoped_array<uint8>. +static void ReadData(const FilePath::CharType* filename, + int expected_size, + scoped_array<uint8>* data) { + data->reset(new uint8[expected_size]); + + FilePath path; + CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); + path = path.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(filename); + + // Verify file size is correct. + int64 actual_size = 0; + file_util::GetFileSize(path, &actual_size); + CHECK_EQ(actual_size, expected_size); + + // Verify bytes read are correct. + int bytes_read = file_util::ReadFile( + path, reinterpret_cast<char*>(data->get()), expected_size); + CHECK_EQ(bytes_read, expected_size); +} + +static void ReadYV12Data(scoped_array<uint8>* data) { + ReadData(FILE_PATH_LITERAL("bali_640x360_P420.yuv"), kYUV12Size, data); +} + +static void ReadYV16Data(scoped_array<uint8>* data) { + ReadData(FILE_PATH_LITERAL("bali_640x360_P422.yuv"), kYUV16Size, data); +} + +static void ReadRGB24Data(scoped_array<uint8>* data) { + ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data); +} + +static void ReadYUY2Data(scoped_array<uint8>* data) { + ReadData(FILE_PATH_LITERAL("bali_640x360_YUY2.yuv"), kYUY2Size, data); +} + +TEST(YUVConvertTest, YV12) { + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes; + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> rgb_converted_bytes(new uint8[kRGBSizeConverted]); + + // Read YUV reference data from file. + ReadYV12Data(&yuv_bytes); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_converted_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, + kDJB2HashSeed); + EXPECT_EQ(2413171226u, rgb_hash); +} + +TEST(YUVConvertTest, YV16) { + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes; + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> rgb_converted_bytes(new uint8[kRGBSizeConverted]); + + // Read YUV reference data from file. + ReadYV16Data(&yuv_bytes); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(yuv_bytes.get(), // Y + yuv_bytes.get() + kSourceUOffset, // U + yuv_bytes.get() + kSourceYSize * 3 / 2, // V + rgb_converted_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV16); + + uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, + kDJB2HashSeed); + EXPECT_EQ(4222342047u, rgb_hash); +} + +struct YUVScaleTestData { + YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32 r) + : yuv_type(y), + scale_filter(s), + rgb_hash(r) { + } + + media::YUVType yuv_type; + media::ScaleFilter scale_filter; + uint32 rgb_hash; +}; + +class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> { + public: + YUVScaleTest() { + switch (GetParam().yuv_type) { + case media::YV12: + ReadYV12Data(&yuv_bytes_); + break; + case media::YV16: + ReadYV16Data(&yuv_bytes_); + break; + } + + rgb_bytes_.reset(new uint8[kRGBSizeScaled]); + } + + // Helpers for getting the proper Y, U and V plane offsets. + uint8* y_plane() { return yuv_bytes_.get(); } + uint8* u_plane() { return yuv_bytes_.get() + kSourceYSize; } + uint8* v_plane() { + switch (GetParam().yuv_type) { + case media::YV12: + return yuv_bytes_.get() + kSourceVOffset; + case media::YV16: + return yuv_bytes_.get() + kSourceYSize * 3 / 2; + } + return NULL; + } + + scoped_array<uint8> yuv_bytes_; + scoped_array<uint8> rgb_bytes_; +}; + +TEST_P(YUVScaleTest, NoScale) { + media::ScaleYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kSourceWidth * kBpp, // RgbStride + GetParam().yuv_type, + media::ROTATE_0, + GetParam().scale_filter); + + uint32 yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); + + media::ConvertYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + GetParam().yuv_type); + + uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); + + EXPECT_EQ(yuv_hash, rgb_hash); +} + +TEST_P(YUVScaleTest, Normal) { + media::ScaleYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kScaledWidth, kScaledHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + GetParam().yuv_type, + media::ROTATE_0, + GetParam().scale_filter); + + uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed); + EXPECT_EQ(GetParam().rgb_hash, rgb_hash); +} + +TEST_P(YUVScaleTest, ZeroSourceSize) { + media::ScaleYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + 0, 0, // Dimensions + kScaledWidth, kScaledHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + GetParam().yuv_type, + media::ROTATE_0, + GetParam().scale_filter); + + // Testing for out-of-bound read/writes with AddressSanitizer. +} + +TEST_P(YUVScaleTest, ZeroDestinationSize) { + media::ScaleYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + 0, 0, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + GetParam().yuv_type, + media::ROTATE_0, + GetParam().scale_filter); + + // Testing for out-of-bound read/writes with AddressSanitizer. +} + +TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) { + media::ScaleYUVToRGB32(y_plane(), // Y + u_plane(), // U + v_plane(), // V + rgb_bytes_.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + 3, 3, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kScaledWidth * kBpp, // RgbStride + GetParam().yuv_type, + media::ROTATE_0, + GetParam().scale_filter); +} + +INSTANTIATE_TEST_CASE_P( + YUVScaleFormats, YUVScaleTest, + ::testing::Values( + YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u), + YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u), + YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u), + YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u))); + +// This tests a known worst case YUV value, and for overflow. +TEST(YUVConvertTest, Clamp) { + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes(new uint8[1]); + scoped_array<uint8> rgb_bytes(new uint8[1]); + scoped_array<uint8> rgb_converted_bytes(new uint8[1]); + + // Values that failed previously in bug report. + unsigned char y = 255u; + unsigned char u = 255u; + unsigned char v = 19u; + + // Prefill extra large destination buffer to test for overflow. + unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 }; + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(&y, // Y + &u, // U + &v, // V + &rgb[0], // RGB output + 1, 1, // Dimensions + 0, // YStride + 0, // UVStride + 0, // RGBStride + media::YV12); + + int expected_test = memcmp(rgb, expected, sizeof(expected)); + EXPECT_EQ(0, expected_test); +} + +TEST(YUVConvertTest, RGB24ToYUV) { + // Allocate all surfaces. + scoped_array<uint8> rgb_bytes; + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + + // Read RGB24 reference data from file. + ReadRGB24Data(&rgb_bytes); + + // Convert to I420. + media::ConvertRGB24ToYUV(rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 3, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + uint32 rgb_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, + kDJB2HashSeed); + EXPECT_EQ(320824432u, rgb_hash); +} + +TEST(YUVConvertTest, RGB32ToYUV) { + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_converted_bytes(new uint8[kRGBSize]); + + // Read YUV reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); + EXPECT_EQ(static_cast<int>(kYUV12Size), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes.get()), + static_cast<int>(kYUV12Size))); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + // Convert RGB32 to YV12. + media::ConvertRGB32ToYUV(rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert YV12 back to RGB32. + media::ConvertYUVToRGB32(yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + rgb_converted_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + int error = 0; + for (int i = 0; i < kRGBSize; ++i) { + int diff = rgb_converted_bytes[i] - rgb_bytes[i]; + if (diff < 0) + diff = -diff; + error += diff; + } + + // Make sure error is within bound. + DVLOG(1) << "Average error per channel: " << error / kRGBSize; + EXPECT_GT(5, error / kRGBSize); +} + +TEST(YUVConvertTest, YUY2ToYUV) { + // Allocate all surfaces. + scoped_array<uint8> yuy_bytes; + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + + // Read YUY reference data from file. + ReadYUY2Data(&yuy_bytes); + + // Convert to I420. + media::ConvertYUY2ToYUV(yuy_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight); + + uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, + kDJB2HashSeed); + EXPECT_EQ(666823187u, yuy_hash); +} + +TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { + // Read YUV reference data from file. + FilePath yuv_url; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); + yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); + const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp. + scoped_array<uint8> yuv_bytes(new uint8[size_of_yuv]); + EXPECT_EQ(static_cast<int>(size_of_yuv), + file_util::ReadFile(yuv_url, + reinterpret_cast<char*>(yuv_bytes.get()), + static_cast<int>(size_of_yuv))); + + // Scale the full frame of YUV to 32 bit ARGB. + // The API currently only supports down-scaling, so we don't test up-scaling. + const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp; + scoped_array<uint8> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]); + gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight); + + // We can't compare with the full-frame scaler because it uses slightly + // different sampling coordinates. + media::ScaleYUVToRGB32WithRect( + yuv_bytes.get(), // Y + yuv_bytes.get() + kSourceUOffset, // U + yuv_bytes.get() + kSourceVOffset, // V + rgb_scaled_bytes.get(), // Rgb output + kSourceWidth, kSourceHeight, // Dimensions + kDownScaledWidth, kDownScaledHeight, // Dimensions + sub_rect.x(), sub_rect.y(), // Dest rect + sub_rect.right(), sub_rect.bottom(), // Dest rect + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kDownScaledWidth * kBpp); // RgbStride + + uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(), + size_of_rgb_scaled, + kDJB2HashSeed); + + // Re-scale sub-rectangles and verify the results are the same. + int next_sub_rect = 0; + while (!sub_rect.IsEmpty()) { + // Scale a partial rectangle. + media::ScaleYUVToRGB32WithRect( + yuv_bytes.get(), // Y + yuv_bytes.get() + kSourceUOffset, // U + yuv_bytes.get() + kSourceVOffset, // V + rgb_scaled_bytes.get(), // Rgb output + kSourceWidth, kSourceHeight, // Dimensions + kDownScaledWidth, kDownScaledHeight, // Dimensions + sub_rect.x(), sub_rect.y(), // Dest rect + sub_rect.right(), sub_rect.bottom(), // Dest rect + kSourceWidth, // YStride + kSourceWidth / 2, // UvStride + kDownScaledWidth * kBpp); // RgbStride + uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(), + size_of_rgb_scaled, + kDJB2HashSeed); + + EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect); + + // Now pick choose a quarter rect of this sub-rect. + if (next_sub_rect & 1) + sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2); + if (next_sub_rect & 2) + sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2); + sub_rect.set_width(sub_rect.width() / 2); + sub_rect.set_height(sub_rect.height() / 2); + next_sub_rect++; + } +} + +#if !defined(ARCH_CPU_ARM_FAMILY) +TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { + base::CPU cpu; + if (!cpu.has_sse2()) { + LOG(WARNING) << "System doesn't support SSE2, test not executed."; + return; + } + + // Allocate all surfaces. + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes(new uint8[kRGBSize]); + scoped_array<uint8> yuv_converted_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> yuv_reference_bytes(new uint8[kYUV12Size]); + + ReadYV12Data(&yuv_bytes); + + // Convert a frame of YUV to 32 bit ARGB. + media::ConvertYUVToRGB32( + yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes.get(), // RGB output + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth, // YStride + kSourceWidth / 2, // UVStride + kSourceWidth * kBpp, // RGBStride + media::YV12); + + // Convert RGB32 to YV12 with SSE2 version. + media::ConvertRGB32ToYUV_SSE2( + rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert RGB32 to YV12 with reference version. + media::ConvertRGB32ToYUV_SSE2_Reference( + rgb_bytes.get(), + yuv_reference_bytes.get(), + yuv_reference_bytes.get() + kSourceUOffset, + yuv_reference_bytes.get() + kSourceVOffset, + kSourceWidth, kSourceHeight, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Now convert a odd width and height, this overrides part of the buffer + // generated above but that is fine because the point of this test is to + // match the result with the reference code. + + // Convert RGB32 to YV12 with SSE2 version. + media::ConvertRGB32ToYUV_SSE2( + rgb_bytes.get(), + yuv_converted_bytes.get(), + yuv_converted_bytes.get() + kSourceUOffset, + yuv_converted_bytes.get() + kSourceVOffset, + 7, 7, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + // Convert RGB32 to YV12 with reference version. + media::ConvertRGB32ToYUV_SSE2_Reference( + rgb_bytes.get(), + yuv_reference_bytes.get(), + yuv_reference_bytes.get() + kSourceUOffset, + yuv_reference_bytes.get() + kSourceVOffset, + 7, 7, // Dimensions + kSourceWidth * 4, // RGBStride + kSourceWidth, // YStride + kSourceWidth / 2); // UVStride + + int error = 0; + for (int i = 0; i < kYUV12Size; ++i) { + int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i]; + if (diff < 0) + diff = -diff; + error += diff; + } + + // Make sure there's no difference from the reference. + EXPECT_EQ(0, error); +} + +TEST(YUVConvertTest, ConvertYUVToRGB32Row_MMX) { + base::CPU cpu; + if (!cpu.has_mmx()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + ConvertYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth); + ConvertYUVToRGB32Row_MMX(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) { + base::CPU cpu; + if (!cpu.has_sse()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + ConvertYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth); + ConvertYUVToRGB32Row_SSE(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, ScaleYUVToRGB32Row_MMX) { + base::CPU cpu; + if (!cpu.has_mmx()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + ScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + ScaleYUVToRGB32Row_MMX(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) { + base::CPU cpu; + if (!cpu.has_sse()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + ScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + ScaleYUVToRGB32Row_SSE(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX) { + base::CPU cpu; + if (!cpu.has_mmx()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + LinearScaleYUVToRGB32Row_MMX(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) { + base::CPU cpu; + if (!cpu.has_sse()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) { + scoped_array<uint8> src(new uint8[16]); + scoped_array<uint8> dst(new uint8[16]); + + memset(src.get(), 0xff, 16); + memset(dst.get(), 0, 16); + + media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255); + + EXPECT_EQ(255u, dst[0]); + for (int i = 1; i < 16; ++i) { + EXPECT_EQ(0u, dst[i]) << " not equal at " << i; + } +} + +TEST(YUVConvertTest, FilterYUVRows_MMX_OutOfBounds) { + base::CPU cpu; + if (!cpu.has_mmx()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> src(new uint8[16]); + scoped_array<uint8> dst(new uint8[16]); + + memset(src.get(), 0xff, 16); + memset(dst.get(), 0, 16); + + media::FilterYUVRows_MMX(dst.get(), src.get(), src.get(), 1, 255); + media::EmptyRegisterState(); + + EXPECT_EQ(255u, dst[0]); + for (int i = 1; i < 16; ++i) { + EXPECT_EQ(0u, dst[i]); + } +} + +TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) { + base::CPU cpu; + if (!cpu.has_sse2()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + scoped_array<uint8> src(new uint8[16]); + scoped_array<uint8> dst(new uint8[16]); + + memset(src.get(), 0xff, 16); + memset(dst.get(), 0, 16); + + media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255); + + EXPECT_EQ(255u, dst[0]); + for (int i = 1; i < 16; ++i) { + EXPECT_EQ(0u, dst[i]); + } +} + +TEST(YUVConvertTest, FilterYUVRows_MMX_UnalignedDestination) { + base::CPU cpu; + if (!cpu.has_mmx()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + const int kSize = 32; + scoped_array<uint8> src(new uint8[kSize]); + scoped_array<uint8> dst_sample(new uint8[kSize]); + scoped_array<uint8> dst(new uint8[kSize]); + + memset(dst_sample.get(), 0, kSize); + memset(dst.get(), 0, kSize); + for (int i = 0; i < kSize; ++i) + src[i] = 100 + i; + + media::FilterYUVRows_C(dst_sample.get(), + src.get(), src.get(), 17, 128); + + // Generate an unaligned output address. + uint8* dst_ptr = + reinterpret_cast<uint8*>( + (reinterpret_cast<uintptr_t>(dst.get() + 8) & ~7) + 1); + media::FilterYUVRows_MMX(dst_ptr, src.get(), src.get(), 17, 128); + media::EmptyRegisterState(); + + EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 17)); +} + +TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) { + base::CPU cpu; + if (!cpu.has_sse2()) { + LOG(WARNING) << "System not supported. Test skipped."; + return; + } + + const int kSize = 64; + scoped_array<uint8> src(new uint8[kSize]); + scoped_array<uint8> dst_sample(new uint8[kSize]); + scoped_array<uint8> dst(new uint8[kSize]); + + memset(dst_sample.get(), 0, kSize); + memset(dst.get(), 0, kSize); + for (int i = 0; i < kSize; ++i) + src[i] = 100 + i; + + media::FilterYUVRows_C(dst_sample.get(), + src.get(), src.get(), 37, 128); + + // Generate an unaligned output address. + uint8* dst_ptr = + reinterpret_cast<uint8*>( + (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1); + media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128); + media::EmptyRegisterState(); + + EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37)); +} + +#if defined(ARCH_CPU_X86_64) + +TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) { + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + ScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) { + scoped_array<uint8> yuv_bytes(new uint8[kYUV12Size]); + scoped_array<uint8> rgb_bytes_reference(new uint8[kRGBSize]); + scoped_array<uint8> rgb_bytes_converted(new uint8[kRGBSize]); + ReadYV12Data(&yuv_bytes); + + const int kWidth = 167; + const int kSourceDx = 80000; // This value means a scale down. + LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_reference.get(), + kWidth, + kSourceDx); + LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(), + yuv_bytes.get() + kSourceUOffset, + yuv_bytes.get() + kSourceVOffset, + rgb_bytes_converted.get(), + kWidth, + kSourceDx); + media::EmptyRegisterState(); + EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), + rgb_bytes_converted.get(), + kWidth * kBpp)); +} + +#endif // defined(ARCH_CPU_X86_64) + +#endif // defined(ARCH_CPU_X86_FAMILY) |