michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/layers/YCbCrImageDataSerializer.h" michael@0: #include // for memcpy michael@0: #include "gfx2DGlue.h" // for ToIntSize michael@0: #include "mozilla/gfx/2D.h" // for DataSourceSurface, Factory michael@0: #include "mozilla/gfx/BaseSize.h" // for BaseSize michael@0: #include "mozilla/gfx/Types.h" michael@0: #include "mozilla/mozalloc.h" // for operator delete michael@0: #include "yuv_convert.h" // for ConvertYCbCrToRGB32, etc michael@0: michael@0: #define MOZ_ALIGN_WORD(x) (((x) + 3) & ~3) michael@0: michael@0: namespace mozilla { michael@0: michael@0: using namespace gfx; michael@0: michael@0: namespace layers { michael@0: michael@0: // The Data is layed out as follows: michael@0: // michael@0: // +-----------------+ -++ --+ --+ <-- Beginning of the buffer michael@0: // | YCbCrBufferInfo | | | | michael@0: // +-----------------+ --+ | | michael@0: // | data | | | YCbCrBufferInfo->[mY/mCb/mCr]Offset michael@0: // +-----------------+ ------+ | michael@0: // | data | | michael@0: // +-----------------+ ----------+ michael@0: // | data | michael@0: // +-----------------+ michael@0: // michael@0: // There can be padding between the blocks above to keep word alignment. michael@0: michael@0: // Structure written at the beginning og the data blob containing the image michael@0: // (as shown in the figure above). It contains the necessary informations to michael@0: // read the image in the blob. michael@0: struct YCbCrBufferInfo michael@0: { michael@0: uint32_t mYOffset; michael@0: uint32_t mCbOffset; michael@0: uint32_t mCrOffset; michael@0: uint32_t mYStride; michael@0: uint32_t mYWidth; michael@0: uint32_t mYHeight; michael@0: uint32_t mCbCrStride; michael@0: uint32_t mCbCrWidth; michael@0: uint32_t mCbCrHeight; michael@0: StereoMode mStereoMode; michael@0: }; michael@0: michael@0: static YCbCrBufferInfo* GetYCbCrBufferInfo(uint8_t* aData, size_t aDataSize) michael@0: { michael@0: return aDataSize >= sizeof(YCbCrBufferInfo) michael@0: ? reinterpret_cast(aData) michael@0: : nullptr; michael@0: } michael@0: michael@0: void YCbCrImageDataDeserializerBase::Validate() michael@0: { michael@0: mIsValid = false; michael@0: if (!mData) { michael@0: return; michael@0: } michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: if (!info) { michael@0: return; michael@0: } michael@0: size_t requiredSize = ComputeMinBufferSize( michael@0: IntSize(info->mYWidth, info->mYHeight), michael@0: info->mYStride, michael@0: IntSize(info->mCbCrWidth, info->mCbCrHeight), michael@0: info->mCbCrStride); michael@0: mIsValid = requiredSize <= mDataSize; michael@0: michael@0: } michael@0: michael@0: uint8_t* YCbCrImageDataDeserializerBase::GetYData() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return reinterpret_cast(info) + info->mYOffset; michael@0: } michael@0: michael@0: uint8_t* YCbCrImageDataDeserializerBase::GetCbData() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return reinterpret_cast(info) + info->mCbOffset; michael@0: } michael@0: michael@0: uint8_t* YCbCrImageDataDeserializerBase::GetCrData() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return reinterpret_cast(info) + info->mCrOffset; michael@0: } michael@0: michael@0: uint8_t* YCbCrImageDataDeserializerBase::GetData() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return (reinterpret_cast(info)) + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo)); michael@0: } michael@0: michael@0: uint32_t YCbCrImageDataDeserializerBase::GetYStride() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return info->mYStride; michael@0: } michael@0: michael@0: uint32_t YCbCrImageDataDeserializerBase::GetCbCrStride() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return info->mCbCrStride; michael@0: } michael@0: michael@0: gfx::IntSize YCbCrImageDataDeserializerBase::GetYSize() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return gfx::IntSize(info->mYWidth, info->mYHeight); michael@0: } michael@0: michael@0: gfx::IntSize YCbCrImageDataDeserializerBase::GetCbCrSize() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return gfx::IntSize(info->mCbCrWidth, info->mCbCrHeight); michael@0: } michael@0: michael@0: StereoMode YCbCrImageDataDeserializerBase::GetStereoMode() michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: return info->mStereoMode; michael@0: } michael@0: michael@0: // Offset in bytes michael@0: static size_t ComputeOffset(uint32_t aHeight, uint32_t aStride) michael@0: { michael@0: return MOZ_ALIGN_WORD(aHeight * aStride); michael@0: } michael@0: michael@0: // Minimum required shmem size in bytes michael@0: size_t michael@0: YCbCrImageDataDeserializerBase::ComputeMinBufferSize(const gfx::IntSize& aYSize, michael@0: uint32_t aYStride, michael@0: const gfx::IntSize& aCbCrSize, michael@0: uint32_t aCbCrStride) michael@0: { michael@0: return ComputeOffset(aYSize.height, aYStride) michael@0: + 2 * ComputeOffset(aCbCrSize.height, aCbCrStride) michael@0: + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo)); michael@0: } michael@0: michael@0: // Minimum required shmem size in bytes michael@0: size_t michael@0: YCbCrImageDataDeserializerBase::ComputeMinBufferSize(const gfx::IntSize& aYSize, michael@0: const gfx::IntSize& aCbCrSize) michael@0: { michael@0: return ComputeMinBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width); michael@0: } michael@0: michael@0: // Offset in bytes michael@0: static size_t ComputeOffset(uint32_t aSize) michael@0: { michael@0: return MOZ_ALIGN_WORD(aSize); michael@0: } michael@0: michael@0: // Minimum required shmem size in bytes michael@0: size_t michael@0: YCbCrImageDataDeserializerBase::ComputeMinBufferSize(uint32_t aSize) michael@0: { michael@0: return ComputeOffset(aSize) + MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo)); michael@0: } michael@0: michael@0: void michael@0: YCbCrImageDataSerializer::InitializeBufferInfo(uint32_t aYOffset, michael@0: uint32_t aCbOffset, michael@0: uint32_t aCrOffset, michael@0: uint32_t aYStride, michael@0: uint32_t aCbCrStride, michael@0: const gfx::IntSize& aYSize, michael@0: const gfx::IntSize& aCbCrSize, michael@0: StereoMode aStereoMode) michael@0: { michael@0: YCbCrBufferInfo* info = GetYCbCrBufferInfo(mData, mDataSize); michael@0: MOZ_ASSERT(info); // OK to assert here, this method is client-side-only michael@0: uint32_t info_size = MOZ_ALIGN_WORD(sizeof(YCbCrBufferInfo)); michael@0: info->mYOffset = info_size + aYOffset; michael@0: info->mCbOffset = info_size + aCbOffset; michael@0: info->mCrOffset = info_size + aCrOffset; michael@0: info->mYStride = aYStride; michael@0: info->mYWidth = aYSize.width; michael@0: info->mYHeight = aYSize.height; michael@0: info->mCbCrStride = aCbCrStride; michael@0: info->mCbCrWidth = aCbCrSize.width; michael@0: info->mCbCrHeight = aCbCrSize.height; michael@0: info->mStereoMode = aStereoMode; michael@0: Validate(); michael@0: } michael@0: michael@0: void michael@0: YCbCrImageDataSerializer::InitializeBufferInfo(uint32_t aYStride, michael@0: uint32_t aCbCrStride, michael@0: const gfx::IntSize& aYSize, michael@0: const gfx::IntSize& aCbCrSize, michael@0: StereoMode aStereoMode) michael@0: { michael@0: uint32_t yOffset = 0; michael@0: uint32_t cbOffset = yOffset + MOZ_ALIGN_WORD(aYStride * aYSize.height); michael@0: uint32_t crOffset = cbOffset + MOZ_ALIGN_WORD(aCbCrStride * aCbCrSize.height); michael@0: return InitializeBufferInfo(yOffset, cbOffset, crOffset, michael@0: aYStride, aCbCrStride, aYSize, aCbCrSize, aStereoMode); michael@0: } michael@0: michael@0: void michael@0: YCbCrImageDataSerializer::InitializeBufferInfo(const gfx::IntSize& aYSize, michael@0: const gfx::IntSize& aCbCrSize, michael@0: StereoMode aStereoMode) michael@0: { michael@0: return InitializeBufferInfo(aYSize.width, aCbCrSize.width, aYSize, aCbCrSize, aStereoMode); michael@0: } michael@0: michael@0: static void CopyLineWithSkip(const uint8_t* src, uint8_t* dst, uint32_t len, uint32_t skip) { michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: *dst = *src; michael@0: src += 1 + skip; michael@0: ++dst; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: YCbCrImageDataSerializer::CopyData(const uint8_t* aYData, michael@0: const uint8_t* aCbData, const uint8_t* aCrData, michael@0: gfx::IntSize aYSize, uint32_t aYStride, michael@0: gfx::IntSize aCbCrSize, uint32_t aCbCrStride, michael@0: uint32_t aYSkip, uint32_t aCbCrSkip) michael@0: { michael@0: if (!IsValid() || GetYSize() != aYSize || GetCbCrSize() != aCbCrSize) { michael@0: return false; michael@0: } michael@0: for (int i = 0; i < aYSize.height; ++i) { michael@0: if (aYSkip == 0) { michael@0: // fast path michael@0: memcpy(GetYData() + i * GetYStride(), michael@0: aYData + i * aYStride, michael@0: aYSize.width); michael@0: } else { michael@0: // slower path michael@0: CopyLineWithSkip(aYData + i * aYStride, michael@0: GetYData() + i * GetYStride(), michael@0: aYSize.width, aYSkip); michael@0: } michael@0: } michael@0: for (int i = 0; i < aCbCrSize.height; ++i) { michael@0: if (aCbCrSkip == 0) { michael@0: // fast path michael@0: memcpy(GetCbData() + i * GetCbCrStride(), michael@0: aCbData + i * aCbCrStride, michael@0: aCbCrSize.width); michael@0: memcpy(GetCrData() + i * GetCbCrStride(), michael@0: aCrData + i * aCbCrStride, michael@0: aCbCrSize.width); michael@0: } else { michael@0: // slower path michael@0: CopyLineWithSkip(aCbData + i * aCbCrStride, michael@0: GetCbData() + i * GetCbCrStride(), michael@0: aCbCrSize.width, aCbCrSkip); michael@0: CopyLineWithSkip(aCrData + i * aCbCrStride, michael@0: GetCrData() + i * GetCbCrStride(), michael@0: aCbCrSize.width, aCbCrSkip); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: YCbCrImageDataDeserializer::ToDataSourceSurface() michael@0: { michael@0: RefPtr result = michael@0: Factory::CreateDataSourceSurface(GetYSize(), gfx::SurfaceFormat::B8G8R8X8); michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: result->Map(DataSourceSurface::MapType::WRITE, &map); michael@0: michael@0: gfx::ConvertYCbCrToRGB32(GetYData(), GetCbData(), GetCrData(), michael@0: map.mData, michael@0: 0, 0, //pic x and y michael@0: GetYSize().width, GetYSize().height, michael@0: GetYStride(), GetCbCrStride(), michael@0: map.mStride, michael@0: gfx::YV12); michael@0: result->Unmap(); michael@0: return result.forget(); michael@0: } michael@0: michael@0: michael@0: } // namespace michael@0: } // namespace