/* -*- 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 "mozilla/ipc/Shmem.h" #include "mozilla/ipc/CrossProcessMutex.h" #include "ImageLayers.h" #include "gfxImageSurface.h" #include "yuv_convert.h" #ifdef XP_MACOSX #include "nsCoreAnimationSupport.h" #endif #ifdef XP_WIN #include "gfxD2DSurface.h" #include "gfxWindowsPlatform.h" #include #include "d3d10/ImageLayerD3D10.h" #endif using namespace mozilla::ipc; namespace mozilla { namespace layers { already_AddRefed ImageFactory::CreateImage(const Image::Format *aFormats, PRUint32 aNumFormats, const gfxIntSize &, BufferRecycleBin *aRecycleBin) { if (!aNumFormats) { return nsnull; } nsRefPtr img; if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) { img = new PlanarYCbCrImage(aRecycleBin); } else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) { img = new CairoImage(); #ifdef XP_MACOSX } else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) { img = new MacIOSurfaceImage(); #endif } return img.forget(); } BufferRecycleBin::BufferRecycleBin() : mLock("mozilla.layers.BufferRecycleBin.mLock") { } void BufferRecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize) { MutexAutoLock lock(mLock); if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) { mRecycledBuffers.Clear(); } mRecycledBufferSize = aSize; mRecycledBuffers.AppendElement(aBuffer); } PRUint8* BufferRecycleBin::GetBuffer(PRUint32 aSize) { MutexAutoLock lock(mLock); if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) return new PRUint8[aSize]; PRUint32 last = mRecycledBuffers.Length() - 1; PRUint8* result = mRecycledBuffers[last].forget(); mRecycledBuffers.RemoveElementAt(last); return result; } ImageContainer::~ImageContainer() { } already_AddRefed ImageContainer::CreateImage(const Image::Format *aFormats, PRUint32 aNumFormats) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin); } void ImageContainer::SetCurrentImage(Image *aImage) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!"); mRemoteDataMutex->Lock(); // This is important since it ensures we won't change the active image // when we currently have a locked image that depends on mRemoteData. } mActiveImage = aImage; CurrentImageChanged(); if (mRemoteData) { mRemoteDataMutex->Unlock(); } } bool ImageContainer::HasCurrentImage() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex); EnsureActiveImage(); return !!mActiveImage.get(); } return !!mActiveImage.get(); } already_AddRefed ImageContainer::LockCurrentImage() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!"); mRemoteDataMutex->Lock(); } EnsureActiveImage(); nsRefPtr retval = mActiveImage; return retval.forget(); } already_AddRefed ImageContainer::LockCurrentAsSurface(gfxIntSize *aSize, Image** aCurrentImage) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!"); mRemoteDataMutex->Lock(); EnsureActiveImage(); if (aCurrentImage) { NS_IF_ADDREF(mActiveImage); *aCurrentImage = mActiveImage.get(); } if (!mActiveImage) { return nsnull; } if (mActiveImage->GetFormat() == Image::REMOTE_IMAGE_BITMAP) { nsRefPtr newSurf = new gfxImageSurface(mRemoteData->mBitmap.mData, mRemoteData->mSize, mRemoteData->mBitmap.mStride, mRemoteData->mFormat == RemoteImageData::BGRX32 ? gfxASurface::ImageFormatARGB32 : gfxASurface::ImageFormatRGB24); *aSize = newSurf->GetSize(); return newSurf.forget(); } *aSize = mActiveImage->GetSize(); return mActiveImage->GetAsSurface(); } if (aCurrentImage) { NS_IF_ADDREF(mActiveImage); *aCurrentImage = mActiveImage.get(); } if (!mActiveImage) { return nsnull; } *aSize = mActiveImage->GetSize(); return mActiveImage->GetAsSurface(); } void ImageContainer::UnlockCurrentImage() { if (mRemoteData) { NS_ASSERTION(mRemoteDataMutex, "Should have remote data mutex when having remote data!"); mRemoteDataMutex->Unlock(); } } already_AddRefed ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex); EnsureActiveImage(); if (!mActiveImage) return nsnull; *aSize = mRemoteData->mSize; } else { if (!mActiveImage) return nsnull; *aSize = mActiveImage->GetSize(); } return mActiveImage->GetAsSurface(); } gfxIntSize ImageContainer::GetCurrentSize() { ReentrantMonitorAutoEnter mon(mReentrantMonitor); if (mRemoteData) { CrossProcessMutexAutoLock autoLock(*mRemoteDataMutex); // We don't need to ensure we have an active image here, as we need to // be in the mutex anyway, and this is easiest to return from there. return mRemoteData->mSize; } if (!mActiveImage) { return gfxIntSize(0,0); } return mActiveImage->GetSize(); } void ImageContainer::SetRemoteImageData(RemoteImageData *aData, CrossProcessMutex *aMutex) { ReentrantMonitorAutoEnter mon(mReentrantMonitor); NS_ASSERTION(!mActiveImage || !aData, "No active image expected when SetRemoteImageData is called with non-NULL aData."); NS_ASSERTION(!mRemoteData || !aData, "No remote data expected when SetRemoteImageData is called with non-NULL aData."); mRemoteData = aData; if (aData) { memset(aData, 0, sizeof(RemoteImageData)); } else { mActiveImage = nsnull; } mRemoteDataMutex = aMutex; } void ImageContainer::EnsureActiveImage() { if (mRemoteData) { if (mRemoteData->mWasUpdated) { mActiveImage = nsnull; } if (mRemoteData->mType == RemoteImageData::RAW_BITMAP && mRemoteData->mBitmap.mData && !mActiveImage) { nsRefPtr newImg = new RemoteBitmapImage(); newImg->mFormat = mRemoteData->mFormat; newImg->mData = mRemoteData->mBitmap.mData; newImg->mSize = mRemoteData->mSize; newImg->mStride = mRemoteData->mBitmap.mStride; mRemoteData->mWasUpdated = false; mActiveImage = newImg; } #ifdef XP_WIN else if (mRemoteData->mType == RemoteImageData::DXGI_TEXTURE_HANDLE && mRemoteData->mTextureHandle && !mActiveImage) { nsRefPtr newImg = new RemoteDXGITextureImage(); newImg->mSize = mRemoteData->mSize; newImg->mHandle = mRemoteData->mTextureHandle; newImg->mFormat = mRemoteData->mFormat; mRemoteData->mWasUpdated = false; mActiveImage = newImg; } #endif } } PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin) : Image(nsnull, PLANAR_YCBCR) , mBufferSize(0) , mRecycleBin(aRecycleBin) { } PlanarYCbCrImage::~PlanarYCbCrImage() { if (mBuffer) { mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize); } } PRUint8* PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize) { return mRecycleBin->GetBuffer(aSize); } static void CopyPlane(PRUint8 *aDst, PRUint8 *aSrc, const gfxIntSize &aSize, PRInt32 aStride, PRInt32 aOffset, PRInt32 aSkip) { if (!aOffset && !aSkip) { // Fast path: planar input. memcpy(aDst, aSrc, aSize.height * aStride); } else { PRInt32 height = aSize.height; PRInt32 width = aSize.width; for (int y = 0; y < height; ++y) { PRUint8 *src = aSrc + aOffset; PRUint8 *dst = aDst; if (!aSkip) { // Fast path: offset only, no per-pixel skip. memcpy(dst, src, width); } else { // Slow path for (int x = 0; x < width; ++x) { *dst++ = *src++; src += aSkip; } } aSrc += aStride; aDst += aStride; } } } void PlanarYCbCrImage::CopyData(const Data& aData, PRInt32 aYOffset, PRInt32 aYSkip, PRInt32 aCbOffset, PRInt32 aCbSkip, PRInt32 aCrOffset, PRInt32 aCrSkip) { mData = aData; // update buffer size mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 + mData.mYStride * mData.mYSize.height; // get new buffer mBuffer = AllocateBuffer(mBufferSize); if (!mBuffer) return; mData.mYChannel = mBuffer; mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height; mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height; CopyPlane(mData.mYChannel, aData.mYChannel, mData.mYSize, mData.mYStride, aYOffset, aYSkip); CopyPlane(mData.mCbChannel, aData.mCbChannel, mData.mCbCrSize, mData.mCbCrStride, aCbOffset, aCbSkip); CopyPlane(mData.mCrChannel, aData.mCrChannel, mData.mCbCrSize, mData.mCbCrStride, aCrOffset, aCrSkip); mSize = aData.mPicSize; } void PlanarYCbCrImage::SetData(const Data &aData) { CopyData(aData); } already_AddRefed PlanarYCbCrImage::GetAsSurface() { if (mSurface) { nsRefPtr result = mSurface.get(); return result.forget(); } nsRefPtr imageSurface = new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24); gfx::YUVType type = gfx::TypeFromSize(mData.mYSize.width, mData.mYSize.height, mData.mCbCrSize.width, mData.mCbCrSize.height); // Convert from YCbCr to RGB now gfx::ConvertYCbCrToRGB32(mData.mYChannel, mData.mCbChannel, mData.mCrChannel, imageSurface->Data(), mData.mPicX, mData.mPicY, mData.mPicSize.width, mData.mPicSize.height, mData.mYStride, mData.mCbCrStride, imageSurface->Stride(), type); mSurface = imageSurface; return imageSurface.forget().get(); } #ifdef XP_MACOSX void MacIOSurfaceImage::SetData(const Data& aData) { mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID()); mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight()); } already_AddRefed MacIOSurfaceImage::GetAsSurface() { return mIOSurface->GetAsSurface(); } void MacIOSurfaceImage::Update(ImageContainer* aContainer) { if (mUpdateCallback) { mUpdateCallback(aContainer, mPluginInstanceOwner); } } #endif already_AddRefed RemoteBitmapImage::GetAsSurface() { nsRefPtr newSurf = new gfxImageSurface(mSize, mFormat == RemoteImageData::BGRX32 ? gfxASurface::ImageFormatRGB24 : gfxASurface::ImageFormatARGB32); for (int y = 0; y < mSize.height; y++) { memcpy(newSurf->Data() + newSurf->Stride() * y, mData + mStride * y, mSize.width * 4); } return newSurf.forget(); } } }