Home | History | Annotate | Download | only in base
      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   file_util::GetFileSize(path, &actual_size);
     55   CHECK_EQ(actual_size, expected_size);
     56 
     57   // Verify bytes read are correct.
     58   int bytes_read = file_util::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             file_util::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             file_util::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