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 // for uint8_t, uint32_t michael@0: #include "BasicLayers.h" // for BasicLayerManager michael@0: #include "ImageContainer.h" // for PlanarYCbCrImage, etc michael@0: #include "ImageTypes.h" // for ImageFormat, etc michael@0: #include "cairo.h" // for cairo_user_data_key_t michael@0: #include "gfxASurface.h" // for gfxASurface, etc michael@0: #include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat michael@0: #include "gfxUtils.h" // for gfxUtils michael@0: #include "mozilla/mozalloc.h" // for operator delete[], etc michael@0: #include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr michael@0: #include "nsAutoRef.h" // for nsCountedRef michael@0: #include "nsCOMPtr.h" // for already_AddRefed michael@0: #include "nsDebug.h" // for NS_ERROR, NS_ASSERTION michael@0: #include "nsISupportsImpl.h" // for Image::Release, etc michael@0: #include "nsThreadUtils.h" // for NS_IsMainThread michael@0: #include "mozilla/gfx/Point.h" // for IntSize michael@0: #include "gfx2DGlue.h" michael@0: #include "YCbCrUtils.h" // for YCbCr conversions michael@0: #ifdef XP_MACOSX michael@0: #include "gfxQuartzImageSurface.h" michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: class BasicPlanarYCbCrImage : public PlanarYCbCrImage michael@0: { michael@0: public: michael@0: BasicPlanarYCbCrImage(const gfx::IntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin) michael@0: : PlanarYCbCrImage(aRecycleBin) michael@0: , mScaleHint(aScaleHint) michael@0: , mDelayedConversion(false) michael@0: { michael@0: SetOffscreenFormat(aOffscreenFormat); michael@0: } michael@0: michael@0: ~BasicPlanarYCbCrImage() michael@0: { michael@0: if (mDecodedBuffer) { michael@0: // Right now this only happens if the Image was never drawn, otherwise michael@0: // this will have been tossed away at surface destruction. michael@0: mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride); michael@0: } michael@0: } michael@0: michael@0: virtual void SetData(const Data& aData); michael@0: virtual void SetDelayedConversion(bool aDelayed) { mDelayedConversion = aDelayed; } michael@0: michael@0: TemporaryRef GetAsSourceSurface(); michael@0: michael@0: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE michael@0: { michael@0: size_t size = PlanarYCbCrImage::SizeOfExcludingThis(aMallocSizeOf); michael@0: size += mDecodedBuffer.SizeOfExcludingThis(aMallocSizeOf); michael@0: return size; michael@0: } michael@0: michael@0: private: michael@0: nsAutoArrayPtr mDecodedBuffer; michael@0: gfx::IntSize mScaleHint; michael@0: int mStride; michael@0: bool mDelayedConversion; michael@0: }; michael@0: michael@0: class BasicImageFactory : public ImageFactory michael@0: { michael@0: public: michael@0: BasicImageFactory() {} michael@0: michael@0: virtual already_AddRefed CreateImage(ImageFormat aFormat, michael@0: const gfx::IntSize &aScaleHint, michael@0: BufferRecycleBin *aRecycleBin) michael@0: { michael@0: nsRefPtr image; michael@0: if (aFormat == ImageFormat::PLANAR_YCBCR) { michael@0: image = new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin); michael@0: return image.forget(); michael@0: } michael@0: michael@0: return ImageFactory::CreateImage(aFormat, aScaleHint, aRecycleBin); michael@0: } michael@0: }; michael@0: michael@0: void michael@0: BasicPlanarYCbCrImage::SetData(const Data& aData) michael@0: { michael@0: PlanarYCbCrImage::SetData(aData); michael@0: michael@0: if (mDelayedConversion) { michael@0: return; michael@0: } michael@0: michael@0: // Do some sanity checks to prevent integer overflow michael@0: if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION || michael@0: aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) { michael@0: NS_ERROR("Illegal image source width or height"); michael@0: return; michael@0: } michael@0: michael@0: gfx::SurfaceFormat format = gfx::ImageFormatToSurfaceFormat(GetOffscreenFormat()); michael@0: michael@0: gfx::IntSize size(mScaleHint); michael@0: gfx::GetYCbCrToRGBDestFormatAndSize(aData, format, size); michael@0: if (size.width > PlanarYCbCrImage::MAX_DIMENSION || michael@0: size.height > PlanarYCbCrImage::MAX_DIMENSION) { michael@0: NS_ERROR("Illegal image dest width or height"); michael@0: return; michael@0: } michael@0: michael@0: gfxImageFormat iFormat = gfx::SurfaceFormatToImageFormat(format); michael@0: mStride = gfxASurface::FormatStrideForWidth(iFormat, size.width); michael@0: mDecodedBuffer = AllocateBuffer(size.height * mStride); michael@0: if (!mDecodedBuffer) { michael@0: // out of memory michael@0: return; michael@0: } michael@0: michael@0: gfx::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer, mStride); michael@0: SetOffscreenFormat(iFormat); michael@0: mSize = size; michael@0: } michael@0: michael@0: TemporaryRef michael@0: BasicPlanarYCbCrImage::GetAsSourceSurface() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); michael@0: michael@0: if (mSourceSurface) { michael@0: return mSourceSurface.get(); michael@0: } michael@0: michael@0: if (!mDecodedBuffer) { michael@0: return PlanarYCbCrImage::GetAsSourceSurface(); michael@0: } michael@0: michael@0: gfxImageFormat format = GetOffscreenFormat(); michael@0: michael@0: RefPtr surface; michael@0: { michael@0: // Create a DrawTarget so that we can own the data inside mDecodeBuffer. michael@0: // We create the target out of mDecodedBuffer, and get a snapshot from it. michael@0: // The draw target is destroyed on scope exit and the surface owns the data. michael@0: RefPtr drawTarget michael@0: = gfxPlatform::GetPlatform()->CreateDrawTargetForData(mDecodedBuffer, michael@0: mSize, michael@0: mStride, michael@0: gfx::ImageFormatToSurfaceFormat(format)); michael@0: if (!drawTarget) { michael@0: return nullptr; michael@0: } michael@0: michael@0: surface = drawTarget->Snapshot(); michael@0: } michael@0: michael@0: mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride); michael@0: michael@0: mSourceSurface = surface; michael@0: return mSourceSurface.get(); michael@0: } michael@0: michael@0: michael@0: ImageFactory* michael@0: BasicLayerManager::GetImageFactory() michael@0: { michael@0: if (!mFactory) { michael@0: mFactory = new BasicImageFactory(); michael@0: } michael@0: michael@0: return mFactory.get(); michael@0: } michael@0: michael@0: } michael@0: }