summaryrefslogtreecommitdiff
path: root/media/base/yuv_convert_unittest.cc
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
committerTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
commit5821806d5e7f356e8fa4b058a389a808ea183019 (patch)
treee19f4793aac92e2c0d9a01087019a60d6657d838 /media/base/yuv_convert_unittest.cc
parent8e79a8efe247f109aafd917a69e8a392961b3687 (diff)
downloadchromium_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.cc937
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)