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. 4 5 #include "base/base_paths.h" 6 #include "base/cpu.h" 7 #include "base/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/rect.h" 17 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; 29 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; 38 39 // Helper for reading test data into a scoped_ptr<uint8[]>. 40 static void ReadData(const base::FilePath::CharType* filename, 41 int expected_size, 42 scoped_ptr<uint8[]>* data) { 43 data->reset(new uint8[expected_size]); 44 45 base::FilePath path; 46 CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &path)); 47 path = path.Append(FILE_PATH_LITERAL("media")) 48 .Append(FILE_PATH_LITERAL("test")) 49 .Append(FILE_PATH_LITERAL("data")) 50 .Append(filename); 51 52 // Verify file size is correct. 53 int64 actual_size = 0; 54 base::GetFileSize(path, &actual_size); 55 CHECK_EQ(actual_size, expected_size); 56 57 // Verify bytes read are correct. 58 int bytes_read = base::ReadFile( 59 path, reinterpret_cast<char*>(data->get()), expected_size); 60 CHECK_EQ(bytes_read, expected_size); 61 } 62 63 static void ReadYV12Data(scoped_ptr<uint8[]>* data) { 64 ReadData(FILE_PATH_LITERAL("bali_640x360_P420.yuv"), kYUV12Size, data); 65 } 66 67 static void ReadYV16Data(scoped_ptr<uint8[]>* data) { 68 ReadData(FILE_PATH_LITERAL("bali_640x360_P422.yuv"), kYUV16Size, data); 69 } 70 71 static void ReadRGB24Data(scoped_ptr<uint8[]>* data) { 72 ReadData(FILE_PATH_LITERAL("bali_640x360_RGB24.rgb"), kRGB24Size, data); 73 } 74 75 static void ReadYUY2Data(scoped_ptr<uint8[]>* data) { 76 ReadData(FILE_PATH_LITERAL("bali_640x360_YUY2.yuv"), kYUY2Size, data); 77 } 78 79 #if defined(OS_ANDROID) 80 // Helper for swapping red and blue channels of RGBA or BGRA. 81 static void SwapRedAndBlueChannels(unsigned char* pixels, size_t buffer_size) { 82 for (size_t i = 0; i < buffer_size; i += 4) { 83 std::swap(pixels[i], pixels[i + 2]); 84 } 85 } 86 #endif 87 88 namespace media { 89 90 TEST(YUVConvertTest, YV12) { 91 // Allocate all surfaces. 92 scoped_ptr<uint8[]> yuv_bytes; 93 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 94 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]); 95 96 // Read YUV reference data from file. 97 ReadYV12Data(&yuv_bytes); 98 99 // Convert a frame of YUV to 32 bit ARGB. 100 media::ConvertYUVToRGB32(yuv_bytes.get(), 101 yuv_bytes.get() + kSourceUOffset, 102 yuv_bytes.get() + kSourceVOffset, 103 rgb_converted_bytes.get(), // RGB output 104 kSourceWidth, kSourceHeight, // Dimensions 105 kSourceWidth, // YStride 106 kSourceWidth / 2, // UVStride 107 kSourceWidth * kBpp, // RGBStride 108 media::YV12); 109 110 #if defined(OS_ANDROID) 111 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); 112 #endif 113 114 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, 115 kDJB2HashSeed); 116 EXPECT_EQ(2413171226u, rgb_hash); 117 } 118 119 TEST(YUVConvertTest, YV16) { 120 // Allocate all surfaces. 121 scoped_ptr<uint8[]> yuv_bytes; 122 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 123 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSizeConverted]); 124 125 // Read YUV reference data from file. 126 ReadYV16Data(&yuv_bytes); 127 128 // Convert a frame of YUV to 32 bit ARGB. 129 media::ConvertYUVToRGB32(yuv_bytes.get(), // Y 130 yuv_bytes.get() + kSourceUOffset, // U 131 yuv_bytes.get() + kSourceYSize * 3 / 2, // V 132 rgb_converted_bytes.get(), // RGB output 133 kSourceWidth, kSourceHeight, // Dimensions 134 kSourceWidth, // YStride 135 kSourceWidth / 2, // UVStride 136 kSourceWidth * kBpp, // RGBStride 137 media::YV16); 138 139 #if defined(OS_ANDROID) 140 SwapRedAndBlueChannels(rgb_converted_bytes.get(), kRGBSizeConverted); 141 #endif 142 143 uint32 rgb_hash = DJB2Hash(rgb_converted_bytes.get(), kRGBSizeConverted, 144 kDJB2HashSeed); 145 EXPECT_EQ(4222342047u, rgb_hash); 146 } 147 148 struct YUVScaleTestData { 149 YUVScaleTestData(media::YUVType y, media::ScaleFilter s, uint32 r) 150 : yuv_type(y), 151 scale_filter(s), 152 rgb_hash(r) { 153 } 154 155 media::YUVType yuv_type; 156 media::ScaleFilter scale_filter; 157 uint32 rgb_hash; 158 }; 159 160 class YUVScaleTest : public ::testing::TestWithParam<YUVScaleTestData> { 161 public: 162 YUVScaleTest() { 163 switch (GetParam().yuv_type) { 164 case media::YV12: 165 ReadYV12Data(&yuv_bytes_); 166 break; 167 case media::YV16: 168 ReadYV16Data(&yuv_bytes_); 169 break; 170 } 171 172 rgb_bytes_.reset(new uint8[kRGBSizeScaled]); 173 } 174 175 // Helpers for getting the proper Y, U and V plane offsets. 176 uint8* y_plane() { return yuv_bytes_.get(); } 177 uint8* u_plane() { return yuv_bytes_.get() + kSourceYSize; } 178 uint8* v_plane() { 179 switch (GetParam().yuv_type) { 180 case media::YV12: 181 return yuv_bytes_.get() + kSourceVOffset; 182 case media::YV16: 183 return yuv_bytes_.get() + kSourceYSize * 3 / 2; 184 } 185 return NULL; 186 } 187 188 scoped_ptr<uint8[]> yuv_bytes_; 189 scoped_ptr<uint8[]> rgb_bytes_; 190 }; 191 192 TEST_P(YUVScaleTest, NoScale) { 193 media::ScaleYUVToRGB32(y_plane(), // Y 194 u_plane(), // U 195 v_plane(), // V 196 rgb_bytes_.get(), // RGB output 197 kSourceWidth, kSourceHeight, // Dimensions 198 kSourceWidth, kSourceHeight, // Dimensions 199 kSourceWidth, // YStride 200 kSourceWidth / 2, // UvStride 201 kSourceWidth * kBpp, // RgbStride 202 GetParam().yuv_type, 203 media::ROTATE_0, 204 GetParam().scale_filter); 205 206 uint32 yuv_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); 207 208 media::ConvertYUVToRGB32(y_plane(), // Y 209 u_plane(), // U 210 v_plane(), // V 211 rgb_bytes_.get(), // RGB output 212 kSourceWidth, kSourceHeight, // Dimensions 213 kSourceWidth, // YStride 214 kSourceWidth / 2, // UVStride 215 kSourceWidth * kBpp, // RGBStride 216 GetParam().yuv_type); 217 218 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSize, kDJB2HashSeed); 219 220 EXPECT_EQ(yuv_hash, rgb_hash); 221 } 222 223 TEST_P(YUVScaleTest, Normal) { 224 media::ScaleYUVToRGB32(y_plane(), // Y 225 u_plane(), // U 226 v_plane(), // V 227 rgb_bytes_.get(), // RGB output 228 kSourceWidth, kSourceHeight, // Dimensions 229 kScaledWidth, kScaledHeight, // Dimensions 230 kSourceWidth, // YStride 231 kSourceWidth / 2, // UvStride 232 kScaledWidth * kBpp, // RgbStride 233 GetParam().yuv_type, 234 media::ROTATE_0, 235 GetParam().scale_filter); 236 237 #if defined(OS_ANDROID) 238 SwapRedAndBlueChannels(rgb_bytes_.get(), kRGBSizeScaled); 239 #endif 240 241 uint32 rgb_hash = DJB2Hash(rgb_bytes_.get(), kRGBSizeScaled, kDJB2HashSeed); 242 EXPECT_EQ(GetParam().rgb_hash, rgb_hash); 243 } 244 245 TEST_P(YUVScaleTest, ZeroSourceSize) { 246 media::ScaleYUVToRGB32(y_plane(), // Y 247 u_plane(), // U 248 v_plane(), // V 249 rgb_bytes_.get(), // RGB output 250 0, 0, // Dimensions 251 kScaledWidth, kScaledHeight, // Dimensions 252 kSourceWidth, // YStride 253 kSourceWidth / 2, // UvStride 254 kScaledWidth * kBpp, // RgbStride 255 GetParam().yuv_type, 256 media::ROTATE_0, 257 GetParam().scale_filter); 258 259 // Testing for out-of-bound read/writes with AddressSanitizer. 260 } 261 262 TEST_P(YUVScaleTest, ZeroDestinationSize) { 263 media::ScaleYUVToRGB32(y_plane(), // Y 264 u_plane(), // U 265 v_plane(), // V 266 rgb_bytes_.get(), // RGB output 267 kSourceWidth, kSourceHeight, // Dimensions 268 0, 0, // Dimensions 269 kSourceWidth, // YStride 270 kSourceWidth / 2, // UvStride 271 kScaledWidth * kBpp, // RgbStride 272 GetParam().yuv_type, 273 media::ROTATE_0, 274 GetParam().scale_filter); 275 276 // Testing for out-of-bound read/writes with AddressSanitizer. 277 } 278 279 TEST_P(YUVScaleTest, OddWidthAndHeightNotCrash) { 280 media::ScaleYUVToRGB32(y_plane(), // Y 281 u_plane(), // U 282 v_plane(), // V 283 rgb_bytes_.get(), // RGB output 284 kSourceWidth, kSourceHeight, // Dimensions 285 3, 3, // Dimensions 286 kSourceWidth, // YStride 287 kSourceWidth / 2, // UvStride 288 kScaledWidth * kBpp, // RgbStride 289 GetParam().yuv_type, 290 media::ROTATE_0, 291 GetParam().scale_filter); 292 } 293 294 INSTANTIATE_TEST_CASE_P( 295 YUVScaleFormats, YUVScaleTest, 296 ::testing::Values( 297 YUVScaleTestData(media::YV12, media::FILTER_NONE, 4136904952u), 298 YUVScaleTestData(media::YV16, media::FILTER_NONE, 1501777547u), 299 YUVScaleTestData(media::YV12, media::FILTER_BILINEAR, 3164274689u), 300 YUVScaleTestData(media::YV16, media::FILTER_BILINEAR, 3095878046u))); 301 302 // This tests a known worst case YUV value, and for overflow. 303 TEST(YUVConvertTest, Clamp) { 304 // Allocate all surfaces. 305 scoped_ptr<uint8[]> yuv_bytes(new uint8[1]); 306 scoped_ptr<uint8[]> rgb_bytes(new uint8[1]); 307 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[1]); 308 309 // Values that failed previously in bug report. 310 unsigned char y = 255u; 311 unsigned char u = 255u; 312 unsigned char v = 19u; 313 314 // Prefill extra large destination buffer to test for overflow. 315 unsigned char rgb[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; 316 unsigned char expected[8] = { 255, 255, 104, 255, 4, 5, 6, 7 }; 317 // Convert a frame of YUV to 32 bit ARGB. 318 media::ConvertYUVToRGB32(&y, // Y 319 &u, // U 320 &v, // V 321 &rgb[0], // RGB output 322 1, 1, // Dimensions 323 0, // YStride 324 0, // UVStride 325 0, // RGBStride 326 media::YV12); 327 328 #if defined(OS_ANDROID) 329 SwapRedAndBlueChannels(rgb, kBpp); 330 #endif 331 332 int expected_test = memcmp(rgb, expected, sizeof(expected)); 333 EXPECT_EQ(0, expected_test); 334 } 335 336 TEST(YUVConvertTest, RGB24ToYUV) { 337 // Allocate all surfaces. 338 scoped_ptr<uint8[]> rgb_bytes; 339 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 340 341 // Read RGB24 reference data from file. 342 ReadRGB24Data(&rgb_bytes); 343 344 // Convert to I420. 345 media::ConvertRGB24ToYUV(rgb_bytes.get(), 346 yuv_converted_bytes.get(), 347 yuv_converted_bytes.get() + kSourceUOffset, 348 yuv_converted_bytes.get() + kSourceVOffset, 349 kSourceWidth, kSourceHeight, // Dimensions 350 kSourceWidth * 3, // RGBStride 351 kSourceWidth, // YStride 352 kSourceWidth / 2); // UVStride 353 354 uint32 rgb_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, 355 kDJB2HashSeed); 356 EXPECT_EQ(320824432u, rgb_hash); 357 } 358 359 TEST(YUVConvertTest, RGB32ToYUV) { 360 // Allocate all surfaces. 361 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 362 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 363 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 364 scoped_ptr<uint8[]> rgb_converted_bytes(new uint8[kRGBSize]); 365 366 // Read YUV reference data from file. 367 base::FilePath yuv_url; 368 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); 369 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) 370 .Append(FILE_PATH_LITERAL("test")) 371 .Append(FILE_PATH_LITERAL("data")) 372 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); 373 EXPECT_EQ(static_cast<int>(kYUV12Size), 374 base::ReadFile(yuv_url, 375 reinterpret_cast<char*>(yuv_bytes.get()), 376 static_cast<int>(kYUV12Size))); 377 378 // Convert a frame of YUV to 32 bit ARGB. 379 media::ConvertYUVToRGB32(yuv_bytes.get(), 380 yuv_bytes.get() + kSourceUOffset, 381 yuv_bytes.get() + kSourceVOffset, 382 rgb_bytes.get(), // RGB output 383 kSourceWidth, kSourceHeight, // Dimensions 384 kSourceWidth, // YStride 385 kSourceWidth / 2, // UVStride 386 kSourceWidth * kBpp, // RGBStride 387 media::YV12); 388 389 // Convert RGB32 to YV12. 390 media::ConvertRGB32ToYUV(rgb_bytes.get(), 391 yuv_converted_bytes.get(), 392 yuv_converted_bytes.get() + kSourceUOffset, 393 yuv_converted_bytes.get() + kSourceVOffset, 394 kSourceWidth, kSourceHeight, // Dimensions 395 kSourceWidth * 4, // RGBStride 396 kSourceWidth, // YStride 397 kSourceWidth / 2); // UVStride 398 399 // Convert YV12 back to RGB32. 400 media::ConvertYUVToRGB32(yuv_converted_bytes.get(), 401 yuv_converted_bytes.get() + kSourceUOffset, 402 yuv_converted_bytes.get() + kSourceVOffset, 403 rgb_converted_bytes.get(), // RGB output 404 kSourceWidth, kSourceHeight, // Dimensions 405 kSourceWidth, // YStride 406 kSourceWidth / 2, // UVStride 407 kSourceWidth * kBpp, // RGBStride 408 media::YV12); 409 410 int error = 0; 411 for (int i = 0; i < kRGBSize; ++i) { 412 int diff = rgb_converted_bytes[i] - rgb_bytes[i]; 413 if (diff < 0) 414 diff = -diff; 415 error += diff; 416 } 417 418 // Make sure error is within bound. 419 DVLOG(1) << "Average error per channel: " << error / kRGBSize; 420 EXPECT_GT(5, error / kRGBSize); 421 } 422 423 TEST(YUVConvertTest, YUY2ToYUV) { 424 // Allocate all surfaces. 425 scoped_ptr<uint8[]> yuy_bytes; 426 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 427 428 // Read YUY reference data from file. 429 ReadYUY2Data(&yuy_bytes); 430 431 // Convert to I420. 432 media::ConvertYUY2ToYUV(yuy_bytes.get(), 433 yuv_converted_bytes.get(), 434 yuv_converted_bytes.get() + kSourceUOffset, 435 yuv_converted_bytes.get() + kSourceVOffset, 436 kSourceWidth, kSourceHeight); 437 438 uint32 yuy_hash = DJB2Hash(yuv_converted_bytes.get(), kYUV12Size, 439 kDJB2HashSeed); 440 EXPECT_EQ(666823187u, yuy_hash); 441 } 442 443 TEST(YUVConvertTest, DownScaleYUVToRGB32WithRect) { 444 // Read YUV reference data from file. 445 base::FilePath yuv_url; 446 EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &yuv_url)); 447 yuv_url = yuv_url.Append(FILE_PATH_LITERAL("media")) 448 .Append(FILE_PATH_LITERAL("test")) 449 .Append(FILE_PATH_LITERAL("data")) 450 .Append(FILE_PATH_LITERAL("bali_640x360_P420.yuv")); 451 const size_t size_of_yuv = kSourceYSize * 12 / 8; // 12 bpp. 452 scoped_ptr<uint8[]> yuv_bytes(new uint8[size_of_yuv]); 453 EXPECT_EQ(static_cast<int>(size_of_yuv), 454 base::ReadFile(yuv_url, 455 reinterpret_cast<char*>(yuv_bytes.get()), 456 static_cast<int>(size_of_yuv))); 457 458 // Scale the full frame of YUV to 32 bit ARGB. 459 // The API currently only supports down-scaling, so we don't test up-scaling. 460 const size_t size_of_rgb_scaled = kDownScaledWidth * kDownScaledHeight * kBpp; 461 scoped_ptr<uint8[]> rgb_scaled_bytes(new uint8[size_of_rgb_scaled]); 462 gfx::Rect sub_rect(0, 0, kDownScaledWidth, kDownScaledHeight); 463 464 // We can't compare with the full-frame scaler because it uses slightly 465 // different sampling coordinates. 466 media::ScaleYUVToRGB32WithRect( 467 yuv_bytes.get(), // Y 468 yuv_bytes.get() + kSourceUOffset, // U 469 yuv_bytes.get() + kSourceVOffset, // V 470 rgb_scaled_bytes.get(), // Rgb output 471 kSourceWidth, kSourceHeight, // Dimensions 472 kDownScaledWidth, kDownScaledHeight, // Dimensions 473 sub_rect.x(), sub_rect.y(), // Dest rect 474 sub_rect.right(), sub_rect.bottom(), // Dest rect 475 kSourceWidth, // YStride 476 kSourceWidth / 2, // UvStride 477 kDownScaledWidth * kBpp); // RgbStride 478 479 uint32 rgb_hash_full_rect = DJB2Hash(rgb_scaled_bytes.get(), 480 size_of_rgb_scaled, 481 kDJB2HashSeed); 482 483 // Re-scale sub-rectangles and verify the results are the same. 484 int next_sub_rect = 0; 485 while (!sub_rect.IsEmpty()) { 486 // Scale a partial rectangle. 487 media::ScaleYUVToRGB32WithRect( 488 yuv_bytes.get(), // Y 489 yuv_bytes.get() + kSourceUOffset, // U 490 yuv_bytes.get() + kSourceVOffset, // V 491 rgb_scaled_bytes.get(), // Rgb output 492 kSourceWidth, kSourceHeight, // Dimensions 493 kDownScaledWidth, kDownScaledHeight, // Dimensions 494 sub_rect.x(), sub_rect.y(), // Dest rect 495 sub_rect.right(), sub_rect.bottom(), // Dest rect 496 kSourceWidth, // YStride 497 kSourceWidth / 2, // UvStride 498 kDownScaledWidth * kBpp); // RgbStride 499 uint32 rgb_hash_sub_rect = DJB2Hash(rgb_scaled_bytes.get(), 500 size_of_rgb_scaled, 501 kDJB2HashSeed); 502 503 EXPECT_EQ(rgb_hash_full_rect, rgb_hash_sub_rect); 504 505 // Now pick choose a quarter rect of this sub-rect. 506 if (next_sub_rect & 1) 507 sub_rect.set_x(sub_rect.x() + sub_rect.width() / 2); 508 if (next_sub_rect & 2) 509 sub_rect.set_y(sub_rect.y() + sub_rect.height() / 2); 510 sub_rect.set_width(sub_rect.width() / 2); 511 sub_rect.set_height(sub_rect.height() / 2); 512 next_sub_rect++; 513 } 514 } 515 516 #if !defined(ARCH_CPU_ARM_FAMILY) && !defined(ARCH_CPU_MIPS_FAMILY) 517 TEST(YUVConvertTest, RGB32ToYUV_SSE2_MatchReference) { 518 base::CPU cpu; 519 if (!cpu.has_sse2()) { 520 LOG(WARNING) << "System doesn't support SSE2, test not executed."; 521 return; 522 } 523 524 // Allocate all surfaces. 525 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 526 scoped_ptr<uint8[]> rgb_bytes(new uint8[kRGBSize]); 527 scoped_ptr<uint8[]> yuv_converted_bytes(new uint8[kYUV12Size]); 528 scoped_ptr<uint8[]> yuv_reference_bytes(new uint8[kYUV12Size]); 529 530 ReadYV12Data(&yuv_bytes); 531 532 // Convert a frame of YUV to 32 bit ARGB. 533 media::ConvertYUVToRGB32( 534 yuv_bytes.get(), 535 yuv_bytes.get() + kSourceUOffset, 536 yuv_bytes.get() + kSourceVOffset, 537 rgb_bytes.get(), // RGB output 538 kSourceWidth, kSourceHeight, // Dimensions 539 kSourceWidth, // YStride 540 kSourceWidth / 2, // UVStride 541 kSourceWidth * kBpp, // RGBStride 542 media::YV12); 543 544 // Convert RGB32 to YV12 with SSE2 version. 545 media::ConvertRGB32ToYUV_SSE2( 546 rgb_bytes.get(), 547 yuv_converted_bytes.get(), 548 yuv_converted_bytes.get() + kSourceUOffset, 549 yuv_converted_bytes.get() + kSourceVOffset, 550 kSourceWidth, kSourceHeight, // Dimensions 551 kSourceWidth * 4, // RGBStride 552 kSourceWidth, // YStride 553 kSourceWidth / 2); // UVStride 554 555 // Convert RGB32 to YV12 with reference version. 556 media::ConvertRGB32ToYUV_SSE2_Reference( 557 rgb_bytes.get(), 558 yuv_reference_bytes.get(), 559 yuv_reference_bytes.get() + kSourceUOffset, 560 yuv_reference_bytes.get() + kSourceVOffset, 561 kSourceWidth, kSourceHeight, // Dimensions 562 kSourceWidth * 4, // RGBStride 563 kSourceWidth, // YStride 564 kSourceWidth / 2); // UVStride 565 566 // Now convert a odd width and height, this overrides part of the buffer 567 // generated above but that is fine because the point of this test is to 568 // match the result with the reference code. 569 570 // Convert RGB32 to YV12 with SSE2 version. 571 media::ConvertRGB32ToYUV_SSE2( 572 rgb_bytes.get(), 573 yuv_converted_bytes.get(), 574 yuv_converted_bytes.get() + kSourceUOffset, 575 yuv_converted_bytes.get() + kSourceVOffset, 576 7, 7, // Dimensions 577 kSourceWidth * 4, // RGBStride 578 kSourceWidth, // YStride 579 kSourceWidth / 2); // UVStride 580 581 // Convert RGB32 to YV12 with reference version. 582 media::ConvertRGB32ToYUV_SSE2_Reference( 583 rgb_bytes.get(), 584 yuv_reference_bytes.get(), 585 yuv_reference_bytes.get() + kSourceUOffset, 586 yuv_reference_bytes.get() + kSourceVOffset, 587 7, 7, // Dimensions 588 kSourceWidth * 4, // RGBStride 589 kSourceWidth, // YStride 590 kSourceWidth / 2); // UVStride 591 592 int error = 0; 593 for (int i = 0; i < kYUV12Size; ++i) { 594 int diff = yuv_reference_bytes[i] - yuv_converted_bytes[i]; 595 if (diff < 0) 596 diff = -diff; 597 error += diff; 598 } 599 600 // Make sure there's no difference from the reference. 601 EXPECT_EQ(0, error); 602 } 603 604 TEST(YUVConvertTest, ConvertYUVToRGB32Row_MMX) { 605 base::CPU cpu; 606 if (!cpu.has_mmx()) { 607 LOG(WARNING) << "System not supported. Test skipped."; 608 return; 609 } 610 611 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 612 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 613 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 614 ReadYV12Data(&yuv_bytes); 615 616 const int kWidth = 167; 617 ConvertYUVToRGB32Row_C(yuv_bytes.get(), 618 yuv_bytes.get() + kSourceUOffset, 619 yuv_bytes.get() + kSourceVOffset, 620 rgb_bytes_reference.get(), 621 kWidth); 622 ConvertYUVToRGB32Row_MMX(yuv_bytes.get(), 623 yuv_bytes.get() + kSourceUOffset, 624 yuv_bytes.get() + kSourceVOffset, 625 rgb_bytes_converted.get(), 626 kWidth); 627 media::EmptyRegisterState(); 628 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 629 rgb_bytes_converted.get(), 630 kWidth * kBpp)); 631 } 632 633 TEST(YUVConvertTest, ConvertYUVToRGB32Row_SSE) { 634 base::CPU cpu; 635 if (!cpu.has_sse()) { 636 LOG(WARNING) << "System not supported. Test skipped."; 637 return; 638 } 639 640 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 641 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 642 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 643 ReadYV12Data(&yuv_bytes); 644 645 const int kWidth = 167; 646 ConvertYUVToRGB32Row_C(yuv_bytes.get(), 647 yuv_bytes.get() + kSourceUOffset, 648 yuv_bytes.get() + kSourceVOffset, 649 rgb_bytes_reference.get(), 650 kWidth); 651 ConvertYUVToRGB32Row_SSE(yuv_bytes.get(), 652 yuv_bytes.get() + kSourceUOffset, 653 yuv_bytes.get() + kSourceVOffset, 654 rgb_bytes_converted.get(), 655 kWidth); 656 media::EmptyRegisterState(); 657 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 658 rgb_bytes_converted.get(), 659 kWidth * kBpp)); 660 } 661 662 TEST(YUVConvertTest, ScaleYUVToRGB32Row_MMX) { 663 base::CPU cpu; 664 if (!cpu.has_mmx()) { 665 LOG(WARNING) << "System not supported. Test skipped."; 666 return; 667 } 668 669 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 670 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 671 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 672 ReadYV12Data(&yuv_bytes); 673 674 const int kWidth = 167; 675 const int kSourceDx = 80000; // This value means a scale down. 676 ScaleYUVToRGB32Row_C(yuv_bytes.get(), 677 yuv_bytes.get() + kSourceUOffset, 678 yuv_bytes.get() + kSourceVOffset, 679 rgb_bytes_reference.get(), 680 kWidth, 681 kSourceDx); 682 ScaleYUVToRGB32Row_MMX(yuv_bytes.get(), 683 yuv_bytes.get() + kSourceUOffset, 684 yuv_bytes.get() + kSourceVOffset, 685 rgb_bytes_converted.get(), 686 kWidth, 687 kSourceDx); 688 media::EmptyRegisterState(); 689 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 690 rgb_bytes_converted.get(), 691 kWidth * kBpp)); 692 } 693 694 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE) { 695 base::CPU cpu; 696 if (!cpu.has_sse()) { 697 LOG(WARNING) << "System not supported. Test skipped."; 698 return; 699 } 700 701 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 702 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 703 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 704 ReadYV12Data(&yuv_bytes); 705 706 const int kWidth = 167; 707 const int kSourceDx = 80000; // This value means a scale down. 708 ScaleYUVToRGB32Row_C(yuv_bytes.get(), 709 yuv_bytes.get() + kSourceUOffset, 710 yuv_bytes.get() + kSourceVOffset, 711 rgb_bytes_reference.get(), 712 kWidth, 713 kSourceDx); 714 ScaleYUVToRGB32Row_SSE(yuv_bytes.get(), 715 yuv_bytes.get() + kSourceUOffset, 716 yuv_bytes.get() + kSourceVOffset, 717 rgb_bytes_converted.get(), 718 kWidth, 719 kSourceDx); 720 media::EmptyRegisterState(); 721 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 722 rgb_bytes_converted.get(), 723 kWidth * kBpp)); 724 } 725 726 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX) { 727 base::CPU cpu; 728 if (!cpu.has_mmx()) { 729 LOG(WARNING) << "System not supported. Test skipped."; 730 return; 731 } 732 733 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 734 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 735 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 736 ReadYV12Data(&yuv_bytes); 737 738 const int kWidth = 167; 739 const int kSourceDx = 80000; // This value means a scale down. 740 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), 741 yuv_bytes.get() + kSourceUOffset, 742 yuv_bytes.get() + kSourceVOffset, 743 rgb_bytes_reference.get(), 744 kWidth, 745 kSourceDx); 746 LinearScaleYUVToRGB32Row_MMX(yuv_bytes.get(), 747 yuv_bytes.get() + kSourceUOffset, 748 yuv_bytes.get() + kSourceVOffset, 749 rgb_bytes_converted.get(), 750 kWidth, 751 kSourceDx); 752 media::EmptyRegisterState(); 753 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 754 rgb_bytes_converted.get(), 755 kWidth * kBpp)); 756 } 757 758 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_SSE) { 759 base::CPU cpu; 760 if (!cpu.has_sse()) { 761 LOG(WARNING) << "System not supported. Test skipped."; 762 return; 763 } 764 765 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 766 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 767 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 768 ReadYV12Data(&yuv_bytes); 769 770 const int kWidth = 167; 771 const int kSourceDx = 80000; // This value means a scale down. 772 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), 773 yuv_bytes.get() + kSourceUOffset, 774 yuv_bytes.get() + kSourceVOffset, 775 rgb_bytes_reference.get(), 776 kWidth, 777 kSourceDx); 778 LinearScaleYUVToRGB32Row_SSE(yuv_bytes.get(), 779 yuv_bytes.get() + kSourceUOffset, 780 yuv_bytes.get() + kSourceVOffset, 781 rgb_bytes_converted.get(), 782 kWidth, 783 kSourceDx); 784 media::EmptyRegisterState(); 785 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 786 rgb_bytes_converted.get(), 787 kWidth * kBpp)); 788 } 789 790 TEST(YUVConvertTest, FilterYUVRows_C_OutOfBounds) { 791 scoped_ptr<uint8[]> src(new uint8[16]); 792 scoped_ptr<uint8[]> dst(new uint8[16]); 793 794 memset(src.get(), 0xff, 16); 795 memset(dst.get(), 0, 16); 796 797 media::FilterYUVRows_C(dst.get(), src.get(), src.get(), 1, 255); 798 799 EXPECT_EQ(255u, dst[0]); 800 for (int i = 1; i < 16; ++i) { 801 EXPECT_EQ(0u, dst[i]) << " not equal at " << i; 802 } 803 } 804 805 #if defined(MEDIA_MMX_INTRINSICS_AVAILABLE) 806 TEST(YUVConvertTest, FilterYUVRows_MMX_OutOfBounds) { 807 base::CPU cpu; 808 if (!cpu.has_mmx()) { 809 LOG(WARNING) << "System not supported. Test skipped."; 810 return; 811 } 812 813 scoped_ptr<uint8[]> src(new uint8[16]); 814 scoped_ptr<uint8[]> dst(new uint8[16]); 815 816 memset(src.get(), 0xff, 16); 817 memset(dst.get(), 0, 16); 818 819 media::FilterYUVRows_MMX(dst.get(), src.get(), src.get(), 1, 255); 820 media::EmptyRegisterState(); 821 822 EXPECT_EQ(255u, dst[0]); 823 for (int i = 1; i < 16; ++i) { 824 EXPECT_EQ(0u, dst[i]); 825 } 826 } 827 #endif // defined(MEDIA_MMX_INTRINSICS_AVAILABLE) 828 829 TEST(YUVConvertTest, FilterYUVRows_SSE2_OutOfBounds) { 830 base::CPU cpu; 831 if (!cpu.has_sse2()) { 832 LOG(WARNING) << "System not supported. Test skipped."; 833 return; 834 } 835 836 scoped_ptr<uint8[]> src(new uint8[16]); 837 scoped_ptr<uint8[]> dst(new uint8[16]); 838 839 memset(src.get(), 0xff, 16); 840 memset(dst.get(), 0, 16); 841 842 media::FilterYUVRows_SSE2(dst.get(), src.get(), src.get(), 1, 255); 843 844 EXPECT_EQ(255u, dst[0]); 845 for (int i = 1; i < 16; ++i) { 846 EXPECT_EQ(0u, dst[i]); 847 } 848 } 849 850 #if defined(MEDIA_MMX_INTRINSICS_AVAILABLE) 851 TEST(YUVConvertTest, FilterYUVRows_MMX_UnalignedDestination) { 852 base::CPU cpu; 853 if (!cpu.has_mmx()) { 854 LOG(WARNING) << "System not supported. Test skipped."; 855 return; 856 } 857 858 const int kSize = 32; 859 scoped_ptr<uint8[]> src(new uint8[kSize]); 860 scoped_ptr<uint8[]> dst_sample(new uint8[kSize]); 861 scoped_ptr<uint8[]> dst(new uint8[kSize]); 862 863 memset(dst_sample.get(), 0, kSize); 864 memset(dst.get(), 0, kSize); 865 for (int i = 0; i < kSize; ++i) 866 src[i] = 100 + i; 867 868 media::FilterYUVRows_C(dst_sample.get(), 869 src.get(), src.get(), 17, 128); 870 871 // Generate an unaligned output address. 872 uint8* dst_ptr = 873 reinterpret_cast<uint8*>( 874 (reinterpret_cast<uintptr_t>(dst.get() + 8) & ~7) + 1); 875 media::FilterYUVRows_MMX(dst_ptr, src.get(), src.get(), 17, 128); 876 media::EmptyRegisterState(); 877 878 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 17)); 879 } 880 #endif // defined(MEDIA_MMX_INTRINSICS_AVAILABLE) 881 882 TEST(YUVConvertTest, FilterYUVRows_SSE2_UnalignedDestination) { 883 base::CPU cpu; 884 if (!cpu.has_sse2()) { 885 LOG(WARNING) << "System not supported. Test skipped."; 886 return; 887 } 888 889 const int kSize = 64; 890 scoped_ptr<uint8[]> src(new uint8[kSize]); 891 scoped_ptr<uint8[]> dst_sample(new uint8[kSize]); 892 scoped_ptr<uint8[]> dst(new uint8[kSize]); 893 894 memset(dst_sample.get(), 0, kSize); 895 memset(dst.get(), 0, kSize); 896 for (int i = 0; i < kSize; ++i) 897 src[i] = 100 + i; 898 899 media::FilterYUVRows_C(dst_sample.get(), 900 src.get(), src.get(), 37, 128); 901 902 // Generate an unaligned output address. 903 uint8* dst_ptr = 904 reinterpret_cast<uint8*>( 905 (reinterpret_cast<uintptr_t>(dst.get() + 16) & ~15) + 1); 906 media::FilterYUVRows_SSE2(dst_ptr, src.get(), src.get(), 37, 128); 907 media::EmptyRegisterState(); 908 909 EXPECT_EQ(0, memcmp(dst_sample.get(), dst_ptr, 37)); 910 } 911 912 #if defined(ARCH_CPU_X86_64) 913 914 TEST(YUVConvertTest, ScaleYUVToRGB32Row_SSE2_X64) { 915 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 916 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 917 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 918 ReadYV12Data(&yuv_bytes); 919 920 const int kWidth = 167; 921 const int kSourceDx = 80000; // This value means a scale down. 922 ScaleYUVToRGB32Row_C(yuv_bytes.get(), 923 yuv_bytes.get() + kSourceUOffset, 924 yuv_bytes.get() + kSourceVOffset, 925 rgb_bytes_reference.get(), 926 kWidth, 927 kSourceDx); 928 ScaleYUVToRGB32Row_SSE2_X64(yuv_bytes.get(), 929 yuv_bytes.get() + kSourceUOffset, 930 yuv_bytes.get() + kSourceVOffset, 931 rgb_bytes_converted.get(), 932 kWidth, 933 kSourceDx); 934 media::EmptyRegisterState(); 935 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 936 rgb_bytes_converted.get(), 937 kWidth * kBpp)); 938 } 939 940 TEST(YUVConvertTest, LinearScaleYUVToRGB32Row_MMX_X64) { 941 scoped_ptr<uint8[]> yuv_bytes(new uint8[kYUV12Size]); 942 scoped_ptr<uint8[]> rgb_bytes_reference(new uint8[kRGBSize]); 943 scoped_ptr<uint8[]> rgb_bytes_converted(new uint8[kRGBSize]); 944 ReadYV12Data(&yuv_bytes); 945 946 const int kWidth = 167; 947 const int kSourceDx = 80000; // This value means a scale down. 948 LinearScaleYUVToRGB32Row_C(yuv_bytes.get(), 949 yuv_bytes.get() + kSourceUOffset, 950 yuv_bytes.get() + kSourceVOffset, 951 rgb_bytes_reference.get(), 952 kWidth, 953 kSourceDx); 954 LinearScaleYUVToRGB32Row_MMX_X64(yuv_bytes.get(), 955 yuv_bytes.get() + kSourceUOffset, 956 yuv_bytes.get() + kSourceVOffset, 957 rgb_bytes_converted.get(), 958 kWidth, 959 kSourceDx); 960 media::EmptyRegisterState(); 961 EXPECT_EQ(0, memcmp(rgb_bytes_reference.get(), 962 rgb_bytes_converted.get(), 963 kWidth * kBpp)); 964 } 965 966 #endif // defined(ARCH_CPU_X86_64) 967 968 #endif // defined(ARCH_CPU_X86_FAMILY) 969 970 } // namespace media 971