/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * 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 // for uint8_t, uint32_t #include "BasicLayers.h" // for BasicLayerManager #include "ImageContainer.h" // for PlanarYCbCrImage, etc #include "ImageTypes.h" // for ImageFormat, etc #include "cairo.h" // for cairo_user_data_key_t #include "gfxASurface.h" // for gfxASurface, etc #include "gfxImageSurface.h" // for gfxImageSurface #include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat #include "gfxPoint.h" // for gfxIntSize #include "gfxUtils.h" // for gfxUtils #include "mozilla/mozalloc.h" // for operator delete[], etc #include "nsAutoPtr.h" // for nsRefPtr, nsAutoArrayPtr #include "nsAutoRef.h" // for nsCountedRef #include "nsCOMPtr.h" // for already_AddRefed #include "nsDebug.h" // for NS_ERROR, NS_ASSERTION #include "nsISupportsImpl.h" // for Image::Release, etc #include "nsThreadUtils.h" // for NS_IsMainThread #ifdef XP_MACOSX #include "gfxQuartzImageSurface.h" #endif namespace mozilla { namespace layers { class BasicPlanarYCbCrImage : public PlanarYCbCrImage { public: BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin) : PlanarYCbCrImage(aRecycleBin) , mScaleHint(aScaleHint) , mDelayedConversion(false) { SetOffscreenFormat(aOffscreenFormat); } ~BasicPlanarYCbCrImage() { if (mDecodedBuffer) { // Right now this only happens if the Image was never drawn, otherwise // this will have been tossed away at surface destruction. mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride); } } virtual void SetData(const Data& aData); virtual void SetDelayedConversion(bool aDelayed) { mDelayedConversion = aDelayed; } already_AddRefed GetAsSurface(); private: nsAutoArrayPtr mDecodedBuffer; gfxIntSize mScaleHint; int mStride; bool mDelayedConversion; }; class BasicImageFactory : public ImageFactory { public: BasicImageFactory() {} virtual already_AddRefed CreateImage(const ImageFormat* aFormats, uint32_t aNumFormats, const gfxIntSize &aScaleHint, BufferRecycleBin *aRecycleBin) { if (!aNumFormats) { return nullptr; } nsRefPtr image; if (aFormats[0] == PLANAR_YCBCR) { image = new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin); return image.forget(); } return ImageFactory::CreateImage(aFormats, aNumFormats, aScaleHint, aRecycleBin); } }; void BasicPlanarYCbCrImage::SetData(const Data& aData) { PlanarYCbCrImage::SetData(aData); if (mDelayedConversion) { return; } // Do some sanity checks to prevent integer overflow if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION || aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image source width or height"); return; } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); gfxIntSize size(mScaleHint); gfxUtils::GetYCbCrToRGBDestFormatAndSize(aData, format, size); if (size.width > PlanarYCbCrImage::MAX_DIMENSION || size.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image dest width or height"); return; } mStride = gfxASurface::FormatStrideForWidth(format, size.width); mDecodedBuffer = AllocateBuffer(size.height * mStride); if (!mDecodedBuffer) { // out of memory return; } gfxUtils::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer, mStride); SetOffscreenFormat(format); mSize = size; } static cairo_user_data_key_t imageSurfaceDataKey; static void DestroyBuffer(void* aBuffer) { delete[] static_cast(aBuffer); } already_AddRefed BasicPlanarYCbCrImage::GetAsSurface() { NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); if (mSurface) { nsRefPtr result = mSurface.get(); return result.forget(); } if (!mDecodedBuffer) { return PlanarYCbCrImage::GetAsSurface(); } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); nsRefPtr imgSurface = new gfxImageSurface(mDecodedBuffer, mSize, mStride, format); if (!imgSurface || imgSurface->CairoStatus() != 0) { return nullptr; } // Pass ownership of the buffer to the surface imgSurface->SetData(&imageSurfaceDataKey, mDecodedBuffer.forget(), DestroyBuffer); nsRefPtr result = imgSurface.get(); #if defined(XP_MACOSX) nsRefPtr quartzSurface = new gfxQuartzImageSurface(imgSurface); if (quartzSurface) { result = quartzSurface.forget(); } #endif mSurface = result; return result.forget(); } ImageFactory* BasicLayerManager::GetImageFactory() { if (!mFactory) { mFactory = new BasicImageFactory(); } return mFactory.get(); } } }