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