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 }