Change blimp README to use Markdown.
[chromium-blink-merge.git] / media / base / yuv_convert_unittest.cc
blobfff56b98ff401f4570a49901384fa4cc293b96e3
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 !defined(OS_ANDROID)
78 static void ReadYV12AData(scoped_ptr<uint8[]>* data) {
79 ReadData(FILE_PATH_LITERAL("bali_640x360_P420_alpha.yuv"), kYUVA12Size, data);
81 #endif
83 static void ReadRGB24Data(scoped_ptr<uint8[]>* data) {
84 ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data);
87 static void ReadYUY2Data(scoped_ptr<uint8[]>* data) {
88 ReadData(FILE_PATH_LITERAL("bali_640x360_YUY2.yuv"), kYUY2Size, data);
91 #if defined(OS_ANDROID)
92 // Helper for swapping red and blue channels of RGBA or BGRA.
93 static void SwapRedAndBlueChannels(unsigned char* pixels, size_t buffer_size) {
94 for (size_t i = 0; i < buffer_size; i += 4) {
95 std::swap(pixels[i], pixels[i + 2]);
98 #endif
100 namespace media {
102 TEST(YUVConvertTest, YV12) {
103 // Allocate all surfaces.
104 scoped_ptr<uint8[]> yuv_bytes;
105 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
106 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
108 // Read YUV reference data from file.
109 ReadYV12Data(&yuv_bytes);
111 // Convert a frame of YUV to 32 bit ARGB.
112 media::ConvertYUVToRGB32(yuv_bytes.get(),
113 yuv_bytes.get() + kSourceUOffset,
114 yuv_bytes.get() + kSourceVOffset,
115 rgb_converted_bytes.get(), // RGB output
116 kSourceWidth, kSourceHeight, // Dimensions
117 kSourceWidth, // YStride
118 kSourceWidth / 2, // UVStride
119 kSourceWidth * kBpp, // RGBStride
120 media::YV12);
122 #if defined(OS_ANDROID)
123 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted);
124 #endif
126 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted,
127 kDJB2HashSeed);
128 EXPECT_EQ(2413171226u, rgb_hash);
131 TEST(YUVConvertTest, YV16) {
132 // Allocate all surfaces.
133 scoped_ptr<uint8[]> yuv_bytes;
134 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
135 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
137 // Read YUV reference data from file.
138 ReadYV16Data(&yuv_bytes);
140 // Convert a frame of YUV to 32 bit ARGB.
141 media::ConvertYUVToRGB32(yuv_bytes.get(), // Y
142 yuv_bytes.get() + kSourceUOffset, // U
143 yuv_bytes.get() + kSourceYSize * 3 / 2, // V
144 rgb_converted_bytes.get(), // RGB output
145 kSourceWidth, kSourceHeight, // Dimensions
146 kSourceWidth, // YStride
147 kSourceWidth / 2, // UVStride
148 kSourceWidth * kBpp, // RGBStride
149 media::YV16);
151 #if defined(OS_ANDROID)
152 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted);
153 #endif
155 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted,
156 kDJB2HashSeed);
157 EXPECT_EQ(4222342047u, rgb_hash);
160 struct YUVScaleTestData {
161 YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32 r)
162 : yuv_type(y),
163 scale_filter(s),
164 rgb_hash(r) {
167 media::YUVType yuv_type;
168 media::ScaleFilter scale_filter;
169 uint32 rgb_hash;
172 class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> {
173 public:
174 YUVScaleTest() {
175 switch (GetParam().yuv_type) {
176 case media::YV12:
177 case media::YV12J:
178 case media::YV12HD:
179 ReadYV12Data(&yuv_bytes_);
180 break;
181 case media::YV16:
182 ReadYV16Data(&yuv_bytes_);
183 break;
186 rgb_bytes_.reset(new uint8[kRGBSizeScaled]);
189 // Helpers for getting the proper Y, U and V plane offsets.
190 uint8* y_plane() { return yuv_bytes_.get(); }
191 uint8* u_plane() { return yuv_bytes_.get() + kSourceYSize; }
192 uint8* v_plane() {
193 switch (GetParam().yuv_type) {
194 case media::YV12:
195 case media::YV12J:
196 case media::YV12HD:
197 return yuv_bytes_.get() + kSourceVOffset;
198 case media::YV16:
199 return yuv_bytes_.get() + kSourceYSize * 3 / 2;
201 return NULL;
204 scoped_ptr<uint8[]> yuv_bytes_;
205 scoped_ptr<uint8[]> rgb_bytes_;
208 TEST_P(YUVScaleTest, NoScale) {
209 media::ScaleYUVToRGB32(y_plane(), // Y
210 u_plane(), // U
211 v_plane(), // V
212 rgb_bytes_.get(), // RGB output
213 kSourceWidth, kSourceHeight, // Dimensions
214 kSourceWidth, kSourceHeight, // Dimensions
215 kSourceWidth, // YStride
216 kSourceWidth / 2, // UvStride
217 kSourceWidth * kBpp, // RgbStride
218 GetParam().yuv_type,
219 media::ROTATE_0,
220 GetParam().scale_filter);
222 uint32 yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed);
224 media::ConvertYUVToRGB32(y_plane(), // Y
225 u_plane(), // U
226 v_plane(), // V
227 rgb_bytes_.get(), // RGB output
228 kSourceWidth, kSourceHeight, // Dimensions
229 kSourceWidth, // YStride
230 kSourceWidth / 2, // UVStride
231 kSourceWidth * kBpp, // RGBStride
232 GetParam().yuv_type);
234 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed);
236 EXPECT_EQ(yuv_hash, rgb_hash);
239 TEST_P(YUVScaleTest, Normal) {
240 media::ScaleYUVToRGB32(y_plane(), // Y
241 u_plane(), // U
242 v_plane(), // V
243 rgb_bytes_.get(), // RGB output
244 kSourceWidth, kSourceHeight, // Dimensions
245 kScaledWidth, kScaledHeight, // Dimensions
246 kSourceWidth, // YStride
247 kSourceWidth / 2, // UvStride
248 kScaledWidth * kBpp, // RgbStride
249 GetParam().yuv_type,
250 media::ROTATE_0,
251 GetParam().scale_filter);
253 #if defined(OS_ANDROID)
254 SwapRedAndBlueChannels(rgb_bytes_.get(), kRGBSizeScaled);
255 #endif
257 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed);
258 EXPECT_EQ(GetParam().rgb_hash, rgb_hash);
261 TEST_P(YUVScaleTest, ZeroSourceSize) {
262 media::ScaleYUVToRGB32(y_plane(), // Y
263 u_plane(), // U
264 v_plane(), // V
265 rgb_bytes_.get(), // RGB output
266 0, 0, // Dimensions
267 kScaledWidth, kScaledHeight, // Dimensions
268 kSourceWidth, // YStride
269 kSourceWidth / 2, // UvStride
270 kScaledWidth * kBpp, // RgbStride
271 GetParam().yuv_type,
272 media::ROTATE_0,
273 GetParam().scale_filter);
275 // Testing for out-of-bound read/writes with AddressSanitizer.
278 TEST_P(YUVScaleTest, ZeroDestinationSize) {
279 media::ScaleYUVToRGB32(y_plane(), // Y
280 u_plane(), // U
281 v_plane(), // V
282 rgb_bytes_.get(), // RGB output
283 kSourceWidth, kSourceHeight, // Dimensions
284 0, 0, // Dimensions
285 kSourceWidth, // YStride
286 kSourceWidth / 2, // UvStride
287 kScaledWidth * kBpp, // RgbStride
288 GetParam().yuv_type,
289 media::ROTATE_0,
290 GetParam().scale_filter);
292 // Testing for out-of-bound read/writes with AddressSanitizer.
295 TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) {
296 media::ScaleYUVToRGB32(y_plane(), // Y
297 u_plane(), // U
298 v_plane(), // V
299 rgb_bytes_.get(), // RGB output
300 kSourceWidth, kSourceHeight, // Dimensions
301 3, 3, // Dimensions
302 kSourceWidth, // YStride
303 kSourceWidth / 2, // UvStride
304 kScaledWidth * kBpp, // RgbStride
305 GetParam().yuv_type,
306 media::ROTATE_0,
307 GetParam().scale_filter);
310 INSTANTIATE_TEST_CASE_P(
311 YUVScaleFormats, YUVScaleTest,
312 ::testing::Values(
313 YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u),
314 YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u),
315 YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u),
316 YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u)));
318 // This tests a known worst case YUV value, and for overflow.
319 TEST(YUVConvertTest, Clamp) {
320 // Allocate all surfaces.
321 scoped_ptr<uint8[]> yuv_bytes(new uint8[1]);
322 scoped_ptr<uint8[]> rgb_bytes(new uint8[1]);
323 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[1]);
325 // Values that failed previously in bug report.
326 unsigned char y = 255u;
327 unsigned char u = 255u;
328 unsigned char v = 19u;
330 // Prefill extra large destination buffer to test for overflow.
331 unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
332 unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 };
333 // Convert a frame of YUV to 32 bit ARGB.
334 media::ConvertYUVToRGB32(&y, // Y
335 &u, // U
336 &v, // V
337 &rgb[0], // RGB output
338 1, 1, // Dimensions
339 0, // YStride
340 0, // UVStride
341 0, // RGBStride
342 media::YV12);
344 #if defined(OS_ANDROID)
345 SwapRedAndBlueChannels(rgb, kBpp);
346 #endif
348 int expected_test = memcmp(rgb, expected, sizeof(expected));
349 EXPECT_EQ(0, expected_test);
352 TEST(YUVConvertTest, RGB24ToYUV) {
353 // Allocate all surfaces.
354 scoped_ptr<uint8[]> rgb_bytes;
355 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
357 // Read RGB24 reference data from file.
358 ReadRGB24Data(&rgb_bytes);
360 // Convert to I420.
361 media::ConvertRGB24ToYUV(rgb_bytes.get(),
362 yuv_converted_bytes.get(),
363 yuv_converted_bytes.get() + kSourceUOffset,
364 yuv_converted_bytes.get() + kSourceVOffset,
365 kSourceWidth, kSourceHeight, // Dimensions
366 kSourceWidth * 3, // RGBStride
367 kSourceWidth, // YStride
368 kSourceWidth / 2); // UVStride
370 uint32 rgb_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size,
371 kDJB2HashSeed);
372 EXPECT_EQ(320824432u, rgb_hash);
375 TEST(YUVConvertTest, RGB32ToYUV) {
376 // Allocate all surfaces.
377 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
378 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
379 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
380 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSize]);
382 // Read YUV reference data from file.
383 base::FilePath yuv_url;
384 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url));
385 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media"))
386 .Append(FILE_PATH_LITERAL("test"))
387 .Append(FILE_PATH_LITERAL("data"))
388 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv"));
389 EXPECT_EQ(static_cast<int>(kYUV12Size),
390 base::ReadFile(yuv_url,
391 reinterpret_cast<char*>(yuv_bytes.get()),
392 static_cast<int>(kYUV12Size)));
394 // Convert a frame of YUV to 32 bit ARGB.
395 media::ConvertYUVToRGB32(yuv_bytes.get(),
396 yuv_bytes.get() + kSourceUOffset,
397 yuv_bytes.get() + kSourceVOffset,
398 rgb_bytes.get(), // RGB output
399 kSourceWidth, kSourceHeight, // Dimensions
400 kSourceWidth, // YStride
401 kSourceWidth / 2, // UVStride
402 kSourceWidth * kBpp, // RGBStride
403 media::YV12);
405 // Convert RGB32 to YV12.
406 media::ConvertRGB32ToYUV(rgb_bytes.get(),
407 yuv_converted_bytes.get(),
408 yuv_converted_bytes.get() + kSourceUOffset,
409 yuv_converted_bytes.get() + kSourceVOffset,
410 kSourceWidth, kSourceHeight, // Dimensions
411 kSourceWidth * 4, // RGBStride
412 kSourceWidth, // YStride
413 kSourceWidth / 2); // UVStride
415 // Convert YV12 back to RGB32.
416 media::ConvertYUVToRGB32(yuv_converted_bytes.get(),
417 yuv_converted_bytes.get() + kSourceUOffset,
418 yuv_converted_bytes.get() + kSourceVOffset,
419 rgb_converted_bytes.get(), // RGB output
420 kSourceWidth, kSourceHeight, // Dimensions
421 kSourceWidth, // YStride
422 kSourceWidth / 2, // UVStride
423 kSourceWidth * kBpp, // RGBStride
424 media::YV12);
426 int error = 0;
427 for (int i = 0; i < kRGBSize; ++i) {
428 int diff = rgb_converted_bytes[i] - rgb_bytes[i];
429 if (diff < 0)
430 diff = -diff;
431 error += diff;
434 // Make sure error is within bound.
435 DVLOG(1) << "Average error per channel: " << error / kRGBSize;
436 EXPECT_GT(5, error / kRGBSize);
439 TEST(YUVConvertTest, YUY2ToYUV) {
440 // Allocate all surfaces.
441 scoped_ptr<uint8[]> yuy_bytes;
442 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
444 // Read YUY reference data from file.
445 ReadYUY2Data(&yuy_bytes);
447 // Convert to I420.
448 media::ConvertYUY2ToYUV(yuy_bytes.get(),
449 yuv_converted_bytes.get(),
450 yuv_converted_bytes.get() + kSourceUOffset,
451 yuv_converted_bytes.get() + kSourceVOffset,
452 kSourceWidth, kSourceHeight);
454 uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size,
455 kDJB2HashSeed);
456 EXPECT_EQ(666823187u, yuy_hash);
459 TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) {
460 // Read YUV reference data from file.
461 base::FilePath yuv_url;
462 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url));
463 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media"))
464 .Append(FILE_PATH_LITERAL("test"))
465 .Append(FILE_PATH_LITERAL("data"))
466 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv"));
467 const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp.
468 scoped_ptr<uint8[]> yuv_bytes(new uint8[size_of_yuv]);
469 EXPECT_EQ(static_cast<int>(size_of_yuv),
470 base::ReadFile(yuv_url,
471 reinterpret_cast<char*>(yuv_bytes.get()),
472 static_cast<int>(size_of_yuv)));
474 // Scale the full frame of YUV to 32 bit ARGB.
475 // The API currently only supports down-scaling, so we don't test up-scaling.
476 const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp;
477 scoped_ptr<uint8[]> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]);
478 gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight);
480 // We can't compare with the full-frame scaler because it uses slightly
481 // different sampling coordinates.
482 media::ScaleYUVToRGB32WithRect(
483 yuv_bytes.get(), // Y
484 yuv_bytes.get() + kSourceUOffset, // U
485 yuv_bytes.get() + kSourceVOffset, // V
486 rgb_scaled_bytes.get(), // Rgb output
487 kSourceWidth, kSourceHeight, // Dimensions
488 kDownScaledWidth, kDownScaledHeight, // Dimensions
489 sub_rect.x(), sub_rect.y(), // Dest rect
490 sub_rect.right(), sub_rect.bottom(), // Dest rect
491 kSourceWidth, // YStride
492 kSourceWidth / 2, // UvStride
493 kDownScaledWidth * kBpp); // RgbStride
495 uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(),
496 size_of_rgb_scaled,
497 kDJB2HashSeed);
499 // Re-scale sub-rectangles and verify the results are the same.
500 int next_sub_rect = 0;
501 while (!sub_rect.IsEmpty()) {
502 // Scale a partial rectangle.
503 media::ScaleYUVToRGB32WithRect(
504 yuv_bytes.get(), // Y
505 yuv_bytes.get() + kSourceUOffset, // U
506 yuv_bytes.get() + kSourceVOffset, // V
507 rgb_scaled_bytes.get(), // Rgb output
508 kSourceWidth, kSourceHeight, // Dimensions
509 kDownScaledWidth, kDownScaledHeight, // Dimensions
510 sub_rect.x(), sub_rect.y(), // Dest rect
511 sub_rect.right(), sub_rect.bottom(), // Dest rect
512 kSourceWidth, // YStride
513 kSourceWidth / 2, // UvStride
514 kDownScaledWidth * kBpp); // RgbStride
515 uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(),
516 size_of_rgb_scaled,
517 kDJB2HashSeed);
519 EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect);
521 // Now pick choose a quarter rect of this sub-rect.
522 if (next_sub_rect & 1)
523 sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2);
524 if (next_sub_rect & 2)
525 sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2);
526 sub_rect.set_width(sub_rect.width() / 2);
527 sub_rect.set_height(sub_rect.height() / 2);
528 next_sub_rect++;
532 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY)
533 #if !defined(OS_ANDROID)
534 TEST(YUVConvertTest, YUVAtoARGB_MMX_MatchReference) {
535 // Allocate all surfaces.
536 scoped_ptr<uint8[]> yuv_bytes;
537 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
538 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]);
539 scoped_ptr<uint8[]> rgb_converted_bytes_ref(new uint8[kRGBSizeConverted]);
541 // Read YUV reference data from file.
542 ReadYV12AData(&yuv_bytes);
544 // Convert a frame of YUV to 32 bit ARGB using both C and MMX versions.
545 media::ConvertYUVAToARGB_C(yuv_bytes.get(),
546 yuv_bytes.get() + kSourceUOffset,
547 yuv_bytes.get() + kSourceVOffset,
548 yuv_bytes.get() + kSourceAOffset,
549 rgb_converted_bytes_ref.get(),
550 kSourceWidth,
551 kSourceHeight,
552 kSourceWidth,
553 kSourceWidth / 2,
554 kSourceWidth,
555 kSourceWidth * kBpp,
556 media::YV12);
557 media::ConvertYUVAToARGB_MMX(yuv_bytes.get(),
558 yuv_bytes.get() + kSourceUOffset,
559 yuv_bytes.get() + kSourceVOffset,
560 yuv_bytes.get() + kSourceAOffset,
561 rgb_converted_bytes.get(),
562 kSourceWidth,
563 kSourceHeight,
564 kSourceWidth,
565 kSourceWidth / 2,
566 kSourceWidth,
567 kSourceWidth * kBpp,
568 media::YV12);
570 EXPECT_EQ(0,
571 memcmp(rgb_converted_bytes.get(),
572 rgb_converted_bytes_ref.get(),
573 kRGBSizeConverted));
575 #endif // !defined(OS_ANDROID)
577 TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) {
578 base::CPU cpu;
579 if (!cpu.has_sse2()) {
580 LOG(WARNING) << "System doesn't support SSE2, test not executed.";
581 return;
584 // Allocate all surfaces.
585 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
586 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]);
587 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]);
588 scoped_ptr<uint8[]> yuv_reference_bytes(new uint8[kYUV12Size]);
590 ReadYV12Data(&yuv_bytes);
592 // Convert a frame of YUV to 32 bit ARGB.
593 media::ConvertYUVToRGB32(
594 yuv_bytes.get(),
595 yuv_bytes.get() + kSourceUOffset,
596 yuv_bytes.get() + kSourceVOffset,
597 rgb_bytes.get(), // RGB output
598 kSourceWidth, kSourceHeight, // Dimensions
599 kSourceWidth, // YStride
600 kSourceWidth / 2, // UVStride
601 kSourceWidth * kBpp, // RGBStride
602 media::YV12);
604 // Convert RGB32 to YV12 with SSE2 version.
605 media::ConvertRGB32ToYUV_SSE2(
606 rgb_bytes.get(),
607 yuv_converted_bytes.get(),
608 yuv_converted_bytes.get() + kSourceUOffset,
609 yuv_converted_bytes.get() + kSourceVOffset,
610 kSourceWidth, kSourceHeight, // Dimensions
611 kSourceWidth * 4, // RGBStride
612 kSourceWidth, // YStride
613 kSourceWidth / 2); // UVStride
615 // Convert RGB32 to YV12 with reference version.
616 media::ConvertRGB32ToYUV_SSE2_Reference(
617 rgb_bytes.get(),
618 yuv_reference_bytes.get(),
619 yuv_reference_bytes.get() + kSourceUOffset,
620 yuv_reference_bytes.get() + kSourceVOffset,
621 kSourceWidth, kSourceHeight, // Dimensions
622 kSourceWidth * 4, // RGBStride
623 kSourceWidth, // YStride
624 kSourceWidth / 2); // UVStride
626 // Now convert a odd width and height, this overrides part of the buffer
627 // generated above but that is fine because the point of this test is to
628 // match the result with the reference code.
630 // Convert RGB32 to YV12 with SSE2 version.
631 media::ConvertRGB32ToYUV_SSE2(
632 rgb_bytes.get(),
633 yuv_converted_bytes.get(),
634 yuv_converted_bytes.get() + kSourceUOffset,
635 yuv_converted_bytes.get() + kSourceVOffset,
636 7, 7, // Dimensions
637 kSourceWidth * 4, // RGBStride
638 kSourceWidth, // YStride
639 kSourceWidth / 2); // UVStride
641 // Convert RGB32 to YV12 with reference version.
642 media::ConvertRGB32ToYUV_SSE2_Reference(
643 rgb_bytes.get(),
644 yuv_reference_bytes.get(),
645 yuv_reference_bytes.get() + kSourceUOffset,
646 yuv_reference_bytes.get() + kSourceVOffset,
647 7, 7, // Dimensions
648 kSourceWidth * 4, // RGBStride
649 kSourceWidth, // YStride
650 kSourceWidth / 2); // UVStride
652 int error = 0;
653 for (int i = 0; i < kYUV12Size; ++i) {
654 int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i];
655 if (diff < 0)
656 diff = -diff;
657 error += diff;
660 // Make sure there's no difference from the reference.
661 EXPECT_EQ(0, error);
664 TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) {
665 base::CPU cpu;
666 if (!cpu.has_sse()) {
667 LOG(WARNING) << "System not supported. Test skipped.";
668 return;
671 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
672 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
673 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
674 ReadYV12Data(&yuv_bytes);
676 const int kWidth = 167;
677 ConvertYUVToRGB32Row_C(yuv_bytes.get(),
678 yuv_bytes.get() + kSourceUOffset,
679 yuv_bytes.get() + kSourceVOffset,
680 rgb_bytes_reference.get(),
681 kWidth,
682 GetLookupTable(YV12));
683 ConvertYUVToRGB32Row_SSE(yuv_bytes.get(),
684 yuv_bytes.get() + kSourceUOffset,
685 yuv_bytes.get() + kSourceVOffset,
686 rgb_bytes_converted.get(),
687 kWidth,
688 GetLookupTable(YV12));
689 media::EmptyRegisterState();
690 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
691 rgb_bytes_converted.get(),
692 kWidth * kBpp));
695 // 64-bit release + component builds on Windows are too smart and optimizes
696 // away the function being tested.
697 #if defined(OS_WIN) && (defined(ARCH_CPU_X86) || !defined(COMPONENT_BUILD))
698 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) {
699 base::CPU cpu;
700 if (!cpu.has_sse()) {
701 LOG(WARNING) << "System not supported. Test skipped.";
702 return;
705 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
706 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
707 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
708 ReadYV12Data(&yuv_bytes);
710 const int kWidth = 167;
711 const int kSourceDx = 80000; // This value means a scale down.
712 ScaleYUVToRGB32Row_C(yuv_bytes.get(),
713 yuv_bytes.get() + kSourceUOffset,
714 yuv_bytes.get() + kSourceVOffset,
715 rgb_bytes_reference.get(),
716 kWidth,
717 kSourceDx,
718 GetLookupTable(YV12));
719 ScaleYUVToRGB32Row_SSE(yuv_bytes.get(),
720 yuv_bytes.get() + kSourceUOffset,
721 yuv_bytes.get() + kSourceVOffset,
722 rgb_bytes_converted.get(),
723 kWidth,
724 kSourceDx,
725 GetLookupTable(YV12));
726 media::EmptyRegisterState();
727 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
728 rgb_bytes_converted.get(),
729 kWidth * kBpp));
732 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) {
733 base::CPU cpu;
734 if (!cpu.has_sse()) {
735 LOG(WARNING) << "System not supported. Test skipped.";
736 return;
739 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
740 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
741 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
742 ReadYV12Data(&yuv_bytes);
744 const int kWidth = 167;
745 const int kSourceDx = 80000; // This value means a scale down.
746 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(),
747 yuv_bytes.get() + kSourceUOffset,
748 yuv_bytes.get() + kSourceVOffset,
749 rgb_bytes_reference.get(),
750 kWidth,
751 kSourceDx,
752 GetLookupTable(YV12));
753 LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(),
754 yuv_bytes.get() + kSourceUOffset,
755 yuv_bytes.get() + kSourceVOffset,
756 rgb_bytes_converted.get(),
757 kWidth,
758 kSourceDx,
759 GetLookupTable(YV12));
760 media::EmptyRegisterState();
761 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
762 rgb_bytes_converted.get(),
763 kWidth * kBpp));
765 #endif // defined(OS_WIN) && (ARCH_CPU_X86 || COMPONENT_BUILD)
767 TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) {
768 scoped_ptr<uint8[]> src(new uint8[16]);
769 scoped_ptr<uint8[]> dst(new uint8[16]);
771 memset(src.get(), 0xff, 16);
772 memset(dst.get(), 0, 16);
774 media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255);
776 EXPECT_EQ(255u, dst[0]);
777 for (int i = 1; i < 16; ++i) {
778 EXPECT_EQ(0u, dst[i]) << " not equal at " << i;
782 TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) {
783 base::CPU cpu;
784 if (!cpu.has_sse2()) {
785 LOG(WARNING) << "System not supported. Test skipped.";
786 return;
789 scoped_ptr<uint8[]> src(new uint8[16]);
790 scoped_ptr<uint8[]> dst(new uint8[16]);
792 memset(src.get(), 0xff, 16);
793 memset(dst.get(), 0, 16);
795 media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255);
797 EXPECT_EQ(255u, dst[0]);
798 for (int i = 1; i < 16; ++i) {
799 EXPECT_EQ(0u, dst[i]);
803 TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) {
804 base::CPU cpu;
805 if (!cpu.has_sse2()) {
806 LOG(WARNING) << "System not supported. Test skipped.";
807 return;
810 const int kSize = 64;
811 scoped_ptr<uint8[]> src(new uint8[kSize]);
812 scoped_ptr<uint8[]> dst_sample(new uint8[kSize]);
813 scoped_ptr<uint8[]> dst(new uint8[kSize]);
815 memset(dst_sample.get(), 0, kSize);
816 memset(dst.get(), 0, kSize);
817 for (int i = 0; i < kSize; ++i)
818 src[i] = 100 + i;
820 media::FilterYUVRows_C(dst_sample.get(),
821 src.get(), src.get(), 37, 128);
823 // Generate an unaligned output address.
824 uint8* dst_ptr =
825 reinterpret_cast<uint8*>(
826 (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1);
827 media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128);
828 media::EmptyRegisterState();
830 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37));
833 #if defined(ARCH_CPU_X86_64)
835 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) {
836 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
837 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
838 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
839 ReadYV12Data(&yuv_bytes);
841 const int kWidth = 167;
842 const int kSourceDx = 80000; // This value means a scale down.
843 ScaleYUVToRGB32Row_C(yuv_bytes.get(),
844 yuv_bytes.get() + kSourceUOffset,
845 yuv_bytes.get() + kSourceVOffset,
846 rgb_bytes_reference.get(),
847 kWidth,
848 kSourceDx,
849 GetLookupTable(YV12));
850 ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(),
851 yuv_bytes.get() + kSourceUOffset,
852 yuv_bytes.get() + kSourceVOffset,
853 rgb_bytes_converted.get(),
854 kWidth,
855 kSourceDx,
856 GetLookupTable(YV12));
857 media::EmptyRegisterState();
858 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
859 rgb_bytes_converted.get(),
860 kWidth * kBpp));
863 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) {
864 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]);
865 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]);
866 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]);
867 ReadYV12Data(&yuv_bytes);
869 const int kWidth = 167;
870 const int kSourceDx = 80000; // This value means a scale down.
871 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(),
872 yuv_bytes.get() + kSourceUOffset,
873 yuv_bytes.get() + kSourceVOffset,
874 rgb_bytes_reference.get(),
875 kWidth,
876 kSourceDx,
877 GetLookupTable(YV12));
878 LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(),
879 yuv_bytes.get() + kSourceUOffset,
880 yuv_bytes.get() + kSourceVOffset,
881 rgb_bytes_converted.get(),
882 kWidth,
883 kSourceDx,
884 GetLookupTable(YV12));
885 media::EmptyRegisterState();
886 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(),
887 rgb_bytes_converted.get(),
888 kWidth * kBpp));
891 #endif // defined(ARCH_CPU_X86_64)
893 #endif // defined(ARCH_CPU_X86_FAMILY)
895 } // namespace media