ImageDataSerializer.cpp (12821B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=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 "ImageDataSerializer.h" 8 9 #include "YCbCrUtils.h" // for YCbCr conversions 10 #include "gfx2DGlue.h" // for SurfaceFormatToImageFormat 11 #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc 12 #include "mozilla/DebugOnly.h" // for DebugOnly 13 #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory 14 #include "mozilla/gfx/Logging.h" // for gfxDebug 15 #include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc 16 #include "mozilla/gfx/Types.h" 17 #include "mozilla/mozalloc.h" // for operator delete, etc 18 19 namespace mozilla { 20 namespace layers { 21 namespace ImageDataSerializer { 22 23 using namespace gfx; 24 25 int32_t ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth) { 26 #ifdef XP_MACOSX 27 // Some drivers require an alignment of 32 bytes for efficient texture upload. 28 return GetAlignedStride<32>(aWidth, BytesPerPixel(aFormat)); 29 #else 30 return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat)); 31 #endif 32 } 33 34 int32_t GetRGBStride(const RGBDescriptor& aDescriptor) { 35 return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width); 36 } 37 38 uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) { 39 MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0); 40 41 // This takes care of checking whether there could be overflow 42 // with enough margin for the metadata. 43 if (!gfx::Factory::AllowedSurfaceSize(aSize)) { 44 return 0; 45 } 46 47 // Note we're passing height instad of the bpp parameter, but the end 48 // result is the same - and the bpp was already taken care of in the 49 // ComputeRGBStride function. 50 int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width), 51 aSize.height); 52 53 if (bufsize < 0) { 54 // This should not be possible thanks to Factory::AllowedSurfaceSize 55 return 0; 56 } 57 58 return bufsize; 59 } 60 61 // Minimum required shmem size in bytes 62 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, 63 const gfx::IntSize& aCbCrSize, 64 int32_t aCbCrStride) { 65 MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); 66 67 if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || 68 aCbCrSize.width < 0 || 69 !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || 70 !gfx::Factory::AllowedSurfaceSize( 71 IntSize(aCbCrStride, aCbCrSize.height))) { 72 return 0; 73 } 74 75 // Overflow checks are performed in AllowedSurfaceSize 76 return GetAlignedStride<4>(aYSize.height, aYStride) + 77 2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride); 78 } 79 80 uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, 81 const gfx::IntSize& aCbCrSize, 82 int32_t aCbCrStride, uint32_t aYOffset, 83 uint32_t aCbOffset, uint32_t aCrOffset) { 84 MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); 85 86 if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || 87 aCbCrSize.width < 0 || 88 !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || 89 !gfx::Factory::AllowedSurfaceSize( 90 IntSize(aCbCrStride, aCbCrSize.height))) { 91 return 0; 92 } 93 94 uint32_t yLength = GetAlignedStride<4>(aYStride, aYSize.height); 95 uint32_t cbCrLength = GetAlignedStride<4>(aCbCrStride, aCbCrSize.height); 96 if (yLength == 0 || cbCrLength == 0) { 97 return 0; 98 } 99 100 CheckedInt<uint32_t> yEnd = aYOffset; 101 yEnd += yLength; 102 CheckedInt<uint32_t> cbEnd = aCbOffset; 103 cbEnd += cbCrLength; 104 CheckedInt<uint32_t> crEnd = aCrOffset; 105 crEnd += cbCrLength; 106 107 if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() || 108 yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) { 109 return 0; 110 } 111 112 return crEnd.value(); 113 } 114 115 uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize) { 116 return GetAlignedStride<4>(aBufferSize, 1); 117 } 118 119 void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride, 120 int32_t cbCrHeight, uint32_t& outYOffset, 121 uint32_t& outCbOffset, uint32_t& outCrOffset) { 122 outYOffset = 0; 123 outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight); 124 outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight); 125 } 126 127 gfx::SurfaceFormat FormatFromBufferDescriptor( 128 const BufferDescriptor& aDescriptor) { 129 switch (aDescriptor.type()) { 130 case BufferDescriptor::TRGBDescriptor: 131 return aDescriptor.get_RGBDescriptor().format(); 132 case BufferDescriptor::TYCbCrDescriptor: 133 return gfx::SurfaceFormat::YUV420; 134 default: 135 MOZ_CRASH("GFX: FormatFromBufferDescriptor"); 136 } 137 } 138 139 gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) { 140 switch (aDescriptor.type()) { 141 case BufferDescriptor::TRGBDescriptor: 142 return aDescriptor.get_RGBDescriptor().size(); 143 case BufferDescriptor::TYCbCrDescriptor: { 144 return aDescriptor.get_YCbCrDescriptor().display().Size(); 145 } 146 default: 147 MOZ_CRASH("GFX: SizeFromBufferDescriptor"); 148 } 149 } 150 151 gfx::IntRect RectFromBufferDescriptor(const BufferDescriptor& aDescriptor) { 152 switch (aDescriptor.type()) { 153 case BufferDescriptor::TRGBDescriptor: { 154 auto size = aDescriptor.get_RGBDescriptor().size(); 155 return gfx::IntRect(0, 0, size.Width(), size.Height()); 156 } 157 case BufferDescriptor::TYCbCrDescriptor: 158 return aDescriptor.get_YCbCrDescriptor().display(); 159 default: 160 MOZ_CRASH("GFX: RectFromBufferDescriptor"); 161 } 162 } 163 164 Maybe<gfx::IntSize> YSizeFromBufferDescriptor( 165 const BufferDescriptor& aDescriptor) { 166 switch (aDescriptor.type()) { 167 case BufferDescriptor::TRGBDescriptor: 168 return Nothing(); 169 case BufferDescriptor::TYCbCrDescriptor: 170 return Some(aDescriptor.get_YCbCrDescriptor().ySize()); 171 default: 172 MOZ_CRASH("GFX: YSizeFromBufferDescriptor"); 173 } 174 } 175 176 Maybe<gfx::IntSize> CbCrSizeFromBufferDescriptor( 177 const BufferDescriptor& aDescriptor) { 178 switch (aDescriptor.type()) { 179 case BufferDescriptor::TRGBDescriptor: 180 return Nothing(); 181 case BufferDescriptor::TYCbCrDescriptor: 182 return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize()); 183 default: 184 MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor"); 185 } 186 } 187 188 Maybe<int32_t> YStrideFromBufferDescriptor( 189 const BufferDescriptor& aDescriptor) { 190 switch (aDescriptor.type()) { 191 case BufferDescriptor::TRGBDescriptor: 192 return Nothing(); 193 case BufferDescriptor::TYCbCrDescriptor: 194 return Some(aDescriptor.get_YCbCrDescriptor().yStride()); 195 default: 196 MOZ_CRASH("GFX: YStrideFromBufferDescriptor"); 197 } 198 } 199 200 Maybe<int32_t> CbCrStrideFromBufferDescriptor( 201 const BufferDescriptor& aDescriptor) { 202 switch (aDescriptor.type()) { 203 case BufferDescriptor::TRGBDescriptor: 204 return Nothing(); 205 case BufferDescriptor::TYCbCrDescriptor: 206 return Some(aDescriptor.get_YCbCrDescriptor().cbCrStride()); 207 default: 208 MOZ_CRASH("GFX: CbCrStrideFromBufferDescriptor"); 209 } 210 } 211 212 Maybe<gfx::YUVColorSpace> YUVColorSpaceFromBufferDescriptor( 213 const BufferDescriptor& aDescriptor) { 214 switch (aDescriptor.type()) { 215 case BufferDescriptor::TRGBDescriptor: 216 return Nothing(); 217 case BufferDescriptor::TYCbCrDescriptor: 218 return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace()); 219 default: 220 MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor"); 221 } 222 } 223 224 Maybe<gfx::ColorDepth> ColorDepthFromBufferDescriptor( 225 const BufferDescriptor& aDescriptor) { 226 switch (aDescriptor.type()) { 227 case BufferDescriptor::TRGBDescriptor: 228 return Nothing(); 229 case BufferDescriptor::TYCbCrDescriptor: 230 return Some(aDescriptor.get_YCbCrDescriptor().colorDepth()); 231 default: 232 MOZ_CRASH("GFX: ColorDepthFromBufferDescriptor"); 233 } 234 } 235 236 Maybe<gfx::ColorRange> ColorRangeFromBufferDescriptor( 237 const BufferDescriptor& aDescriptor) { 238 switch (aDescriptor.type()) { 239 case BufferDescriptor::TRGBDescriptor: 240 return Nothing(); 241 case BufferDescriptor::TYCbCrDescriptor: 242 return Some(aDescriptor.get_YCbCrDescriptor().colorRange()); 243 default: 244 MOZ_CRASH("GFX: YUVFullRangeFromBufferDescriptor"); 245 } 246 } 247 248 Maybe<StereoMode> StereoModeFromBufferDescriptor( 249 const BufferDescriptor& aDescriptor) { 250 switch (aDescriptor.type()) { 251 case BufferDescriptor::TRGBDescriptor: 252 return Nothing(); 253 case BufferDescriptor::TYCbCrDescriptor: 254 return Some(aDescriptor.get_YCbCrDescriptor().stereoMode()); 255 default: 256 MOZ_CRASH("GFX: StereoModeFromBufferDescriptor"); 257 } 258 } 259 260 Maybe<gfx::ChromaSubsampling> ChromaSubsamplingFromBufferDescriptor( 261 const BufferDescriptor& aDescriptor) { 262 switch (aDescriptor.type()) { 263 case BufferDescriptor::TRGBDescriptor: 264 return Nothing(); 265 case BufferDescriptor::TYCbCrDescriptor: 266 return Some(aDescriptor.get_YCbCrDescriptor().chromaSubsampling()); 267 default: 268 MOZ_CRASH("GFX: ChromaSubsamplingFromBufferDescriptor"); 269 } 270 } 271 272 uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { 273 return aBuffer + aDescriptor.yOffset(); 274 } 275 276 uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { 277 return aBuffer + aDescriptor.cbOffset(); 278 } 279 280 uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { 281 return aBuffer + aDescriptor.crOffset(); 282 } 283 284 already_AddRefed<DataSourceSurface> DataSourceSurfaceFromYCbCrDescriptor( 285 uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, 286 gfx::DataSourceSurface* aSurface) { 287 const gfx::IntRect display = aDescriptor.display(); 288 const gfx::IntSize size = display.Size(); 289 RefPtr<DataSourceSurface> result; 290 if (aSurface) { 291 MOZ_ASSERT(aSurface->GetSize() == size); 292 MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8); 293 if (aSurface->GetSize() == size && 294 aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) { 295 result = aSurface; 296 } 297 } 298 299 if (!result) { 300 result = 301 Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8X8); 302 } 303 if (NS_WARN_IF(!result)) { 304 return nullptr; 305 } 306 307 DataSourceSurface::MappedSurface map; 308 if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) { 309 return nullptr; 310 } 311 312 layers::PlanarYCbCrData ycbcrData; 313 ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); 314 ycbcrData.mYStride = aDescriptor.yStride(); 315 ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); 316 ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); 317 ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); 318 ycbcrData.mPictureRect = aDescriptor.display(); 319 ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); 320 ycbcrData.mColorDepth = aDescriptor.colorDepth(); 321 ycbcrData.mChromaSubsampling = aDescriptor.chromaSubsampling(); 322 323 if (NS_WARN_IF(NS_FAILED( 324 gfx::ConvertYCbCrToRGB(ycbcrData, gfx::SurfaceFormat::B8G8R8X8, size, 325 map.mData, map.mStride)))) { 326 MOZ_ASSERT_UNREACHABLE("Failed to convert YUV into RGB data"); 327 return nullptr; 328 } 329 330 result->Unmap(); 331 return result.forget(); 332 } 333 334 void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer, 335 const YCbCrDescriptor& aDescriptor, 336 const gfx::SurfaceFormat& aDestFormat, 337 const gfx::IntSize& aDestSize, 338 unsigned char* aDestBuffer, 339 int32_t aStride) { 340 MOZ_ASSERT(aBuffer); 341 342 layers::PlanarYCbCrData ycbcrData; 343 ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); 344 ycbcrData.mYStride = aDescriptor.yStride(); 345 ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); 346 ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); 347 ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); 348 ycbcrData.mPictureRect = aDescriptor.display(); 349 ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); 350 ycbcrData.mColorDepth = aDescriptor.colorDepth(); 351 ycbcrData.mChromaSubsampling = aDescriptor.chromaSubsampling(); 352 353 DebugOnly<nsresult> result = gfx::ConvertYCbCrToRGB( 354 ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride); 355 MOZ_ASSERT(NS_SUCCEEDED(result), "Failed to convert YUV into RGB data"); 356 } 357 358 gfx::IntSize GetCroppedCbCrSize(const YCbCrDescriptor& aDescriptor) { 359 return ChromaSize(aDescriptor.display().Size(), 360 aDescriptor.chromaSubsampling()); 361 } 362 363 } // namespace ImageDataSerializer 364 } // namespace layers 365 } // namespace mozilla