/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ImageDataSerializer.h" #include "YCbCrUtils.h" // for YCbCr conversions #include "gfx2DGlue.h" // for SurfaceFormatToImageFormat #include "mozilla/Assertions.h" // for MOZ_ASSERT, etc #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory #include "mozilla/gfx/Logging.h" // for gfxDebug #include "mozilla/gfx/Tools.h" // for GetAlignedStride, etc #include "mozilla/gfx/Types.h" #include "mozilla/mozalloc.h" // for operator delete, etc namespace mozilla { namespace layers { namespace ImageDataSerializer { using namespace gfx; int32_t ComputeRGBStride(SurfaceFormat aFormat, int32_t aWidth) { #ifdef XP_MACOSX // Some drivers require an alignment of 32 bytes for efficient texture upload. return GetAlignedStride<32>(aWidth, BytesPerPixel(aFormat)); #else return GetAlignedStride<4>(aWidth, BytesPerPixel(aFormat)); #endif } int32_t GetRGBStride(const RGBDescriptor& aDescriptor) { return ComputeRGBStride(aDescriptor.format(), aDescriptor.size().width); } uint32_t ComputeRGBBufferSize(IntSize aSize, SurfaceFormat aFormat) { MOZ_ASSERT(aSize.height >= 0 && aSize.width >= 0); // This takes care of checking whether there could be overflow // with enough margin for the metadata. if (!gfx::Factory::AllowedSurfaceSize(aSize)) { return 0; } // Note we're passing height instad of the bpp parameter, but the end // result is the same - and the bpp was already taken care of in the // ComputeRGBStride function. int32_t bufsize = GetAlignedStride<16>(ComputeRGBStride(aFormat, aSize.width), aSize.height); if (bufsize < 0) { // This should not be possible thanks to Factory::AllowedSurfaceSize return 0; } return bufsize; } // Minimum required shmem size in bytes uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, const gfx::IntSize& aCbCrSize, int32_t aCbCrStride) { MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 || !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || !gfx::Factory::AllowedSurfaceSize( IntSize(aCbCrStride, aCbCrSize.height))) { return 0; } // Overflow checks are performed in AllowedSurfaceSize return GetAlignedStride<4>(aYSize.height, aYStride) + 2 * GetAlignedStride<4>(aCbCrSize.height, aCbCrStride); } uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aYStride, const gfx::IntSize& aCbCrSize, int32_t aCbCrStride, uint32_t aYOffset, uint32_t aCbOffset, uint32_t aCrOffset) { MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 || !gfx::Factory::AllowedSurfaceSize(IntSize(aYStride, aYSize.height)) || !gfx::Factory::AllowedSurfaceSize( IntSize(aCbCrStride, aCbCrSize.height))) { return 0; } uint32_t yLength = GetAlignedStride<4>(aYStride, aYSize.height); uint32_t cbCrLength = GetAlignedStride<4>(aCbCrStride, aCbCrSize.height); if (yLength == 0 || cbCrLength == 0) { return 0; } CheckedInt yEnd = aYOffset; yEnd += yLength; CheckedInt cbEnd = aCbOffset; cbEnd += cbCrLength; CheckedInt crEnd = aCrOffset; crEnd += cbCrLength; if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() || yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) { return 0; } return crEnd.value(); } uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize) { return GetAlignedStride<4>(aBufferSize, 1); } void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, int32_t cbCrStride, int32_t cbCrHeight, uint32_t& outYOffset, uint32_t& outCbOffset, uint32_t& outCrOffset) { outYOffset = 0; outCbOffset = outYOffset + GetAlignedStride<4>(yStride, yHeight); outCrOffset = outCbOffset + GetAlignedStride<4>(cbCrStride, cbCrHeight); } gfx::SurfaceFormat FormatFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return aDescriptor.get_RGBDescriptor().format(); case BufferDescriptor::TYCbCrDescriptor: return gfx::SurfaceFormat::YUV; default: MOZ_CRASH("GFX: FormatFromBufferDescriptor"); } } gfx::IntSize SizeFromBufferDescriptor(const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return aDescriptor.get_RGBDescriptor().size(); case BufferDescriptor::TYCbCrDescriptor: { return aDescriptor.get_YCbCrDescriptor().display().Size(); } default: MOZ_CRASH("GFX: SizeFromBufferDescriptor"); } } gfx::IntRect RectFromBufferDescriptor(const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: { auto size = aDescriptor.get_RGBDescriptor().size(); return gfx::IntRect(0, 0, size.Width(), size.Height()); } case BufferDescriptor::TYCbCrDescriptor: return aDescriptor.get_YCbCrDescriptor().display(); default: MOZ_CRASH("GFX: RectFromBufferDescriptor"); } } Maybe YSizeFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().ySize()); default: MOZ_CRASH("GFX: YSizeFromBufferDescriptor"); } } Maybe CbCrSizeFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().cbCrSize()); default: MOZ_CRASH("GFX: CbCrSizeFromBufferDescriptor"); } } Maybe YStrideFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().yStride()); default: MOZ_CRASH("GFX: YStrideFromBufferDescriptor"); } } Maybe CbCrStrideFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().cbCrStride()); default: MOZ_CRASH("GFX: CbCrStrideFromBufferDescriptor"); } } Maybe YUVColorSpaceFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().yUVColorSpace()); default: MOZ_CRASH("GFX: YUVColorSpaceFromBufferDescriptor"); } } Maybe ColorDepthFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().colorDepth()); default: MOZ_CRASH("GFX: ColorDepthFromBufferDescriptor"); } } Maybe ColorRangeFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().colorRange()); default: MOZ_CRASH("GFX: YUVFullRangeFromBufferDescriptor"); } } Maybe StereoModeFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().stereoMode()); default: MOZ_CRASH("GFX: StereoModeFromBufferDescriptor"); } } Maybe ChromaSubsamplingFromBufferDescriptor( const BufferDescriptor& aDescriptor) { switch (aDescriptor.type()) { case BufferDescriptor::TRGBDescriptor: return Nothing(); case BufferDescriptor::TYCbCrDescriptor: return Some(aDescriptor.get_YCbCrDescriptor().chromaSubsampling()); default: MOZ_CRASH("GFX: ChromaSubsamplingFromBufferDescriptor"); } } uint8_t* GetYChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { return aBuffer + aDescriptor.yOffset(); } uint8_t* GetCbChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { return aBuffer + aDescriptor.cbOffset(); } uint8_t* GetCrChannel(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor) { return aBuffer + aDescriptor.crOffset(); } already_AddRefed DataSourceSurfaceFromYCbCrDescriptor( uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, gfx::DataSourceSurface* aSurface) { const gfx::IntRect display = aDescriptor.display(); const gfx::IntSize size = display.Size(); RefPtr result; if (aSurface) { MOZ_ASSERT(aSurface->GetSize() == size); MOZ_ASSERT(aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8); if (aSurface->GetSize() == size && aSurface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8) { result = aSurface; } } if (!result) { result = Factory::CreateDataSourceSurface(size, gfx::SurfaceFormat::B8G8R8X8); } if (NS_WARN_IF(!result)) { return nullptr; } DataSourceSurface::MappedSurface map; if (NS_WARN_IF(!result->Map(DataSourceSurface::MapType::WRITE, &map))) { return nullptr; } layers::PlanarYCbCrData ycbcrData; ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); ycbcrData.mYStride = aDescriptor.yStride(); ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); ycbcrData.mPictureRect = aDescriptor.display(); ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); ycbcrData.mColorDepth = aDescriptor.colorDepth(); ycbcrData.mChromaSubsampling = aDescriptor.chromaSubsampling(); gfx::ConvertYCbCrToRGB(ycbcrData, gfx::SurfaceFormat::B8G8R8X8, size, map.mData, map.mStride); result->Unmap(); return result.forget(); } void ConvertAndScaleFromYCbCrDescriptor(uint8_t* aBuffer, const YCbCrDescriptor& aDescriptor, const gfx::SurfaceFormat& aDestFormat, const gfx::IntSize& aDestSize, unsigned char* aDestBuffer, int32_t aStride) { MOZ_ASSERT(aBuffer); layers::PlanarYCbCrData ycbcrData; ycbcrData.mYChannel = GetYChannel(aBuffer, aDescriptor); ycbcrData.mYStride = aDescriptor.yStride(); ycbcrData.mCbChannel = GetCbChannel(aBuffer, aDescriptor); ycbcrData.mCrChannel = GetCrChannel(aBuffer, aDescriptor); ycbcrData.mCbCrStride = aDescriptor.cbCrStride(); ycbcrData.mPictureRect = aDescriptor.display(); ycbcrData.mYUVColorSpace = aDescriptor.yUVColorSpace(); ycbcrData.mColorDepth = aDescriptor.colorDepth(); ycbcrData.mChromaSubsampling = aDescriptor.chromaSubsampling(); gfx::ConvertYCbCrToRGB(ycbcrData, aDestFormat, aDestSize, aDestBuffer, aStride); } gfx::IntSize GetCroppedCbCrSize(const YCbCrDescriptor& aDescriptor) { return ChromaSize(aDescriptor.display().Size(), aDescriptor.chromaSubsampling()); } } // namespace ImageDataSerializer } // namespace layers } // namespace mozilla