TestImageConversion.cpp (7653B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ImageContainer.h" 8 #include "ImageConversion.h" 9 #include "SourceSurfaceRawData.h" 10 #include "gtest/gtest.h" 11 #include "mozilla/RefPtr.h" 12 #include "mozilla/dom/ImageBitmapBinding.h" 13 #include "mozilla/dom/ImageUtils.h" 14 15 using mozilla::ConvertToI420; 16 using mozilla::MakeAndAddRef; 17 using mozilla::MakeRefPtr; 18 using mozilla::Maybe; 19 using mozilla::Nothing; 20 using mozilla::Some; 21 using mozilla::dom::ImageBitmapFormat; 22 using mozilla::gfx::ChromaSubsampling; 23 using mozilla::gfx::DataSourceSurface; 24 using mozilla::gfx::IntSize; 25 using mozilla::gfx::SourceSurfaceAlignedRawData; 26 using mozilla::gfx::SurfaceFormat; 27 using mozilla::layers::PlanarYCbCrImage; 28 using mozilla::layers::SourceSurfaceImage; 29 30 class TestRedPlanarYCbCrImage2x2 final : public PlanarYCbCrImage { 31 public: 32 explicit TestRedPlanarYCbCrImage2x2(ImageBitmapFormat aFormat) { 33 mSize = IntSize(2, 2); 34 mBufferSize = sizeof(mY) + sizeof(mU) + sizeof(mV); 35 mData.mPictureRect = mozilla::gfx::IntRect(mozilla::gfx::IntPoint(), mSize); 36 mData.mYChannel = mY; 37 mData.mYStride = 2; 38 switch (aFormat) { 39 case ImageBitmapFormat::YUV420P: 40 mData.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 41 mData.mCbChannel = mU; 42 mData.mCrChannel = mV; 43 mData.mCbCrStride = 1; 44 break; 45 case ImageBitmapFormat::YUV422P: 46 mData.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH; 47 mData.mCbChannel = mU; 48 mData.mCrChannel = mV; 49 mData.mCbCrStride = 1; 50 break; 51 case ImageBitmapFormat::YUV444P: 52 mData.mChromaSubsampling = ChromaSubsampling::FULL; 53 mData.mCbChannel = mU; 54 mData.mCrChannel = mV; 55 mData.mCbCrStride = 2; 56 break; 57 case ImageBitmapFormat::YUV420SP_NV12: 58 mData.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 59 mData.mCbChannel = mU; 60 mData.mCrChannel = mData.mCbChannel + 1; 61 mData.mCbCrStride = 1; 62 mData.mCrSkip = 1; 63 mData.mCbSkip = 1; 64 mU[1] = mV[0]; 65 mU[3] = mV[1]; 66 break; 67 case ImageBitmapFormat::YUV420SP_NV21: 68 mData.mChromaSubsampling = ChromaSubsampling::HALF_WIDTH_AND_HEIGHT; 69 mData.mCrChannel = mU; 70 mData.mCbChannel = mData.mCrChannel + 1; 71 mData.mCbCrStride = 1; 72 mData.mCrSkip = 1; 73 mData.mCbSkip = 1; 74 mU[0] = mV[0]; 75 mU[2] = mV[1]; 76 break; 77 default: 78 MOZ_CRASH("Unsupported ImageBitmapFormat!"); 79 break; 80 } 81 } 82 83 nsresult CopyData(const Data& aData) override { 84 return NS_ERROR_NOT_IMPLEMENTED; 85 } 86 87 size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { 88 return 0; 89 } 90 91 private: 92 uint8_t mY[4] = {0x52, 0x52, 0x52, 0x52}; 93 uint8_t mU[4] = {0x5A, 0x5A, 0x5A, 0x5A}; 94 uint8_t mV[4] = {0xEF, 0xEF, 0xEF, 0xEF}; 95 }; 96 97 static already_AddRefed<SourceSurfaceImage> CreateRedSurfaceImage2x2( 98 SurfaceFormat aFormat) { 99 uint8_t redPixel[4] = {}; 100 101 switch (aFormat) { 102 case SurfaceFormat::R8G8B8A8: 103 case SurfaceFormat::R8G8B8X8: 104 redPixel[0] = 0xFF; 105 redPixel[3] = 0xFF; 106 break; 107 case SurfaceFormat::B8G8R8A8: 108 case SurfaceFormat::B8G8R8X8: 109 redPixel[2] = 0xFF; 110 redPixel[3] = 0xFF; 111 break; 112 case SurfaceFormat::R5G6B5_UINT16: 113 redPixel[1] = 0xF8; 114 break; 115 default: 116 MOZ_ASSERT_UNREACHABLE("Unsupported format!"); 117 return nullptr; 118 } 119 120 const IntSize size(2, 2); 121 122 auto surface = MakeRefPtr<SourceSurfaceAlignedRawData>(); 123 if (NS_WARN_IF(!surface->Init(size, aFormat, /* aClearMem */ false, 0, 0))) { 124 return nullptr; 125 } 126 127 DataSourceSurface::ScopedMap map(surface, DataSourceSurface::WRITE); 128 if (NS_WARN_IF(!map.IsMapped())) { 129 return nullptr; 130 } 131 132 const uint32_t bpp = BytesPerPixel(aFormat); 133 MOZ_ASSERT(bpp <= sizeof(redPixel)); 134 135 uint8_t* rowPtr = map.GetData(); 136 for (int32_t row = 0; row < size.height; ++row) { 137 for (int32_t col = 0; col < size.width; ++col) { 138 for (uint32_t i = 0; i < bpp; ++i) { 139 rowPtr[col * bpp + i] = redPixel[i]; 140 } 141 } 142 rowPtr += map.GetStride(); 143 } 144 145 return MakeAndAddRef<SourceSurfaceImage>(size, surface); 146 } 147 148 TEST(MediaImageConversion, ConvertToI420) 149 { 150 uint8_t y[20] = {}; 151 uint8_t u[20] = {}; 152 uint8_t v[20] = {}; 153 154 auto checkBuf = [&](const uint8_t* aY, const uint8_t* aU, const uint8_t* aV) { 155 for (size_t i = 0; i < sizeof(y); ++i) { 156 EXPECT_EQ(y[i], aY[i]); 157 } 158 for (size_t i = 0; i < sizeof(u); ++i) { 159 EXPECT_EQ(u[i], aU[i]); 160 } 161 for (size_t i = 0; i < sizeof(v); ++i) { 162 EXPECT_EQ(v[i], aV[i]); 163 } 164 memset(y, 0, sizeof(y)); 165 memset(u, 0, sizeof(u)); 166 memset(v, 0, sizeof(v)); 167 }; 168 169 static constexpr uint8_t yRed1x1[20] = {0x52}; 170 static constexpr uint8_t yRed2x2[20] = {0x52, 0x52, 0x52, 0x52}; 171 static constexpr uint8_t yRed4x4[20] = {0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 172 0x52, 0x52, 0x52, 0x52, 0x52, 0x52, 173 0x52, 0x52, 0x52, 0x52}; 174 175 static constexpr uint8_t uRed1x1[20] = {0x5A}; 176 static constexpr uint8_t uRed2x2[20] = {0x5A, 0x5A, 0x5A, 0x5A}; 177 178 static constexpr uint8_t vRed1x1[20] = {0xEF}; 179 static constexpr uint8_t vRed2x2[20] = {0xEF, 0xEF, 0xEF, 0xEF}; 180 181 auto checkImage = [&](mozilla::layers::Image* aImage, 182 const Maybe<ImageBitmapFormat>& aFormat) { 183 ASSERT_TRUE(!!aImage); 184 185 mozilla::dom::ImageUtils utils(aImage); 186 Maybe<ImageBitmapFormat> format = utils.GetFormat(); 187 ASSERT_EQ(format.isSome(), aFormat.isSome()); 188 if (format.isSome()) { 189 ASSERT_EQ(format.value(), aFormat.value()); 190 } 191 192 EXPECT_TRUE( 193 NS_SUCCEEDED(ConvertToI420(aImage, y, 2, u, 1, v, 1, IntSize(2, 2)))); 194 checkBuf(yRed2x2, uRed1x1, vRed1x1); 195 196 EXPECT_TRUE( 197 NS_SUCCEEDED(ConvertToI420(aImage, y, 1, u, 1, v, 1, IntSize(1, 1)))); 198 checkBuf(yRed1x1, uRed1x1, vRed1x1); 199 200 EXPECT_TRUE( 201 NS_SUCCEEDED(ConvertToI420(aImage, y, 4, u, 2, v, 2, IntSize(4, 4)))); 202 checkBuf(yRed4x4, uRed2x2, vRed2x2); 203 }; 204 205 RefPtr<SourceSurfaceImage> imgRgba = 206 CreateRedSurfaceImage2x2(SurfaceFormat::R8G8B8A8); 207 checkImage(imgRgba, Some(ImageBitmapFormat::RGBA32)); 208 209 RefPtr<SourceSurfaceImage> imgBgra = 210 CreateRedSurfaceImage2x2(SurfaceFormat::B8G8R8A8); 211 checkImage(imgBgra, Some(ImageBitmapFormat::BGRA32)); 212 213 RefPtr<SourceSurfaceImage> imgRgb565 = 214 CreateRedSurfaceImage2x2(SurfaceFormat::R5G6B5_UINT16); 215 checkImage(imgRgb565, Nothing()); 216 217 auto imgYuv420p = 218 MakeRefPtr<TestRedPlanarYCbCrImage2x2>(ImageBitmapFormat::YUV420P); 219 checkImage(imgYuv420p, Some(ImageBitmapFormat::YUV420P)); 220 221 auto imgYuv422p = 222 MakeRefPtr<TestRedPlanarYCbCrImage2x2>(ImageBitmapFormat::YUV422P); 223 checkImage(imgYuv422p, Some(ImageBitmapFormat::YUV422P)); 224 225 auto imgYuv444p = 226 MakeRefPtr<TestRedPlanarYCbCrImage2x2>(ImageBitmapFormat::YUV444P); 227 checkImage(imgYuv444p, Some(ImageBitmapFormat::YUV444P)); 228 229 auto imgYuvNv12 = 230 MakeRefPtr<TestRedPlanarYCbCrImage2x2>(ImageBitmapFormat::YUV420SP_NV12); 231 checkImage(imgYuvNv12, Some(ImageBitmapFormat::YUV420SP_NV12)); 232 233 auto imgYuvNv21 = 234 MakeRefPtr<TestRedPlanarYCbCrImage2x2>(ImageBitmapFormat::YUV420SP_NV21); 235 checkImage(imgYuvNv21, Some(ImageBitmapFormat::YUV420SP_NV21)); 236 }