tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TestYUVtoRGB32.cpp (14111B)


      1 #include "gtest/gtest.h"
      2 
      3 #include <array>
      4 #include <cmath>
      5 #include <tuple>
      6 #include <unordered_map>
      7 
      8 #include "ImageContainer.h"
      9 #include "YCbCrUtils.h"
     10 #include "nsTArray.h"
     11 
     12 using Color = std::tuple<uint8_t, uint8_t, uint8_t>;
     13 using namespace mozilla;
     14 
     15 const Color BLACK(0, 0, 0);
     16 const Color BLUE(0, 0, 255);
     17 const Color GREEN(0, 255, 0);
     18 const Color CYAN(0, 255, 255);
     19 const Color RED(255, 0, 0);
     20 const Color MAGENTA(255, 0, 255);
     21 const Color YELLOW(255, 255, 0);
     22 const Color WHITE(255, 255, 255);
     23 const Color CHOCOLATE(210, 105, 30);
     24 const std::array<Color, 9> COLOR_LIST = {BLACK,   BLUE,   GREEN, CYAN,     RED,
     25                                         MAGENTA, YELLOW, WHITE, CHOCOLATE};
     26 
     27 Color RGB2YUV(const Color& aRGBColor) {
     28  const uint8_t& r = std::get<0>(aRGBColor);
     29  const uint8_t& g = std::get<1>(aRGBColor);
     30  const uint8_t& b = std::get<2>(aRGBColor);
     31 
     32  const double y = r * 0.299 + g * 0.587 + b * 0.114;
     33  const double u = r * -0.168736 + g * -0.331264 + b * 0.5 + 128;
     34  const double v = r * 0.5 + g * -0.418688 + b * -0.081312 + 128;
     35 
     36  return Color(round(y), round(u), round(v));
     37 }
     38 
     39 int32_t CeilingOfHalf(int32_t aValue) {
     40  MOZ_ASSERT(aValue >= 0);
     41  return aValue / 2 + (aValue % 2);
     42 }
     43 
     44 already_AddRefed<layers::PlanarYCbCrImage> CreateI420Image(
     45    const Color& aRGBColor, const gfx::YUVColorSpace& aColorSpace,
     46    const gfx::IntSize& aSize, Maybe<uint8_t> aAlphaValue = Nothing()) {
     47  const int32_t halfWidth = CeilingOfHalf(aSize.width);
     48  const int32_t halfHeight = CeilingOfHalf(aSize.height);
     49 
     50  const size_t yPlaneSize = aSize.width * aSize.height;
     51  const size_t uPlaneSize = halfWidth * halfHeight;
     52  const size_t vPlaneSize = uPlaneSize;
     53  const size_t aPlaneSize = aAlphaValue.isSome() ? yPlaneSize : 0;
     54  const size_t imageSize = yPlaneSize + uPlaneSize + vPlaneSize + aPlaneSize;
     55 
     56  const Color yuvColor = RGB2YUV(aRGBColor);
     57  const uint8_t& yColor = std::get<0>(yuvColor);
     58  const uint8_t& uColor = std::get<1>(yuvColor);
     59  const uint8_t& vColor = std::get<2>(yuvColor);
     60 
     61  UniquePtr<uint8_t[]> buffer(new uint8_t[imageSize]);
     62 
     63  layers::PlanarYCbCrData data;
     64  data.mPictureRect = gfx::IntRect({0, 0}, aSize);
     65 
     66  // Y plane.
     67  uint8_t* yChannel = buffer.get();
     68  memset(yChannel, yColor, yPlaneSize);
     69  data.mYChannel = yChannel;
     70  data.mYStride = aSize.width;
     71  data.mYSkip = 0;
     72 
     73  // Cb plane (aka U).
     74  uint8_t* uChannel = yChannel + yPlaneSize;
     75  memset(uChannel, uColor, uPlaneSize);
     76  data.mCbChannel = uChannel;
     77  data.mCbSkip = 0;
     78 
     79  // Cr plane (aka V).
     80  uint8_t* vChannel = uChannel + uPlaneSize;
     81  memset(vChannel, vColor, vPlaneSize);
     82  data.mCrChannel = vChannel;
     83  data.mCrSkip = 0;
     84 
     85  // CrCb plane vectors.
     86  data.mCbCrStride = halfWidth;
     87  data.mChromaSubsampling = gfx::ChromaSubsampling::HALF_WIDTH_AND_HEIGHT;
     88 
     89  // Alpha plane.
     90  if (aPlaneSize) {
     91    uint8_t* aChannel = vChannel + vPlaneSize;
     92    memset(aChannel, *aAlphaValue, aPlaneSize);
     93    data.mAlpha.emplace();
     94    data.mAlpha->mChannel = aChannel;
     95    data.mAlpha->mSize = aSize;
     96  }
     97 
     98  data.mYUVColorSpace = aColorSpace;
     99 
    100  RefPtr<layers::PlanarYCbCrImage> image =
    101      new layers::RecyclingPlanarYCbCrImage(new layers::BufferRecycleBin());
    102  image->CopyData(data);
    103  return image.forget();
    104 }
    105 
    106 already_AddRefed<layers::PlanarYCbCrImage> CreateI444Image(
    107    const Color& aRGBColor, const gfx::YUVColorSpace& aColorSpace,
    108    const gfx::IntSize& aSize, Maybe<uint8_t> aAlphaValue = Nothing()) {
    109  const size_t yPlaneSize = aSize.width * aSize.height;
    110  const size_t uPlaneSize = yPlaneSize;
    111  const size_t vPlaneSize = yPlaneSize;
    112  const size_t aPlaneSize = aAlphaValue.isSome() ? yPlaneSize : 0;
    113  const size_t imageSize = yPlaneSize + uPlaneSize + vPlaneSize + aPlaneSize;
    114 
    115  const Color yuvColor = RGB2YUV(aRGBColor);
    116  const uint8_t& yColor = std::get<0>(yuvColor);
    117  const uint8_t& uColor = std::get<1>(yuvColor);
    118  const uint8_t& vColor = std::get<2>(yuvColor);
    119 
    120  UniquePtr<uint8_t[]> buffer(new uint8_t[imageSize]);
    121 
    122  layers::PlanarYCbCrData data;
    123  data.mPictureRect = gfx::IntRect({0, 0}, aSize);
    124 
    125  // Y plane.
    126  uint8_t* yChannel = buffer.get();
    127  memset(yChannel, yColor, yPlaneSize);
    128  data.mYChannel = yChannel;
    129  data.mYStride = aSize.width;
    130  data.mYSkip = 0;
    131 
    132  // Cb plane (aka U).
    133  uint8_t* uChannel = yChannel + yPlaneSize;
    134  memset(uChannel, uColor, uPlaneSize);
    135  data.mCbChannel = uChannel;
    136  data.mCbSkip = 0;
    137 
    138  // Cr plane (aka V).
    139  uint8_t* vChannel = uChannel + uPlaneSize;
    140  memset(vChannel, vColor, vPlaneSize);
    141  data.mCrChannel = vChannel;
    142  data.mCrSkip = 0;
    143 
    144  // CrCb plane vectors.
    145  data.mCbCrStride = data.mYStride;
    146  data.mChromaSubsampling = gfx::ChromaSubsampling::FULL;
    147 
    148  // Alpha plane.
    149  if (aPlaneSize) {
    150    uint8_t* aChannel = vChannel + vPlaneSize;
    151    memset(aChannel, *aAlphaValue, aPlaneSize);
    152    data.mAlpha.emplace();
    153    data.mAlpha->mChannel = aChannel;
    154    data.mAlpha->mSize = aSize;
    155  }
    156 
    157  data.mYUVColorSpace = aColorSpace;
    158 
    159  RefPtr<layers::PlanarYCbCrImage> image =
    160      new layers::RecyclingPlanarYCbCrImage(new layers::BufferRecycleBin());
    161  image->CopyData(data);
    162  return image.forget();
    163 }
    164 
    165 void IsColorEqual(uint8_t* aBGRX, uint8_t* aRGBX, size_t aSize) {
    166  ASSERT_EQ(aSize % 4, (size_t)0);
    167  for (size_t i = 0; i < aSize; i += 4) {
    168    ASSERT_EQ(aBGRX[i + 2], aRGBX[i]);      // R
    169    ASSERT_EQ(aBGRX[i + 1], aRGBX[i + 1]);  // G
    170    ASSERT_EQ(aBGRX[i], aRGBX[i + 2]);      // B
    171    ASSERT_EQ(aBGRX[i + 3], aRGBX[i + 3]);  // X or A
    172  }
    173 }
    174 
    175 uint32_t Hash(const Color& aColor) {
    176  const uint8_t& r = std::get<0>(aColor);
    177  const uint8_t& g = std::get<1>(aColor);
    178  const uint8_t& b = std::get<2>(aColor);
    179  return r << 16 | g << 8 | b;
    180 }
    181 
    182 std::unordered_map<uint32_t, std::array<Color, 3>> GetExpectedConvertedRGB() {
    183  static std::unordered_map<uint32_t, std::array<Color, 3>> map;
    184  map.emplace(Hash(BLACK), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    185                                                Color(0, 0, 0),
    186                                                // gfx::YUVColorSpace::BT709
    187                                                Color(0, 0, 0),
    188                                                // gfx::YUVColorSpace::BT2020
    189                                                Color(0, 0, 0)});
    190  map.emplace(Hash(BLUE), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    191                                               Color(0, 82, 0),
    192                                               // gfx::YUVColorSpace::BT709
    193                                               Color(0, 54, 0),
    194                                               // gfx::YUVColorSpace::BT2020
    195                                               Color(0, 53, 0)});
    196  map.emplace(Hash(GREEN), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    197                                                Color(0, 255, 0),
    198                                                // gfx::YUVColorSpace::BT709
    199                                                Color(0, 231, 0),
    200                                                // gfx::YUVColorSpace::BT2020
    201                                                Color(0, 242, 0)});
    202  map.emplace(Hash(CYAN), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    203                                               Color(0, 255, 255),
    204                                               // gfx::YUVColorSpace::BT709
    205                                               Color(0, 248, 255),
    206                                               // gfx::YUVColorSpace::BT2020
    207                                               Color(0, 255, 255)});
    208  map.emplace(Hash(RED), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    209                                              Color(0, 191, 0),
    210                                              // gfx::YUVColorSpace::BT709
    211                                              Color(0, 147, 0),
    212                                              // gfx::YUVColorSpace::BT2020
    213                                              Color(0, 162, 0)});
    214  map.emplace(Hash(MAGENTA), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    215                                                  Color(255, 0, 255),
    216                                                  // gfx::YUVColorSpace::BT709
    217                                                  Color(255, 28, 255),
    218                                                  // gfx::YUVColorSpace::BT2020
    219                                                  Color(255, 18, 255)});
    220  map.emplace(Hash(YELLOW), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    221                                                 Color(255, 255, 0),
    222                                                 // gfx::YUVColorSpace::BT709
    223                                                 Color(255, 255, 0),
    224                                                 // gfx::YUVColorSpace::BT2020
    225                                                 Color(255, 255, 0)});
    226  map.emplace(Hash(WHITE), std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    227                                                Color(255, 255, 255),
    228                                                // gfx::YUVColorSpace::BT709
    229                                                Color(255, 255, 255),
    230                                                // gfx::YUVColorSpace::BT2020
    231                                                Color(255, 255, 255)});
    232  map.emplace(Hash(CHOCOLATE),
    233              std::array<Color, 3>{// gfx::YUVColorSpace::BT601
    234                                   Color(224, 104, 20),
    235                                   // gfx::YUVColorSpace::BT709
    236                                   Color(236, 111, 20),
    237                                   // gfx::YUVColorSpace::BT2020
    238                                   Color(229, 102, 20)});
    239  return map;
    240 }
    241 
    242 void IsColorMatched(const Color& aColor, uint8_t* aRGBX, size_t aSize,
    243                    Maybe<uint8_t> aAlphaValue = Nothing()) {
    244  const uint8_t& r = std::get<0>(aColor);
    245  const uint8_t& g = std::get<1>(aColor);
    246  const uint8_t& b = std::get<2>(aColor);
    247  for (size_t i = 0; i < aSize; i += 4) {
    248    ASSERT_EQ(r, aRGBX[i]);      // R
    249    ASSERT_EQ(g, aRGBX[i + 1]);  // G
    250    ASSERT_EQ(b, aRGBX[i + 2]);  // B
    251    if (aAlphaValue) {
    252      ASSERT_EQ(*aAlphaValue, aRGBX[i + 3]);  // A
    253    }
    254  }
    255 }
    256 
    257 TEST(YCbCrUtils, ConvertYCbCrToRGB32)
    258 {
    259  const gfx::IntSize imgSize(32, 16);
    260  const int32_t stride =
    261      imgSize.Width() * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8X8);
    262  const size_t bufferSize = stride * imgSize.Height();
    263 
    264  const std::array<gfx::YUVColorSpace, 3> colorSpaces{
    265      gfx::YUVColorSpace::BT601, gfx::YUVColorSpace::BT709,
    266      gfx::YUVColorSpace::BT2020};
    267 
    268  std::unordered_map<uint32_t, std::array<Color, 3>> expectations =
    269      GetExpectedConvertedRGB();
    270 
    271  for (const Color& color : COLOR_LIST) {
    272    const std::array<Color, 3>& expectedColors = expectations[Hash(color)];
    273    for (const gfx::YUVColorSpace& colorSpace : colorSpaces) {
    274      RefPtr<layers::PlanarYCbCrImage> img =
    275          CreateI420Image(color, colorSpace, imgSize);
    276 
    277      UniquePtr<uint8_t[]> BGRX = MakeUnique<uint8_t[]>(bufferSize);
    278      ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::B8G8R8X8,
    279                          BGRX.get(), stride, nullptr);
    280 
    281      UniquePtr<uint8_t[]> RGBX = MakeUnique<uint8_t[]>(bufferSize);
    282      ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::R8G8B8X8,
    283                          RGBX.get(), stride, nullptr);
    284 
    285      IsColorEqual(BGRX.get(), RGBX.get(), bufferSize);
    286 
    287      Color expectation = expectedColors[static_cast<size_t>(colorSpace)];
    288      IsColorMatched(expectation, RGBX.get(), bufferSize);
    289    }
    290  }
    291 }
    292 
    293 TEST(YCbCrUtils, ConvertYCbCrToRGB32WithAlpha)
    294 {
    295  const gfx::IntSize imgSize(32, 16);
    296  const int32_t stride =
    297      imgSize.Width() * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8A8);
    298  const size_t bufferSize = stride * imgSize.Height();
    299 
    300  const std::array<gfx::YUVColorSpace, 3> colorSpaces{
    301      gfx::YUVColorSpace::BT601, gfx::YUVColorSpace::BT709,
    302      gfx::YUVColorSpace::BT2020};
    303 
    304  std::unordered_map<uint32_t, std::array<Color, 3>> expectations =
    305      GetExpectedConvertedRGB();
    306 
    307  for (const Color& color : COLOR_LIST) {
    308    const std::array<Color, 3>& expectedColors = expectations[Hash(color)];
    309    for (const gfx::YUVColorSpace& colorSpace : colorSpaces) {
    310      Maybe<uint8_t> alpha = Some(128);
    311      RefPtr<layers::PlanarYCbCrImage> img =
    312          CreateI420Image(color, colorSpace, imgSize, alpha);
    313 
    314      UniquePtr<uint8_t[]> BGRA = MakeUnique<uint8_t[]>(bufferSize);
    315      ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::B8G8R8A8,
    316                          BGRA.get(), stride, nullptr);
    317 
    318      UniquePtr<uint8_t[]> RGBA = MakeUnique<uint8_t[]>(bufferSize);
    319      ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::R8G8B8A8,
    320                          RGBA.get(), stride, nullptr);
    321 
    322      IsColorEqual(BGRA.get(), RGBA.get(), bufferSize);
    323 
    324      Color expectation = expectedColors[static_cast<size_t>(colorSpace)];
    325      IsColorMatched(expectation, RGBA.get(), bufferSize, alpha);
    326    }
    327  }
    328 }
    329 
    330 TEST(YCbCrUtils, ConvertYCbCrToRGB32WithIdentityColorSpace)
    331 {
    332  const gfx::IntSize imgSize(32, 16);
    333  const int32_t stride =
    334      imgSize.Width() * gfx::BytesPerPixel(gfx::SurfaceFormat::B8G8R8X8);
    335  const size_t bufferSize = stride * imgSize.Height();
    336 
    337  for (const Color& color : COLOR_LIST) {
    338    RefPtr<layers::PlanarYCbCrImage> img =
    339        CreateI444Image(color, gfx::YUVColorSpace::Identity, imgSize);
    340 
    341    UniquePtr<uint8_t[]> BGRX = MakeUnique<uint8_t[]>(bufferSize);
    342    ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::B8G8R8X8,
    343                        BGRX.get(), stride, nullptr);
    344 
    345    UniquePtr<uint8_t[]> RGBX = MakeUnique<uint8_t[]>(bufferSize);
    346    ConvertYCbCrToRGB32(*img->GetData(), gfx::SurfaceFormat::R8G8B8X8,
    347                        RGBX.get(), stride, nullptr);
    348 
    349    IsColorEqual(BGRX.get(), RGBX.get(), bufferSize);
    350 
    351    const Color yuvColor = RGB2YUV(color);
    352    const uint8_t& y = std::get<0>(yuvColor);
    353    const uint8_t& u = std::get<1>(yuvColor);
    354    const uint8_t& v = std::get<2>(yuvColor);
    355    const Color expectation(v, y, u);
    356    IsColorMatched(expectation, RGBX.get(), bufferSize);
    357  }
    358 }