Add helper method to determine the current application horizontal size classes.
[chromium-blink-merge.git] / media / base / yuv_convert_unittest.cc
blob9ab8a9cab31e3b42f14757ec913c04017d61bbb9
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/base_paths.h"
6 #include "base/cpu.h"
7 #include "base/files/file_util.h"
8 #include "base/logging.h"
9 #include "base/path_service.h"
10 #include "media/base/djb2.h"
11 #include "media/base/simd/convert_rgb_to_yuv.h"
12 #include "media/base/simd/convert_yuv_to_rgb.h"
13 #include "media/base/simd/filter_yuv.h"
14 #include "media/base/yuv_convert.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "ui/gfx/geometry/rect.h"
18 // Size of raw image.
19 static const int kSourceWidth = 640;
20 static const int kSourceHeight = 360;
21 static const int kSourceYSize = kSourceWidth * kSourceHeight;
22 static const int kSourceUOffset = kSourceYSize;
23 static const int kSourceVOffset = kSourceYSize * 5 / 4;
24 static const int kScaledWidth = 1024;
25 static const int kScaledHeight = 768;
26 static const int kDownScaledWidth = 512;
27 static const int kDownScaledHeight = 320;
28 static const int kBpp = 4;
30 // Surface sizes for various test files.
31 static const int kYUV12Size = kSourceYSize * 12 / 8;
32 static const int kYUV16Size = kSourceYSize * 16 / 8;
33 static const int kYUY2Size = kSourceYSize * 16 / 8;
34 static const int kRGBSize = kSourceYSize * kBpp;
35 static const int kRGBSizeScaled = kScaledWidth * kScaledHeight * kBpp;
36 static const int kRGB24Size = kSourceYSize * 3;
37 static const int kRGBSizeConverted = kSourceYSize * kBpp;
39 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
40 static const int kSourceAOffset = kSourceYSize * 12 / 8;
41 static const int kYUVA12Size = kSourceYSize * 20 / 8;
42 #endif
44 // Helper for reading test data into a scoped_ptr<uint8[]>.
45 static void ReadData(const base::FilePath::CharType* filename,
46 int expected_size,
47 scoped_ptr<uint8[]>* data) {
48 data->reset(new uint8[expected_size]);
50 base::FilePath path;
51 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path));
52 path = path.Append(FILE_PATH_LITERAL("media"))
53 .Append(FILE_PATH_LITERAL("test"))
54 .Append(FILE_PATH_LITERAL("data"))
55 .Append(filename);
57 // Verify file size is correct.
58 int64 actual_size = 0;
59 base::GetFileSize(path, &actual_size);
60 CHECK_EQ(actual_size, expected_size);
62 // Verify bytes read are correct.
63 int bytes_read = base::ReadFile(
64 path, reinterpret_cast<char*>(data->get()), expected_size);
65 CHECK_EQ(bytes_read, expected_size);
68 static void ReadYV12Data(scoped_ptr<uint8[]>* data) {
69 ReadData(FILE_PATH_LITERAL("bali_640x360_P420.yuv"), kYUV12Size, data);
72 static void ReadYV16Data(scoped_ptr<uint8[]>* data) {
73 ReadData(FILE_PATH_LITERAL("bali_640x360_P422.yuv"), kYUV16Size, data);
76 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
77 static void ReadYV12AData(scoped_ptr<uint8[]>* data) {
78 ReadData(FILE_PATH_LITERAL("bali_640x360_P420_alpha.yuv"), kYUVA12Size, data);
80 #endif
82 static void ReadRGB24Data(scoped_ptr<uint8[]>* data) {
83 ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data);
86 static void ReadYUY2Data(scoped_ptr<uint8[]>* data) {
87 ReadData(FILE_PATH_LITERAL("bali_640x360_YUY2.yuv"), kYUY2Size, data);
90 #if defined(OS_ANDROID)
91 // Helper for swapping red and blue channels of RGBA or BGRA.
92 static void SwapRedAndBlueChannels(unsigned char* pixels, size_t buffer_size) {
93 for (size_t i = 0; i < buffer_size; i += 4) {
94 std::swap(pixels[i], pixels[i + 2]);
97 #endif
99 namespace media {
101 TEST(YUVConvertTest, YV12) {
102 // Allocate all surfaces.
103 scoped_ptr<uint8[]> yuv_bytes;
104 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
105 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
107 // Read YUV reference data from file.
108 ReadYV12Data(&yuv_bytes);
110 // Convert a frame of YUV to 32 bit ARGB.
111 media::ConvertYUVToRGB32(yuv_bytes.get(),
112 yuv_bytes.get() + kSourceUOffset,
113 yuv_bytes.get() + kSourceVOffset,
114 rgb_converted_bytes.get(), // RGB output
115 kSourceWidth, kSourceHeight, // Dimensions
116 kSourceWidth, // YStride
117 kSourceWidth / 2, // UVStride
118 kSourceWidth * kBpp, // RGBStride
119 media::YV12);
121 #if defined(OS_ANDROID)
122 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted);
123 #endif
125 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted,
126 kDJB2HashSeed);
127 EXPECT_EQ(2413171226u, rgb_hash);
130 TEST(YUVConvertTest, YV16) {
131 // Allocate all surfaces.
132 scoped_ptr<uint8[]> yuv_bytes;
133 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
134 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
136 // Read YUV reference data from file.
137 ReadYV16Data(&yuv_bytes);
139 // Convert a frame of YUV to 32 bit ARGB.
140 media::ConvertYUVToRGB32(yuv_bytes.get(), // Y
141 yuv_bytes.get() + kSourceUOffset, // U
142 yuv_bytes.get() + kSourceYSize * 3 / 2, // V
143 rgb_converted_bytes.get(), // RGB output
144 kSourceWidth, kSourceHeight, // Dimensions
145 kSourceWidth, // YStride
146 kSourceWidth / 2, // UVStride
147 kSourceWidth * kBpp, // RGBStride
148 media::YV16);
150 #if defined(OS_ANDROID)
151 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted);
152 #endif
154 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted,
155 kDJB2HashSeed);
156 EXPECT_EQ(4222342047u, rgb_hash);
159 struct YUVScaleTestData {
160 YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32 r)
161 : yuv_type(y),
162 scale_filter(s),
163 rgb_hash(r) {
166 media::YUVType yuv_type;
167 media::ScaleFilter scale_filter;
168 uint32 rgb_hash;
171 class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> {
172 public:
173 YUVScaleTest() {
174 switch (GetParam().yuv_type) {
175 case media::YV12:
176 case media::YV12J:
177 case media::YV12HD:
178 ReadYV12Data(&yuv_bytes_);
179 break;
180 case media::YV16:
181 ReadYV16Data(&yuv_bytes_);
182 break;
185 rgb_bytes_.reset(new uint8[kRGBSizeScaled]);
188 // Helpers for getting the proper Y, U and V plane offsets.
189 uint8* y_plane() { return yuv_bytes_.get(); }
190 uint8* u_plane() { return yuv_bytes_.get() + kSourceYSize; }
191 uint8* v_plane() {
192 switch (GetParam().yuv_type) {
193 case media::YV12:
194 case media::YV12J:
195 case media::YV12HD:
196 return yuv_bytes_.get() + kSourceVOffset;
197 case media::YV16:
198 return yuv_bytes_.get() + kSourceYSize * 3 / 2;
200 return NULL;
203 scoped_ptr<uint8[]> yuv_bytes_;
204 scoped_ptr<uint8[]> rgb_bytes_;
207 TEST_P(YUVScaleTest, NoScale) {
208 media::ScaleYUVToRGB32(y_plane(), // Y
209 u_plane(), // U
210 v_plane(), // V
211 rgb_bytes_.get(), // RGB output
212 kSourceWidth, kSourceHeight, // Dimensions
213 kSourceWidth, kSourceHeight, // Dimensions
214 kSourceWidth, // YStride
215 kSourceWidth / 2, // UvStride
216 kSourceWidth * kBpp, // RgbStride
217 GetParam().yuv_type,
218 media::ROTATE_0,
219 GetParam().scale_filter);
221 uint32 yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed);
223 media::ConvertYUVToRGB32(y_plane(), // Y
224 u_plane(), // U
225 v_plane(), // V
226 rgb_bytes_.get(), // RGB output
227 kSourceWidth, kSourceHeight, // Dimensions
228 kSourceWidth, // YStride
229 kSourceWidth / 2, // UVStride
230 kSourceWidth * kBpp, // RGBStride
231 GetParam().yuv_type);
233 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed);
235 EXPECT_EQ(yuv_hash, rgb_hash);
238 TEST_P(YUVScaleTest, Normal) {
239 media::ScaleYUVToRGB32(y_plane(), // Y
240 u_plane(), // U
241 v_plane(), // V
242 rgb_bytes_.get(), // RGB output
243 kSourceWidth, kSourceHeight, // Dimensions
244 kScaledWidth, kScaledHeight, // Dimensions
245 kSourceWidth, // YStride
246 kSourceWidth / 2, // UvStride
247 kScaledWidth * kBpp, // RgbStride
248 GetParam().yuv_type,
249 media::ROTATE_0,
250 GetParam().scale_filter);
252 #if defined(OS_ANDROID)
253 SwapRedAndBlueChannels(rgb_bytes_.get(), kRGBSizeScaled);
254 #endif
256 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed);
257 EXPECT_EQ(GetParam().rgb_hash, rgb_hash);
260 TEST_P(YUVScaleTest, ZeroSourceSize) {
261 media::ScaleYUVToRGB32(y_plane(), // Y
262 u_plane(), // U
263 v_plane(), // V
264 rgb_bytes_.get(), // RGB output
265 0, 0, // Dimensions
266 kScaledWidth, kScaledHeight, // Dimensions
267 kSourceWidth, // YStride
268 kSourceWidth / 2, // UvStride
269 kScaledWidth * kBpp, // RgbStride
270 GetParam().yuv_type,
271 media::ROTATE_0,
272 GetParam().scale_filter);
274 // Testing for out-of-bound read/writes with AddressSanitizer.
277 TEST_P(YUVScaleTest, ZeroDestinationSize) {
278 media::ScaleYUVToRGB32(y_plane(), // Y
279 u_plane(), // U
280 v_plane(), // V
281 rgb_bytes_.get(), // RGB output
282 kSourceWidth, kSourceHeight, // Dimensions
283 0, 0, // Dimensions
284 kSourceWidth, // YStride
285 kSourceWidth / 2, // UvStride
286 kScaledWidth * kBpp, // RgbStride
287 GetParam().yuv_type,
288 media::ROTATE_0,
289 GetParam().scale_filter);
291 // Testing for out-of-bound read/writes with AddressSanitizer.
294 TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) {
295 media::ScaleYUVToRGB32(y_plane(), // Y
296 u_plane(), // U
297 v_plane(), // V
298 rgb_bytes_.get(), // RGB output
299 kSourceWidth, kSourceHeight, // Dimensions
300 3, 3, // Dimensions
301 kSourceWidth, // YStride
302 kSourceWidth / 2, // UvStride
303 kScaledWidth * kBpp, // RgbStride
304 GetParam().yuv_type,
305 media::ROTATE_0,
306 GetParam().scale_filter);
309 INSTANTIATE_TEST_CASE_P(
310 YUVScaleFormats, YUVScaleTest,
311 ::testing::Values(
312 YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u),
313 YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u),
314 YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u),
315 YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u)));
317 // This tests a known worst case YUV value, and for overflow.
318 TEST(YUVConvertTest, Clamp) {
319 // Allocate all surfaces.
320 scoped_ptr<uint8[]> yuv_bytes(new uint8[1]);
321 scoped_ptr<uint8[]> rgb_bytes(new uint8[1]);
322 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[1]);
324 // Values that failed previously in bug report.
325 unsigned char y = 255u;
326 unsigned char u = 255u;
327 unsigned char v = 19u;
329 // Prefill extra large destination buffer to test for overflow.
330 unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
331 unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 };
332 // Convert a frame of YUV to 32 bit ARGB.
333 media::ConvertYUVToRGB32(&y, // Y
334 &u, // U
335 &v, // V
336 &rgb[0], // RGB output
337 1, 1, // Dimensions
338 0, // YStride
339 0, // UVStride
340 0, // RGBStride
341 media::YV12);
343 #if defined(OS_ANDROID)
344 SwapRedAndBlueChannels(rgb, kBpp);
345 #endif
347 int expected_test = memcmp(rgb, expected, sizeof(expected));
348 EXPECT_EQ(0, expected_test);
351 TEST(YUVConvertTest, RGB24ToYUV) {
352 // Allocate all surfaces.
353 scoped_ptr<uint8[]> rgb_bytes;
354 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
356 // Read RGB24 reference data from file.
357 ReadRGB24Data(&rgb_bytes);
359 // Convert to I420.
360 media::ConvertRGB24ToYUV(rgb_bytes.get(),
361 yuv_converted_bytes.get(),
362 yuv_converted_bytes.get() + kSourceUOffset,
363 yuv_converted_bytes.get() + kSourceVOffset,
364 kSourceWidth, kSourceHeight, // Dimensions
365 kSourceWidth * 3, // RGBStride
366 kSourceWidth, // YStride
367 kSourceWidth / 2); // UVStride
369 uint32 rgb_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size,
370 kDJB2HashSeed);
371 EXPECT_EQ(320824432u, rgb_hash);
374 TEST(YUVConvertTest, RGB32ToYUV) {
375 // Allocate all surfaces.
376 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
377 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
378 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
379 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSize]);
381 // Read YUV reference data from file.
382 base::FilePath yuv_url;
383 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url));
384 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media"))
385 .Append(FILE_PATH_LITERAL("test"))
386 .Append(FILE_PATH_LITERAL("data"))
387 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv"));
388 EXPECT_EQ(static_cast<int>(kYUV12Size),
389 base::ReadFile(yuv_url,
390 reinterpret_cast<char*>(yuv_bytes.get()),
391 static_cast<int>(kYUV12Size)));
393 // Convert a frame of YUV to 32 bit ARGB.
394 media::ConvertYUVToRGB32(yuv_bytes.get(),
395 yuv_bytes.get() + kSourceUOffset,
396 yuv_bytes.get() + kSourceVOffset,
397 rgb_bytes.get(), // RGB output
398 kSourceWidth, kSourceHeight, // Dimensions
399 kSourceWidth, // YStride
400 kSourceWidth / 2, // UVStride
401 kSourceWidth * kBpp, // RGBStride
402 media::YV12);
404 // Convert RGB32 to YV12.
405 media::ConvertRGB32ToYUV(rgb_bytes.get(),
406 yuv_converted_bytes.get(),
407 yuv_converted_bytes.get() + kSourceUOffset,
408 yuv_converted_bytes.get() + kSourceVOffset,
409 kSourceWidth, kSourceHeight, // Dimensions
410 kSourceWidth * 4, // RGBStride
411 kSourceWidth, // YStride
412 kSourceWidth / 2); // UVStride
414 // Convert YV12 back to RGB32.
415 media::ConvertYUVToRGB32(yuv_converted_bytes.get(),
416 yuv_converted_bytes.get() + kSourceUOffset,
417 yuv_converted_bytes.get() + kSourceVOffset,
418 rgb_converted_bytes.get(), // RGB output
419 kSourceWidth, kSourceHeight, // Dimensions
420 kSourceWidth, // YStride
421 kSourceWidth / 2, // UVStride
422 kSourceWidth * kBpp, // RGBStride
423 media::YV12);
425 int error = 0;
426 for (int i = 0; i < kRGBSize; ++i) {
427 int diff = rgb_converted_bytes[i] - rgb_bytes[i];
428 if (diff < 0)
429 diff = -diff;
430 error += diff;
433 // Make sure error is within bound.
434 DVLOG(1) << "Average error per channel: " << error / kRGBSize;
435 EXPECT_GT(5, error / kRGBSize);
438 TEST(YUVConvertTest, YUY2ToYUV) {
439 // Allocate all surfaces.
440 scoped_ptr<uint8[]> yuy_bytes;
441 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
443 // Read YUY reference data from file.
444 ReadYUY2Data(&yuy_bytes);
446 // Convert to I420.
447 media::ConvertYUY2ToYUV(yuy_bytes.get(),
448 yuv_converted_bytes.get(),
449 yuv_converted_bytes.get() + kSourceUOffset,
450 yuv_converted_bytes.get() + kSourceVOffset,
451 kSourceWidth, kSourceHeight);
453 uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size,
454 kDJB2HashSeed);
455 EXPECT_EQ(666823187u, yuy_hash);
458 TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) {
459 // Read YUV reference data from file.
460 base::FilePath yuv_url;
461 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url));
462 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media"))
463 .Append(FILE_PATH_LITERAL("test"))
464 .Append(FILE_PATH_LITERAL("data"))
465 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv"));
466 const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp.
467 scoped_ptr<uint8[]> yuv_bytes(new uint8[size_of_yuv]);
468 EXPECT_EQ(static_cast<int>(size_of_yuv),
469 base::ReadFile(yuv_url,
470 reinterpret_cast<char*>(yuv_bytes.get()),
471 static_cast<int>(size_of_yuv)));
473 // Scale the full frame of YUV to 32 bit ARGB.
474 // The API currently only supports down-scaling, so we don't test up-scaling.
475 const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp;
476 scoped_ptr<uint8[]> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]);
477 gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight);
479 // We can't compare with the full-frame scaler because it uses slightly
480 // different sampling coordinates.
481 media::ScaleYUVToRGB32WithRect(
482 yuv_bytes.get(), // Y
483 yuv_bytes.get() + kSourceUOffset, // U
484 yuv_bytes.get() + kSourceVOffset, // V
485 rgb_scaled_bytes.get(), // Rgb output
486 kSourceWidth, kSourceHeight, // Dimensions
487 kDownScaledWidth, kDownScaledHeight, // Dimensions
488 sub_rect.x(), sub_rect.y(), // Dest rect
489 sub_rect.right(), sub_rect.bottom(), // Dest rect
490 kSourceWidth, // YStride
491 kSourceWidth / 2, // UvStride
492 kDownScaledWidth * kBpp); // RgbStride
494 uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(),
495 size_of_rgb_scaled,
496 kDJB2HashSeed);
498 // Re-scale sub-rectangles and verify the results are the same.
499 int next_sub_rect = 0;
500 while (!sub_rect.IsEmpty()) {
501 // Scale a partial rectangle.
502 media::ScaleYUVToRGB32WithRect(
503 yuv_bytes.get(), // Y
504 yuv_bytes.get() + kSourceUOffset, // U
505 yuv_bytes.get() + kSourceVOffset, // V
506 rgb_scaled_bytes.get(), // Rgb output
507 kSourceWidth, kSourceHeight, // Dimensions
508 kDownScaledWidth, kDownScaledHeight, // Dimensions
509 sub_rect.x(), sub_rect.y(), // Dest rect
510 sub_rect.right(), sub_rect.bottom(), // Dest rect
511 kSourceWidth, // YStride
512 kSourceWidth / 2, // UvStride
513 kDownScaledWidth * kBpp); // RgbStride
514 uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(),
515 size_of_rgb_scaled,
516 kDJB2HashSeed);
518 EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect);
520 // Now pick choose a quarter rect of this sub-rect.
521 if (next_sub_rect & 1)
522 sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2);
523 if (next_sub_rect & 2)
524 sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2);
525 sub_rect.set_width(sub_rect.width() / 2);
526 sub_rect.set_height(sub_rect.height() / 2);
527 next_sub_rect++;
531 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
532 TEST(YUVConvertTest, YUVAtoARGB_MMX_MatchReference) {
533 // Allocate all surfaces.
534 scoped_ptr<uint8[]> yuv_bytes;
535 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
536 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
537 scoped_ptr<uint8[]> rgb_converted_bytes_ref(new uint8[kRGBSizeConverted]);
539 // Read YUV reference data from file.
540 ReadYV12AData(&yuv_bytes);
542 // Convert a frame of YUV to 32 bit ARGB using both C and MMX versions.
543 media::ConvertYUVAToARGB_C(yuv_bytes.get(),
544 yuv_bytes.get() + kSourceUOffset,
545 yuv_bytes.get() + kSourceVOffset,
546 yuv_bytes.get() + kSourceAOffset,
547 rgb_converted_bytes_ref.get(),
548 kSourceWidth,
549 kSourceHeight,
550 kSourceWidth,
551 kSourceWidth / 2,
552 kSourceWidth,
553 kSourceWidth * kBpp,
554 media::YV12);
555 media::ConvertYUVAToARGB_MMX(yuv_bytes.get(),
556 yuv_bytes.get() + kSourceUOffset,
557 yuv_bytes.get() + kSourceVOffset,
558 yuv_bytes.get() + kSourceAOffset,
559 rgb_converted_bytes.get(),
560 kSourceWidth,
561 kSourceHeight,
562 kSourceWidth,
563 kSourceWidth / 2,
564 kSourceWidth,
565 kSourceWidth * kBpp,
566 media::YV12);
568 EXPECT_EQ(0,
569 memcmp(rgb_converted_bytes.get(),
570 rgb_converted_bytes_ref.get(),
571 kRGBSizeConverted));
574 TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) {
575 base::CPU cpu;
576 if (!cpu.has_sse2()) {
577 LOG(WARNING) << "System doesn't support SSE2, test not executed.";
578 return;
581 // Allocate all surfaces.
582 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
583 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
584 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
585 scoped_ptr<uint8[]> yuv_reference_bytes(new uint8[kYUV12Size]);
587 ReadYV12Data(&yuv_bytes);
589 // Convert a frame of YUV to 32 bit ARGB.
590 media::ConvertYUVToRGB32(
591 yuv_bytes.get(),
592 yuv_bytes.get() + kSourceUOffset,
593 yuv_bytes.get() + kSourceVOffset,
594 rgb_bytes.get(), // RGB output
595 kSourceWidth, kSourceHeight, // Dimensions
596 kSourceWidth, // YStride
597 kSourceWidth / 2, // UVStride
598 kSourceWidth * kBpp, // RGBStride
599 media::YV12);
601 // Convert RGB32 to YV12 with SSE2 version.
602 media::ConvertRGB32ToYUV_SSE2(
603 rgb_bytes.get(),
604 yuv_converted_bytes.get(),
605 yuv_converted_bytes.get() + kSourceUOffset,
606 yuv_converted_bytes.get() + kSourceVOffset,
607 kSourceWidth, kSourceHeight, // Dimensions
608 kSourceWidth * 4, // RGBStride
609 kSourceWidth, // YStride
610 kSourceWidth / 2); // UVStride
612 // Convert RGB32 to YV12 with reference version.
613 media::ConvertRGB32ToYUV_SSE2_Reference(
614 rgb_bytes.get(),
615 yuv_reference_bytes.get(),
616 yuv_reference_bytes.get() + kSourceUOffset,
617 yuv_reference_bytes.get() + kSourceVOffset,
618 kSourceWidth, kSourceHeight, // Dimensions
619 kSourceWidth * 4, // RGBStride
620 kSourceWidth, // YStride
621 kSourceWidth / 2); // UVStride
623 // Now convert a odd width and height, this overrides part of the buffer
624 // generated above but that is fine because the point of this test is to
625 // match the result with the reference code.
627 // Convert RGB32 to YV12 with SSE2 version.
628 media::ConvertRGB32ToYUV_SSE2(
629 rgb_bytes.get(),
630 yuv_converted_bytes.get(),
631 yuv_converted_bytes.get() + kSourceUOffset,
632 yuv_converted_bytes.get() + kSourceVOffset,
633 7, 7, // Dimensions
634 kSourceWidth * 4, // RGBStride
635 kSourceWidth, // YStride
636 kSourceWidth / 2); // UVStride
638 // Convert RGB32 to YV12 with reference version.
639 media::ConvertRGB32ToYUV_SSE2_Reference(
640 rgb_bytes.get(),
641 yuv_reference_bytes.get(),
642 yuv_reference_bytes.get() + kSourceUOffset,
643 yuv_reference_bytes.get() + kSourceVOffset,
644 7, 7, // Dimensions
645 kSourceWidth * 4, // RGBStride
646 kSourceWidth, // YStride
647 kSourceWidth / 2); // UVStride
649 int error = 0;
650 for (int i = 0; i < kYUV12Size; ++i) {
651 int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i];
652 if (diff < 0)
653 diff = -diff;
654 error += diff;
657 // Make sure there's no difference from the reference.
658 EXPECT_EQ(0, error);
661 TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) {
662 base::CPU cpu;
663 if (!cpu.has_sse()) {
664 LOG(WARNING) << "System not supported. Test skipped.";
665 return;
668 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
669 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
670 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
671 ReadYV12Data(&yuv_bytes);
673 const int kWidth = 167;
674 ConvertYUVToRGB32Row_C(yuv_bytes.get(),
675 yuv_bytes.get() + kSourceUOffset,
676 yuv_bytes.get() + kSourceVOffset,
677 rgb_bytes_reference.get(),
678 kWidth,
679 GetLookupTable(YV12));
680 ConvertYUVToRGB32Row_SSE(yuv_bytes.get(),
681 yuv_bytes.get() + kSourceUOffset,
682 yuv_bytes.get() + kSourceVOffset,
683 rgb_bytes_converted.get(),
684 kWidth,
685 GetLookupTable(YV12));
686 media::EmptyRegisterState();
687 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
688 rgb_bytes_converted.get(),
689 kWidth * kBpp));
692 // 64-bit release + component builds on Windows are too smart and optimizes
693 // away the function being tested.
694 #if defined(OS_WIN) && (defined(ARCH_CPU_X86) || !defined(COMPONENT_BUILD))
695 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) {
696 base::CPU cpu;
697 if (!cpu.has_sse()) {
698 LOG(WARNING) << "System not supported. Test skipped.";
699 return;
702 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
703 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
704 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
705 ReadYV12Data(&yuv_bytes);
707 const int kWidth = 167;
708 const int kSourceDx = 80000; // This value means a scale down.
709 ScaleYUVToRGB32Row_C(yuv_bytes.get(),
710 yuv_bytes.get() + kSourceUOffset,
711 yuv_bytes.get() + kSourceVOffset,
712 rgb_bytes_reference.get(),
713 kWidth,
714 kSourceDx,
715 GetLookupTable(YV12));
716 ScaleYUVToRGB32Row_SSE(yuv_bytes.get(),
717 yuv_bytes.get() + kSourceUOffset,
718 yuv_bytes.get() + kSourceVOffset,
719 rgb_bytes_converted.get(),
720 kWidth,
721 kSourceDx,
722 GetLookupTable(YV12));
723 media::EmptyRegisterState();
724 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
725 rgb_bytes_converted.get(),
726 kWidth * kBpp));
729 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) {
730 base::CPU cpu;
731 if (!cpu.has_sse()) {
732 LOG(WARNING) << "System not supported. Test skipped.";
733 return;
736 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
737 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
738 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
739 ReadYV12Data(&yuv_bytes);
741 const int kWidth = 167;
742 const int kSourceDx = 80000; // This value means a scale down.
743 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(),
744 yuv_bytes.get() + kSourceUOffset,
745 yuv_bytes.get() + kSourceVOffset,
746 rgb_bytes_reference.get(),
747 kWidth,
748 kSourceDx,
749 GetLookupTable(YV12));
750 LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(),
751 yuv_bytes.get() + kSourceUOffset,
752 yuv_bytes.get() + kSourceVOffset,
753 rgb_bytes_converted.get(),
754 kWidth,
755 kSourceDx,
756 GetLookupTable(YV12));
757 media::EmptyRegisterState();
758 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
759 rgb_bytes_converted.get(),
760 kWidth * kBpp));
762 #endif // defined(OS_WIN) && (ARCH_CPU_X86 || COMPONENT_BUILD)
764 TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) {
765 scoped_ptr<uint8[]> src(new uint8[16]);
766 scoped_ptr<uint8[]> dst(new uint8[16]);
768 memset(src.get(), 0xff, 16);
769 memset(dst.get(), 0, 16);
771 media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255);
773 EXPECT_EQ(255u, dst[0]);
774 for (int i = 1; i < 16; ++i) {
775 EXPECT_EQ(0u, dst[i]) << " not equal at " << i;
779 TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) {
780 base::CPU cpu;
781 if (!cpu.has_sse2()) {
782 LOG(WARNING) << "System not supported. Test skipped.";
783 return;
786 scoped_ptr<uint8[]> src(new uint8[16]);
787 scoped_ptr<uint8[]> dst(new uint8[16]);
789 memset(src.get(), 0xff, 16);
790 memset(dst.get(), 0, 16);
792 media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255);
794 EXPECT_EQ(255u, dst[0]);
795 for (int i = 1; i < 16; ++i) {
796 EXPECT_EQ(0u, dst[i]);
800 TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) {
801 base::CPU cpu;
802 if (!cpu.has_sse2()) {
803 LOG(WARNING) << "System not supported. Test skipped.";
804 return;
807 const int kSize = 64;
808 scoped_ptr<uint8[]> src(new uint8[kSize]);
809 scoped_ptr<uint8[]> dst_sample(new uint8[kSize]);
810 scoped_ptr<uint8[]> dst(new uint8[kSize]);
812 memset(dst_sample.get(), 0, kSize);
813 memset(dst.get(), 0, kSize);
814 for (int i = 0; i < kSize; ++i)
815 src[i] = 100 + i;
817 media::FilterYUVRows_C(dst_sample.get(),
818 src.get(), src.get(), 37, 128);
820 // Generate an unaligned output address.
821 uint8* dst_ptr =
822 reinterpret_cast<uint8*>(
823 (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1);
824 media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128);
825 media::EmptyRegisterState();
827 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37));
830 #if defined(ARCH_CPU_X86_64)
832 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) {
833 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
834 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
835 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
836 ReadYV12Data(&yuv_bytes);
838 const int kWidth = 167;
839 const int kSourceDx = 80000; // This value means a scale down.
840 ScaleYUVToRGB32Row_C(yuv_bytes.get(),
841 yuv_bytes.get() + kSourceUOffset,
842 yuv_bytes.get() + kSourceVOffset,
843 rgb_bytes_reference.get(),
844 kWidth,
845 kSourceDx,
846 GetLookupTable(YV12));
847 ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(),
848 yuv_bytes.get() + kSourceUOffset,
849 yuv_bytes.get() + kSourceVOffset,
850 rgb_bytes_converted.get(),
851 kWidth,
852 kSourceDx,
853 GetLookupTable(YV12));
854 media::EmptyRegisterState();
855 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
856 rgb_bytes_converted.get(),
857 kWidth * kBpp));
860 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) {
861 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
862 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
863 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
864 ReadYV12Data(&yuv_bytes);
866 const int kWidth = 167;
867 const int kSourceDx = 80000; // This value means a scale down.
868 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(),
869 yuv_bytes.get() + kSourceUOffset,
870 yuv_bytes.get() + kSourceVOffset,
871 rgb_bytes_reference.get(),
872 kWidth,
873 kSourceDx,
874 GetLookupTable(YV12));
875 LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(),
876 yuv_bytes.get() + kSourceUOffset,
877 yuv_bytes.get() + kSourceVOffset,
878 rgb_bytes_converted.get(),
879 kWidth,
880 kSourceDx,
881 GetLookupTable(YV12));
882 media::EmptyRegisterState();
883 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
884 rgb_bytes_converted.get(),
885 kWidth * kBpp));
888 #endif // defined(ARCH_CPU_X86_64)
890 #endif // defined(ARCH_CPU_X86_FAMILY)
892 } // namespace media