/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "SourceSurfaceCapture.h" #include "DrawCommand.h" #include "DrawTargetCapture.h" #include "MainThreadUtils.h" #include "mozilla/gfx/Logging.h" namespace mozilla { namespace gfx { SourceSurfaceCapture::SourceSurfaceCapture(DrawTargetCaptureImpl* aOwner) : mOwner(aOwner), mHasCommandList(false), mShouldResolveToLuminance{false}, mLuminanceType{LuminanceType::LUMINANCE}, mOpacity{1.0f}, mLock("SourceSurfaceCapture.mLock") { mSize = mOwner->GetSize(); mFormat = mOwner->GetFormat(); mRefDT = mOwner->mRefDT; mStride = mOwner->mStride; mSurfaceAllocationSize = mOwner->mSurfaceAllocationSize; } SourceSurfaceCapture::SourceSurfaceCapture( DrawTargetCaptureImpl* aOwner, LuminanceType aLuminanceType /* = LuminanceType::LINEARRGB */, Float aOpacity /* = 1.0f */) : mOwner{aOwner}, mHasCommandList{false}, mShouldResolveToLuminance{true}, mLuminanceType{aLuminanceType}, mOpacity{aOpacity}, mLock{"SourceSurfaceCapture.mLock"} { mSize = mOwner->GetSize(); mFormat = mOwner->GetFormat(); mRefDT = mOwner->mRefDT; mStride = mOwner->mStride; mSurfaceAllocationSize = mOwner->mSurfaceAllocationSize; // In this case our DrawTarget will not track us, so copy its drawing // commands. DrawTargetWillChange(); } SourceSurfaceCapture::SourceSurfaceCapture(DrawTargetCaptureImpl* aOwner, SourceSurface* aSurfToOptimize) : mOwner{aOwner}, mHasCommandList{false}, mShouldResolveToLuminance{false}, mLuminanceType{LuminanceType::LUMINANCE}, mOpacity{1.0f}, mLock{"SourceSurfaceCapture.mLock"}, mSurfToOptimize(aSurfToOptimize) { mSize = aSurfToOptimize->GetSize(); mFormat = aSurfToOptimize->GetFormat(); mRefDT = mOwner->mRefDT; } SourceSurfaceCapture::~SourceSurfaceCapture() = default; void SourceSurfaceCapture::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf, SizeOfInfo& aInfo) const { MutexAutoLock lock(mLock); aInfo.AddType(SurfaceType::CAPTURE); if (mSurfToOptimize) { mSurfToOptimize->SizeOfExcludingThis(aMallocSizeOf, aInfo); return; } if (mResolved) { mResolved->SizeOfExcludingThis(aMallocSizeOf, aInfo); return; } if (mHasCommandList) { aInfo.mHeapBytes += mCommands.BufferCapacity(); return; } } bool SourceSurfaceCapture::IsValid() const { // We must either be able to source a command list, or we must have a cached // and rasterized surface. MutexAutoLock lock(mLock); if (mSurfToOptimize) { // We were given a surface, but we haven't tried to optimize it yet // with the reference draw target. return mSurfToOptimize->IsValid(); } if (mResolved) { // We were given a surface, and we already optimized it with the // reference draw target. return mResolved->IsValid(); } // We have no underlying surface, so it must be a set of drawing commands. return mOwner || mHasCommandList; } RefPtr SourceSurfaceCapture::Resolve(BackendType aBackendType) { MutexAutoLock lock(mLock); if (mSurfToOptimize) { mResolved = mRefDT->OptimizeSourceSurface(mSurfToOptimize); mSurfToOptimize = nullptr; } if (mResolved || (!mOwner && !mHasCommandList)) { // We are already resolved, or there is no way we can rasterize // anything, we don't have a source DrawTarget and we don't have // a command list. Return whatever our cached surface is. return mResolved; } BackendType backendType = aBackendType; if (backendType == BackendType::NONE) { backendType = mRefDT->GetBackendType(); } // Note: SurfaceType is not 1:1 with BackendType, so we can't easily decide // that they match. Instead we just cache the first thing to be requested. // We ensured no mResolved existed before. mResolved = ResolveImpl(backendType); return mResolved; } RefPtr SourceSurfaceCapture::ResolveImpl( BackendType aBackendType) { RefPtr dt; uint8_t* data = nullptr; if (!mSurfaceAllocationSize) { if (aBackendType == mRefDT->GetBackendType()) { dt = mRefDT->CreateSimilarDrawTarget(mSize, mFormat); } else { dt = Factory::CreateDrawTarget(aBackendType, mSize, mFormat); } } else { data = static_cast(calloc(1, mSurfaceAllocationSize)); if (!data) { return nullptr; } BackendType type = Factory::DoesBackendSupportDataDrawtarget(aBackendType) ? aBackendType : BackendType::SKIA; dt = Factory::CreateDrawTargetForData(type, data, mSize, mStride, mFormat); if (!dt || !dt->IsValid()) { free(data); return nullptr; } } if (!dt || !dt->IsValid()) { // Make sure we haven't allocated and aren't leaking something, the code // right anove here should have guaranteed that. MOZ_ASSERT(!data); return nullptr; } // If we're still attached to a DrawTarget, use its command list rather than // our own (which will be empty). CaptureCommandList& commands = mHasCommandList ? mCommands : mOwner->mCommands; for (CaptureCommandList::iterator iter(commands); !iter.Done(); iter.Next()) { DrawingCommand* cmd = iter.Get(); cmd->ExecuteOnDT(dt, nullptr); } RefPtr surf; if (!mShouldResolveToLuminance) { surf = dt->Snapshot(); } else { surf = dt->IntoLuminanceSource(mLuminanceType, mOpacity); } if (data) { surf->AddUserData(reinterpret_cast(dt.get()), data, free); } return surf; } already_AddRefed SourceSurfaceCapture::GetDataSurface() { RefPtr surface = Resolve(); if (!surface) { return nullptr; } return surface->GetDataSurface(); } void SourceSurfaceCapture::DrawTargetWillDestroy() { MutexAutoLock lock(mLock); // The source DrawTarget is going away, so we can just steal its commands. mCommands = std::move(mOwner->mCommands); mHasCommandList = true; mOwner = nullptr; } void SourceSurfaceCapture::DrawTargetWillChange() { MutexAutoLock lock(mLock); for (CaptureCommandList::iterator iter(mOwner->mCommands); !iter.Done(); iter.Next()) { DrawingCommand* cmd = iter.Get(); cmd->CloneInto(&mCommands); } mHasCommandList = true; mOwner = nullptr; } } // namespace gfx } // namespace mozilla