michael@0: /* -*- Mode: C++; tab-width: 2; 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 "BasicLayersImpl.h" // for FillWithMask, etc michael@0: #include "CopyableCanvasLayer.h" michael@0: #include "GLContext.h" // for GLContext michael@0: #include "GLScreenBuffer.h" // for GLScreenBuffer michael@0: #include "SharedSurface.h" // for SharedSurface michael@0: #include "SharedSurfaceGL.h" // for SharedSurface_GL, etc michael@0: #include "SurfaceTypes.h" // for APITypeT, APITypeT::OpenGL, etc michael@0: #include "gfxMatrix.h" // for gfxMatrix michael@0: #include "gfxPattern.h" // for gfxPattern, etc michael@0: #include "gfxPlatform.h" // for gfxPlatform, gfxImageFormat michael@0: #include "gfxRect.h" // for gfxRect michael@0: #include "gfxUtils.h" // for gfxUtils michael@0: #include "gfx2DGlue.h" // for thebes --> moz2d transition michael@0: #include "mozilla/gfx/BaseSize.h" // for BaseSize michael@0: #include "nsDebug.h" // for NS_ASSERTION, NS_WARNING, etc michael@0: #include "nsISupportsImpl.h" // for gfxContext::AddRef, etc michael@0: #include "nsRect.h" // for nsIntRect michael@0: #include "nsSize.h" // for nsIntSize michael@0: #include "LayerUtils.h" michael@0: michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::gl; michael@0: michael@0: namespace mozilla { michael@0: namespace layers { michael@0: michael@0: CopyableCanvasLayer::CopyableCanvasLayer(LayerManager* aLayerManager, void *aImplData) : michael@0: CanvasLayer(aLayerManager, aImplData) michael@0: , mStream(nullptr) michael@0: { michael@0: MOZ_COUNT_CTOR(CopyableCanvasLayer); michael@0: } michael@0: michael@0: CopyableCanvasLayer::~CopyableCanvasLayer() michael@0: { michael@0: MOZ_COUNT_DTOR(CopyableCanvasLayer); michael@0: } michael@0: michael@0: void michael@0: CopyableCanvasLayer::Initialize(const Data& aData) michael@0: { michael@0: NS_ASSERTION(mSurface == nullptr, "BasicCanvasLayer::Initialize called twice!"); michael@0: michael@0: if (aData.mGLContext) { michael@0: mGLContext = aData.mGLContext; michael@0: mStream = aData.mStream; michael@0: mIsGLAlphaPremult = aData.mIsGLAlphaPremult; michael@0: mNeedsYFlip = true; michael@0: MOZ_ASSERT(mGLContext->IsOffscreen(), "canvas gl context isn't offscreen"); michael@0: michael@0: // [Basic Layers, non-OMTC] WebGL layer init. michael@0: // `GLScreenBuffer::Morph`ing is only needed in BasicShadowableCanvasLayer. michael@0: } else if (aData.mDrawTarget) { michael@0: mDrawTarget = aData.mDrawTarget; michael@0: mSurface = mDrawTarget->Snapshot(); michael@0: mNeedsYFlip = false; michael@0: } else { michael@0: NS_ERROR("CanvasLayer created without mSurface, mDrawTarget or mGLContext?"); michael@0: } michael@0: michael@0: mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); michael@0: } michael@0: michael@0: bool michael@0: CopyableCanvasLayer::IsDataValid(const Data& aData) michael@0: { michael@0: return mGLContext == aData.mGLContext && mStream == aData.mStream; michael@0: } michael@0: michael@0: void michael@0: CopyableCanvasLayer::UpdateTarget(DrawTarget* aDestTarget) michael@0: { michael@0: if (!IsDirty()) michael@0: return; michael@0: Painted(); michael@0: michael@0: if (mDrawTarget) { michael@0: mDrawTarget->Flush(); michael@0: mSurface = mDrawTarget->Snapshot(); michael@0: } michael@0: michael@0: if (!mGLContext && aDestTarget) { michael@0: NS_ASSERTION(mSurface, "Must have surface to draw!"); michael@0: if (mSurface) { michael@0: aDestTarget->CopySurface(mSurface, michael@0: IntRect(0, 0, mBounds.width, mBounds.height), michael@0: IntPoint(0, 0)); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (mGLContext) { michael@0: RefPtr readSurf; michael@0: RefPtr resultSurf; michael@0: michael@0: SharedSurface_GL* sharedSurf = nullptr; michael@0: if (mStream) { michael@0: sharedSurf = SharedSurface_GL::Cast(mStream->SwapConsumer()); michael@0: } else { michael@0: sharedSurf = mGLContext->RequestFrame(); michael@0: } michael@0: michael@0: if (!sharedSurf) { michael@0: NS_WARNING("Null frame received."); michael@0: return; michael@0: } michael@0: michael@0: IntSize readSize(sharedSurf->Size()); michael@0: SurfaceFormat format = (GetContentFlags() & CONTENT_OPAQUE) michael@0: ? SurfaceFormat::B8G8R8X8 michael@0: : SurfaceFormat::B8G8R8A8; michael@0: michael@0: if (aDestTarget) { michael@0: resultSurf = aDestTarget->Snapshot(); michael@0: if (!resultSurf) { michael@0: resultSurf = GetTempSurface(readSize, format); michael@0: } michael@0: } else { michael@0: resultSurf = GetTempSurface(readSize, format); michael@0: } michael@0: MOZ_ASSERT(resultSurf); michael@0: MOZ_ASSERT(sharedSurf->APIType() == APITypeT::OpenGL); michael@0: SharedSurface_GL* surfGL = SharedSurface_GL::Cast(sharedSurf); michael@0: michael@0: if (surfGL->Type() == SharedSurfaceType::Basic) { michael@0: // sharedSurf_Basic->mData must outlive readSurf. Alas, readSurf may not michael@0: // leave the scope it was declared in. michael@0: SharedSurface_Basic* sharedSurf_Basic = SharedSurface_Basic::Cast(surfGL); michael@0: readSurf = sharedSurf_Basic->GetData(); michael@0: } else { michael@0: if (resultSurf->GetSize() != readSize || michael@0: !(readSurf = resultSurf->GetDataSurface()) || michael@0: readSurf->GetFormat() != format) michael@0: { michael@0: readSurf = GetTempSurface(readSize, format); michael@0: } michael@0: michael@0: // Readback handles Flush/MarkDirty. michael@0: mGLContext->Screen()->Readback(surfGL, readSurf); michael@0: } michael@0: MOZ_ASSERT(readSurf); michael@0: michael@0: bool needsPremult = surfGL->HasAlpha() && !mIsGLAlphaPremult; michael@0: if (needsPremult) { michael@0: PremultiplySurface(readSurf); michael@0: } michael@0: michael@0: if (readSurf != resultSurf) { michael@0: RefPtr resultDataSurface = michael@0: resultSurf->GetDataSurface(); michael@0: RefPtr dt = michael@0: Factory::CreateDrawTargetForData(BackendType::CAIRO, michael@0: resultDataSurface->GetData(), michael@0: resultDataSurface->GetSize(), michael@0: resultDataSurface->Stride(), michael@0: resultDataSurface->GetFormat()); michael@0: IntSize readSize = readSurf->GetSize(); michael@0: dt->CopySurface(readSurf, michael@0: IntRect(0, 0, readSize.width, readSize.height), michael@0: IntPoint(0, 0)); michael@0: } michael@0: michael@0: // If !aDestSurface then we will end up painting using mSurface, so michael@0: // stick our surface into mSurface, so that the Paint() path is the same. michael@0: if (!aDestTarget) { michael@0: mSurface = resultSurf; michael@0: } michael@0: } michael@0: } michael@0: michael@0: DataSourceSurface* michael@0: CopyableCanvasLayer::GetTempSurface(const IntSize& aSize, michael@0: const SurfaceFormat aFormat) michael@0: { michael@0: if (!mCachedTempSurface || michael@0: aSize.width != mCachedSize.width || michael@0: aSize.height != mCachedSize.height || michael@0: aFormat != mCachedFormat) michael@0: { michael@0: mCachedTempSurface = Factory::CreateDataSourceSurface(aSize, aFormat); michael@0: mCachedSize = aSize; michael@0: mCachedFormat = aFormat; michael@0: } michael@0: michael@0: return mCachedTempSurface; michael@0: } michael@0: michael@0: void michael@0: CopyableCanvasLayer::DiscardTempSurface() michael@0: { michael@0: mCachedTempSurface = nullptr; michael@0: } michael@0: michael@0: } michael@0: }