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"
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"
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;
44 // Helper for reading test data into a scoped_ptr<uint8[]>.
45 static void ReadData(const base::FilePath::CharType
* filename
,
47 scoped_ptr
<uint8
[]>* data
) {
48 data
->reset(new uint8
[expected_size
]);
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"))
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
);
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]);
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
121 #if defined(OS_ANDROID)
122 SwapRedAndBlueChannels(rgb_converted_bytes
.get(), kRGBSizeConverted
);
125 uint32 rgb_hash
= DJB2Hash(rgb_converted_bytes
.get(), kRGBSizeConverted
,
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
150 #if defined(OS_ANDROID)
151 SwapRedAndBlueChannels(rgb_converted_bytes
.get(), kRGBSizeConverted
);
154 uint32 rgb_hash
= DJB2Hash(rgb_converted_bytes
.get(), kRGBSizeConverted
,
156 EXPECT_EQ(4222342047u, rgb_hash
);
159 struct YUVScaleTestData
{
160 YUVScaleTestData(media::YUVType y
, media::ScaleFilter s
, uint32 r
)
166 media::YUVType yuv_type
;
167 media::ScaleFilter scale_filter
;
171 class YUVScaleTest
: public ::testing::TestWithParam
<YUVScaleTestData
> {
174 switch (GetParam().yuv_type
) {
178 ReadYV12Data(&yuv_bytes_
);
181 ReadYV16Data(&yuv_bytes_
);
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
; }
192 switch (GetParam().yuv_type
) {
196 return yuv_bytes_
.get() + kSourceVOffset
;
198 return yuv_bytes_
.get() + kSourceYSize
* 3 / 2;
203 scoped_ptr
<uint8
[]> yuv_bytes_
;
204 scoped_ptr
<uint8
[]> rgb_bytes_
;
207 TEST_P(YUVScaleTest
, NoScale
) {
208 media::ScaleYUVToRGB32(y_plane(), // Y
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
219 GetParam().scale_filter
);
221 uint32 yuv_hash
= DJB2Hash(rgb_bytes_
.get(), kRGBSize
, kDJB2HashSeed
);
223 media::ConvertYUVToRGB32(y_plane(), // Y
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
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
250 GetParam().scale_filter
);
252 #if defined(OS_ANDROID)
253 SwapRedAndBlueChannels(rgb_bytes_
.get(), kRGBSizeScaled
);
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
264 rgb_bytes_
.get(), // RGB output
266 kScaledWidth
, kScaledHeight
, // Dimensions
267 kSourceWidth
, // YStride
268 kSourceWidth
/ 2, // UvStride
269 kScaledWidth
* kBpp
, // RgbStride
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
281 rgb_bytes_
.get(), // RGB output
282 kSourceWidth
, kSourceHeight
, // Dimensions
284 kSourceWidth
, // YStride
285 kSourceWidth
/ 2, // UvStride
286 kScaledWidth
* kBpp
, // RgbStride
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
298 rgb_bytes_
.get(), // RGB output
299 kSourceWidth
, kSourceHeight
, // Dimensions
301 kSourceWidth
, // YStride
302 kSourceWidth
/ 2, // UvStride
303 kScaledWidth
* kBpp
, // RgbStride
306 GetParam().scale_filter
);
309 INSTANTIATE_TEST_CASE_P(
310 YUVScaleFormats
, YUVScaleTest
,
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
336 &rgb
[0], // RGB output
343 #if defined(OS_ANDROID)
344 SwapRedAndBlueChannels(rgb
, kBpp
);
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
);
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
,
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
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
426 for (int i
= 0; i
< kRGBSize
; ++i
) {
427 int diff
= rgb_converted_bytes
[i
] - rgb_bytes
[i
];
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
);
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
,
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(),
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(),
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);
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(),
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(),
569 memcmp(rgb_converted_bytes
.get(),
570 rgb_converted_bytes_ref
.get(),
574 TEST(YUVConvertTest
, RGB32ToYUV_SSE2_MatchReference
) {
576 if (!cpu
.has_sse2()) {
577 LOG(WARNING
) << "System doesn't support SSE2, test not executed.";
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(
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
601 // Convert RGB32 to YV12 with SSE2 version.
602 media::ConvertRGB32ToYUV_SSE2(
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(
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(
630 yuv_converted_bytes
.get(),
631 yuv_converted_bytes
.get() + kSourceUOffset
,
632 yuv_converted_bytes
.get() + kSourceVOffset
,
634 kSourceWidth
* 4, // RGBStride
635 kSourceWidth
, // YStride
636 kSourceWidth
/ 2); // UVStride
638 // Convert RGB32 to YV12 with reference version.
639 media::ConvertRGB32ToYUV_SSE2_Reference(
641 yuv_reference_bytes
.get(),
642 yuv_reference_bytes
.get() + kSourceUOffset
,
643 yuv_reference_bytes
.get() + kSourceVOffset
,
645 kSourceWidth
* 4, // RGBStride
646 kSourceWidth
, // YStride
647 kSourceWidth
/ 2); // UVStride
650 for (int i
= 0; i
< kYUV12Size
; ++i
) {
651 int diff
= yuv_reference_bytes
[i
] - yuv_converted_bytes
[i
];
657 // Make sure there's no difference from the reference.
661 TEST(YUVConvertTest
, ConvertYUVToRGB32Row_SSE
) {
663 if (!cpu
.has_sse()) {
664 LOG(WARNING
) << "System not supported. Test skipped.";
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(),
679 GetLookupTable(YV12
));
680 ConvertYUVToRGB32Row_SSE(yuv_bytes
.get(),
681 yuv_bytes
.get() + kSourceUOffset
,
682 yuv_bytes
.get() + kSourceVOffset
,
683 rgb_bytes_converted
.get(),
685 GetLookupTable(YV12
));
686 media::EmptyRegisterState();
687 EXPECT_EQ(0, memcmp(rgb_bytes_reference
.get(),
688 rgb_bytes_converted
.get(),
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
) {
697 if (!cpu
.has_sse()) {
698 LOG(WARNING
) << "System not supported. Test skipped.";
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(),
715 GetLookupTable(YV12
));
716 ScaleYUVToRGB32Row_SSE(yuv_bytes
.get(),
717 yuv_bytes
.get() + kSourceUOffset
,
718 yuv_bytes
.get() + kSourceVOffset
,
719 rgb_bytes_converted
.get(),
722 GetLookupTable(YV12
));
723 media::EmptyRegisterState();
724 EXPECT_EQ(0, memcmp(rgb_bytes_reference
.get(),
725 rgb_bytes_converted
.get(),
729 TEST(YUVConvertTest
, LinearScaleYUVToRGB32Row_SSE
) {
731 if (!cpu
.has_sse()) {
732 LOG(WARNING
) << "System not supported. Test skipped.";
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(),
749 GetLookupTable(YV12
));
750 LinearScaleYUVToRGB32Row_SSE(yuv_bytes
.get(),
751 yuv_bytes
.get() + kSourceUOffset
,
752 yuv_bytes
.get() + kSourceVOffset
,
753 rgb_bytes_converted
.get(),
756 GetLookupTable(YV12
));
757 media::EmptyRegisterState();
758 EXPECT_EQ(0, memcmp(rgb_bytes_reference
.get(),
759 rgb_bytes_converted
.get(),
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
) {
781 if (!cpu
.has_sse2()) {
782 LOG(WARNING
) << "System not supported. Test skipped.";
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
) {
802 if (!cpu
.has_sse2()) {
803 LOG(WARNING
) << "System not supported. Test skipped.";
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
)
817 media::FilterYUVRows_C(dst_sample
.get(),
818 src
.get(), src
.get(), 37, 128);
820 // Generate an unaligned output address.
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(),
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(),
853 GetLookupTable(YV12
));
854 media::EmptyRegisterState();
855 EXPECT_EQ(0, memcmp(rgb_bytes_reference
.get(),
856 rgb_bytes_converted
.get(),
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(),
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(),
881 GetLookupTable(YV12
));
882 media::EmptyRegisterState();
883 EXPECT_EQ(0, memcmp(rgb_bytes_reference
.get(),
884 rgb_bytes_converted
.get(),
888 #endif // defined(ARCH_CPU_X86_64)
890 #endif // defined(ARCH_CPU_X86_FAMILY)