From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- gfx/2d/DrawTargetRecording.cpp | 936 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 936 insertions(+) create mode 100644 gfx/2d/DrawTargetRecording.cpp (limited to 'gfx/2d/DrawTargetRecording.cpp') diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp new file mode 100644 index 0000000000..6edc8cdf91 --- /dev/null +++ b/gfx/2d/DrawTargetRecording.cpp @@ -0,0 +1,936 @@ +/* -*- 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 "DrawTargetRecording.h" +#include "DrawTargetSkia.h" +#include "PathRecording.h" +#include + +#include "Logging.h" +#include "Tools.h" +#include "Filters.h" +#include "mozilla/gfx/DataSurfaceHelpers.h" +#include "mozilla/layers/CanvasDrawEventRecorder.h" +#include "mozilla/layers/RecordedCanvasEventImpl.h" +#include "mozilla/layers/SourceSurfaceSharedData.h" +#include "mozilla/UniquePtr.h" +#include "nsXULAppAPI.h" // for XRE_IsContentProcess() +#include "RecordingTypes.h" +#include "RecordedEventImpl.h" + +namespace mozilla { +namespace gfx { + +struct RecordingSourceSurfaceUserData { + void* refPtr; + RefPtr recorder; + + // The optimized surface holds a reference to our surface, for GetDataSurface + // calls, so we must hold a weak reference to avoid circular dependency. + ThreadSafeWeakPtr optimizedSurface; +}; + +static void RecordingSourceSurfaceUserDataFunc(void* aUserData) { + RecordingSourceSurfaceUserData* userData = + static_cast(aUserData); + + if (NS_IsMainThread()) { + userData->recorder->RecordSourceSurfaceDestruction(userData->refPtr); + delete userData; + return; + } + + userData->recorder->AddPendingDeletion([userData]() -> void { + userData->recorder->RecordSourceSurfaceDestruction(userData->refPtr); + delete userData; + }); +} + +static bool EnsureSurfaceStoredRecording(DrawEventRecorderPrivate* aRecorder, + SourceSurface* aSurface, + const char* reason) { + // It's important that TryAddStoredObject is called first because that will + // run any pending processing required by recorded objects that have been + // deleted off the main thread. + if (!aRecorder->TryAddStoredObject(aSurface)) { + // Surface is already stored. + return false; + } + aRecorder->StoreSourceSurfaceRecording(aSurface, reason); + aRecorder->AddSourceSurface(aSurface); + + RecordingSourceSurfaceUserData* userData = new RecordingSourceSurfaceUserData; + userData->refPtr = aSurface; + userData->recorder = aRecorder; + aSurface->AddUserData(reinterpret_cast(aRecorder), userData, + &RecordingSourceSurfaceUserDataFunc); + return true; +} + +class SourceSurfaceRecording : public SourceSurface { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording, override) + + SourceSurfaceRecording(IntSize aSize, SurfaceFormat aFormat, + DrawEventRecorderPrivate* aRecorder, + SourceSurface* aOriginalSurface = nullptr) + : mSize(aSize), + mFormat(aFormat), + mRecorder(aRecorder), + mOriginalSurface(aOriginalSurface) { + mRecorder->AddStoredObject(this); + } + + ~SourceSurfaceRecording() { + mRecorder->RemoveStoredObject(this); + mRecorder->RecordEvent( + RecordedSourceSurfaceDestruction(ReferencePtr(this))); + } + + SurfaceType GetType() const override { return SurfaceType::RECORDING; } + IntSize GetSize() const override { return mSize; } + SurfaceFormat GetFormat() const override { return mFormat; } + already_AddRefed GetDataSurface() override { + if (mOriginalSurface) { + return mOriginalSurface->GetDataSurface(); + } + + return nullptr; + } + + already_AddRefed ExtractSubrect(const IntRect& aRect) override; + + IntSize mSize; + SurfaceFormat mFormat; + RefPtr mRecorder; + // If a SourceSurfaceRecording is returned from an OptimizeSourceSurface call + // we need GetDataSurface to work, so we hold the original surface we + // optimized to return its GetDataSurface. + RefPtr mOriginalSurface; +}; + +class GradientStopsRecording : public GradientStops { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording, override) + + explicit GradientStopsRecording(DrawEventRecorderPrivate* aRecorder) + : mRecorder(aRecorder) { + mRecorder->AddStoredObject(this); + } + + virtual ~GradientStopsRecording() { + mRecorder->RemoveStoredObject(this); + mRecorder->RecordEvent( + RecordedGradientStopsDestruction(ReferencePtr(this))); + } + + BackendType GetBackendType() const override { return BackendType::RECORDING; } + + RefPtr mRecorder; +}; + +class FilterNodeRecording : public FilterNode { + public: + MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override) + using FilterNode::SetAttribute; + + explicit FilterNodeRecording(DrawEventRecorderPrivate* aRecorder) + : mRecorder(aRecorder) { + mRecorder->AddStoredObject(this); + } + + virtual ~FilterNodeRecording() { + mRecorder->RemoveStoredObject(this); + mRecorder->RecordEvent(RecordedFilterNodeDestruction(ReferencePtr(this))); + } + + void SetInput(uint32_t aIndex, SourceSurface* aSurface) override { + EnsureSurfaceStoredRecording(mRecorder, aSurface, "SetInput"); + + mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface)); + } + void SetInput(uint32_t aIndex, FilterNode* aFilter) override { + MOZ_ASSERT(mRecorder->HasStoredObject(aFilter)); + + mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter)); + } + +#define FORWARD_SET_ATTRIBUTE(type, argtype) \ + void SetAttribute(uint32_t aIndex, type aValue) override { \ + mRecorder->RecordEvent(RecordedFilterNodeSetAttribute( \ + this, aIndex, aValue, \ + RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \ + } + + FORWARD_SET_ATTRIBUTE(bool, BOOL); + FORWARD_SET_ATTRIBUTE(uint32_t, UINT32); + FORWARD_SET_ATTRIBUTE(Float, FLOAT); + FORWARD_SET_ATTRIBUTE(const Size&, SIZE); + FORWARD_SET_ATTRIBUTE(const IntSize&, INTSIZE); + FORWARD_SET_ATTRIBUTE(const IntPoint&, INTPOINT); + FORWARD_SET_ATTRIBUTE(const Rect&, RECT); + FORWARD_SET_ATTRIBUTE(const IntRect&, INTRECT); + FORWARD_SET_ATTRIBUTE(const Point&, POINT); + FORWARD_SET_ATTRIBUTE(const Matrix&, MATRIX); + FORWARD_SET_ATTRIBUTE(const Matrix5x4&, MATRIX5X4); + FORWARD_SET_ATTRIBUTE(const Point3D&, POINT3D); + FORWARD_SET_ATTRIBUTE(const DeviceColor&, COLOR); + +#undef FORWARD_SET_ATTRIBUTE + + void SetAttribute(uint32_t aIndex, const Float* aFloat, + uint32_t aSize) override { + mRecorder->RecordEvent( + RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize)); + } + + FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; } + + RefPtr mRecorder; +}; + +DrawTargetRecording::DrawTargetRecording( + layers::CanvasDrawEventRecorder* aRecorder, int64_t aTextureId, + const layers::RemoteTextureOwnerId& aTextureOwnerId, DrawTarget* aDT, + const IntSize& aSize) + : mRecorder(static_cast(aRecorder)), + mFinalDT(aDT), + mRect(IntPoint(0, 0), aSize) { + mRecorder->RecordEvent(layers::RecordedCanvasDrawTargetCreation( + this, aTextureId, aTextureOwnerId, mFinalDT->GetBackendType(), aSize, + mFinalDT->GetFormat())); + mFormat = mFinalDT->GetFormat(); + DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat)); +} + +DrawTargetRecording::DrawTargetRecording(DrawEventRecorder* aRecorder, + DrawTarget* aDT, IntRect aRect, + bool aHasData) + : mRecorder(static_cast(aRecorder)), + mFinalDT(aDT), + mRect(aRect) { + MOZ_DIAGNOSTIC_ASSERT(aRecorder->GetRecorderType() != RecorderType::CANVAS); + RefPtr snapshot = aHasData ? mFinalDT->Snapshot() : nullptr; + mRecorder->RecordEvent( + RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect, + mFinalDT->GetFormat(), aHasData, snapshot)); + mFormat = mFinalDT->GetFormat(); + DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat)); +} + +DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording* aDT, + IntRect aRect, SurfaceFormat aFormat) + : mRecorder(aDT->mRecorder), mFinalDT(aDT->mFinalDT), mRect(aRect) { + mFormat = aFormat; + DrawTarget::SetPermitSubpixelAA(IsOpaque(mFormat)); +} + +DrawTargetRecording::~DrawTargetRecording() { + mRecorder->RecordEvent(RecordedDrawTargetDestruction(ReferencePtr(this))); + mRecorder->ClearDrawTarget(this); +} + +void DrawTargetRecording::Link(const char* aDestination, const Rect& aRect) { + MarkChanged(); + + mRecorder->RecordEvent(this, RecordedLink(aDestination, aRect)); +} + +void DrawTargetRecording::Destination(const char* aDestination, + const Point& aPoint) { + MarkChanged(); + + mRecorder->RecordEvent(this, RecordedDestination(aDestination, aPoint)); +} + +void DrawTargetRecording::FillRect(const Rect& aRect, const Pattern& aPattern, + const DrawOptions& aOptions) { + MarkChanged(); + + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(this, RecordedFillRect(aRect, aPattern, aOptions)); +} + +void DrawTargetRecording::StrokeRect(const Rect& aRect, const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + MarkChanged(); + + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent( + this, RecordedStrokeRect(aRect, aPattern, aStrokeOptions, aOptions)); +} + +void DrawTargetRecording::StrokeLine(const Point& aBegin, const Point& aEnd, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + MarkChanged(); + + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(this, RecordedStrokeLine(aBegin, aEnd, aPattern, + aStrokeOptions, aOptions)); +} + +void DrawTargetRecording::Fill(const Path* aPath, const Pattern& aPattern, + const DrawOptions& aOptions) { + if (!aPath) { + return; + } + + MarkChanged(); + + if (aPath->GetBackendType() == BackendType::RECORDING) { + const PathRecording* path = static_cast(aPath); + auto circle = path->AsCircle(); + if (circle) { + EnsurePatternDependenciesStored(aPattern); + mRecorder->RecordEvent( + this, RecordedFillCircle(circle.value(), aPattern, aOptions)); + return; + } + } + + RefPtr pathRecording = EnsurePathStored(aPath); + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(this, RecordedFill(pathRecording, aPattern, aOptions)); +} + +struct RecordingFontUserData { + void* refPtr; + void* unscaledFont; + RefPtr recorder; +}; + +static void RecordingFontUserDataDestroyFunc(void* aUserData) { + RecordingFontUserData* userData = + static_cast(aUserData); + + userData->recorder->RecordEvent( + RecordedScaledFontDestruction(ReferencePtr(userData->refPtr))); + userData->recorder->RemoveScaledFont((ScaledFont*)userData->refPtr); + userData->recorder->DecrementUnscaledFontRefCount(userData->unscaledFont); + delete userData; +} + +void DrawTargetRecording::DrawGlyphs(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const DrawOptions& aOptions, + const StrokeOptions* aStrokeOptions) { + if (!aFont) { + return; + } + + MarkChanged(); + + EnsurePatternDependenciesStored(aPattern); + + UserDataKey* userDataKey = reinterpret_cast(mRecorder.get()); + if (mRecorder->WantsExternalFonts()) { + mRecorder->AddScaledFont(aFont); + } else if (!aFont->GetUserData(userDataKey)) { + UnscaledFont* unscaledFont = aFont->GetUnscaledFont(); + if (mRecorder->IncrementUnscaledFontRefCount(unscaledFont) == 0) { + // Prefer sending the description, if we can create one. This ensures + // we don't record the data of system fonts which saves time and can + // prevent duplicate copies from accumulating in the OS cache during + // playback. + RecordedFontDescriptor fontDesc(unscaledFont); + if (fontDesc.IsValid()) { + mRecorder->RecordEvent(fontDesc); + } else { + RecordedFontData fontData(unscaledFont); + RecordedFontDetails fontDetails; + if (fontData.GetFontDetails(fontDetails)) { + // Try to serialise the whole font, just in case this is a web font + // that is not present on the system. + if (!mRecorder->HasStoredFontData(fontDetails.fontDataKey)) { + mRecorder->RecordEvent(fontData); + mRecorder->AddStoredFontData(fontDetails.fontDataKey); + } + mRecorder->RecordEvent( + RecordedUnscaledFontCreation(unscaledFont, fontDetails)); + } else { + gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise " + "UnscaledFont"; + } + } + } + mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, unscaledFont)); + RecordingFontUserData* userData = new RecordingFontUserData; + userData->refPtr = aFont; + userData->unscaledFont = unscaledFont; + userData->recorder = mRecorder; + aFont->AddUserData(userDataKey, userData, + &RecordingFontUserDataDestroyFunc); + userData->recorder->AddScaledFont(aFont); + } + + if (aStrokeOptions) { + mRecorder->RecordEvent( + this, RecordedStrokeGlyphs(aFont, aPattern, *aStrokeOptions, aOptions, + aBuffer.mGlyphs, aBuffer.mNumGlyphs)); + } else { + mRecorder->RecordEvent( + this, RecordedFillGlyphs(aFont, aPattern, aOptions, aBuffer.mGlyphs, + aBuffer.mNumGlyphs)); + } +} + +void DrawTargetRecording::FillGlyphs(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const DrawOptions& aOptions) { + DrawGlyphs(aFont, aBuffer, aPattern, aOptions); +} + +void DrawTargetRecording::StrokeGlyphs(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + DrawGlyphs(aFont, aBuffer, aPattern, aOptions, &aStrokeOptions); +} + +void DrawTargetRecording::Mask(const Pattern& aSource, const Pattern& aMask, + const DrawOptions& aOptions) { + MarkChanged(); + + EnsurePatternDependenciesStored(aSource); + EnsurePatternDependenciesStored(aMask); + + mRecorder->RecordEvent(this, RecordedMask(aSource, aMask, aOptions)); +} + +void DrawTargetRecording::MaskSurface(const Pattern& aSource, + SourceSurface* aMask, Point aOffset, + const DrawOptions& aOptions) { + if (!aMask) { + return; + } + + MarkChanged(); + + EnsurePatternDependenciesStored(aSource); + EnsureSurfaceStoredRecording(mRecorder, aMask, "MaskSurface"); + + mRecorder->RecordEvent( + this, RecordedMaskSurface(aSource, aMask, aOffset, aOptions)); +} + +void DrawTargetRecording::Stroke(const Path* aPath, const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + MarkChanged(); + + if (aPath->GetBackendType() == BackendType::RECORDING) { + const PathRecording* path = static_cast(aPath); + auto circle = path->AsCircle(); + if (circle && circle->closed) { + EnsurePatternDependenciesStored(aPattern); + mRecorder->RecordEvent( + this, RecordedStrokeCircle(circle.value(), aPattern, aStrokeOptions, + aOptions)); + return; + } + + auto line = path->AsLine(); + if (line) { + EnsurePatternDependenciesStored(aPattern); + mRecorder->RecordEvent( + this, RecordedStrokeLine(line->origin, line->destination, aPattern, + aStrokeOptions, aOptions)); + return; + } + } + + RefPtr pathRecording = EnsurePathStored(aPath); + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent( + this, RecordedStroke(pathRecording, aPattern, aStrokeOptions, aOptions)); +} + +void DrawTargetRecording::DrawShadow(const Path* aPath, const Pattern& aPattern, + const ShadowOptions& aShadow, + const DrawOptions& aOptions, + const StrokeOptions* aStrokeOptions) { + MarkChanged(); + + RefPtr pathRecording = EnsurePathStored(aPath); + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent( + this, RecordedDrawShadow(pathRecording, aPattern, aShadow, aOptions, + aStrokeOptions)); +} + +void DrawTargetRecording::MarkChanged() { mIsDirty = true; } + +already_AddRefed DrawTargetRecording::Snapshot() { + RefPtr retSurf = + new SourceSurfaceRecording(mRect.Size(), mFormat, mRecorder); + + mRecorder->RecordEvent(this, RecordedSnapshot(ReferencePtr(retSurf))); + + return retSurf.forget(); +} + +already_AddRefed DrawTargetRecording::IntoLuminanceSource( + LuminanceType aLuminanceType, float aOpacity) { + RefPtr retSurf = + new SourceSurfaceRecording(mRect.Size(), SurfaceFormat::A8, mRecorder); + + mRecorder->RecordEvent( + this, RecordedIntoLuminanceSource(retSurf, aLuminanceType, aOpacity)); + + return retSurf.forget(); +} + +already_AddRefed SourceSurfaceRecording::ExtractSubrect( + const IntRect& aRect) { + if (aRect.IsEmpty() || !GetRect().Contains(aRect)) { + return nullptr; + } + + RefPtr subSurf = + new SourceSurfaceRecording(aRect.Size(), mFormat, mRecorder); + mRecorder->RecordEvent(RecordedExtractSubrect(subSurf, this, aRect)); + return subSurf.forget(); +} + +void DrawTargetRecording::Flush() { + mRecorder->RecordEvent(this, RecordedFlush()); +} + +void DrawTargetRecording::DetachAllSnapshots() { + mRecorder->RecordEvent(this, RecordedDetachAllSnapshots()); +} + +void DrawTargetRecording::DrawSurface(SourceSurface* aSurface, + const Rect& aDest, const Rect& aSource, + const DrawSurfaceOptions& aSurfOptions, + const DrawOptions& aOptions) { + if (!aSurface) { + return; + } + + MarkChanged(); + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurface"); + + mRecorder->RecordEvent(this, RecordedDrawSurface(aSurface, aDest, aSource, + aSurfOptions, aOptions)); +} + +void DrawTargetRecording::DrawDependentSurface(uint64_t aId, + const Rect& aDest) { + MarkChanged(); + + mRecorder->AddDependentSurface(aId); + mRecorder->RecordEvent(this, RecordedDrawDependentSurface(aId, aDest)); +} + +void DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface* aSurface, + const Point& aDest, + const ShadowOptions& aShadow, + CompositionOp aOp) { + if (!aSurface) { + return; + } + + MarkChanged(); + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurfaceWithShadow"); + + mRecorder->RecordEvent( + this, RecordedDrawSurfaceWithShadow(aSurface, aDest, aShadow, aOp)); +} + +void DrawTargetRecording::DrawFilter(FilterNode* aNode, const Rect& aSourceRect, + const Point& aDestPoint, + const DrawOptions& aOptions) { + if (!aNode) { + return; + } + + MarkChanged(); + + MOZ_ASSERT(mRecorder->HasStoredObject(aNode)); + + mRecorder->RecordEvent( + this, RecordedDrawFilter(aNode, aSourceRect, aDestPoint, aOptions)); +} + +already_AddRefed DrawTargetRecording::CreateFilter( + FilterType aType) { + RefPtr retNode = new FilterNodeRecording(mRecorder); + + mRecorder->RecordEvent(this, RecordedFilterNodeCreation(retNode, aType)); + + return retNode.forget(); +} + +void DrawTargetRecording::ClearRect(const Rect& aRect) { + MarkChanged(); + + mRecorder->RecordEvent(this, RecordedClearRect(aRect)); +} + +void DrawTargetRecording::CopySurface(SourceSurface* aSurface, + const IntRect& aSourceRect, + const IntPoint& aDestination) { + if (!aSurface) { + return; + } + + MarkChanged(); + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "CopySurface"); + + mRecorder->RecordEvent( + this, RecordedCopySurface(aSurface, aSourceRect, aDestination)); +} + +void DrawTargetRecording::PushClip(const Path* aPath) { + if (!aPath) { + return; + } + + // The canvas doesn't have a clipRect API so we always end up in the generic + // path. The D2D backend doesn't have a good way of specializing rectangular + // clips so we take advantage of the fact that aPath is usually backed by a + // SkiaPath which implements AsRect() and specialize it here. + auto rect = aPath->AsRect(); + if (rect.isSome()) { + PushClipRect(rect.value()); + return; + } + + RefPtr pathRecording = EnsurePathStored(aPath); + + mRecorder->RecordEvent(this, RecordedPushClip(ReferencePtr(pathRecording))); +} + +void DrawTargetRecording::PushClipRect(const Rect& aRect) { + mRecorder->RecordEvent(this, RecordedPushClipRect(aRect)); +} + +void DrawTargetRecording::PopClip() { + mRecorder->RecordEvent(this, RecordedPopClip()); +} + +void DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds, + bool aCopyBackground) { + if (aMask) { + EnsureSurfaceStoredRecording(mRecorder, aMask, "PushLayer"); + } + + mRecorder->RecordEvent( + this, RecordedPushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds, + aCopyBackground)); + + PushedLayer layer(GetPermitSubpixelAA()); + mPushedLayers.push_back(layer); + DrawTarget::SetPermitSubpixelAA(aOpaque); +} + +void DrawTargetRecording::PushLayerWithBlend(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds, + bool aCopyBackground, + CompositionOp aCompositionOp) { + if (aMask) { + EnsureSurfaceStoredRecording(mRecorder, aMask, "PushLayer"); + } + + mRecorder->RecordEvent(this, RecordedPushLayerWithBlend( + aOpaque, aOpacity, aMask, aMaskTransform, + aBounds, aCopyBackground, aCompositionOp)); + + PushedLayer layer(GetPermitSubpixelAA()); + mPushedLayers.push_back(layer); + DrawTarget::SetPermitSubpixelAA(aOpaque); +} + +void DrawTargetRecording::PopLayer() { + MarkChanged(); + + mRecorder->RecordEvent(this, RecordedPopLayer()); + + const PushedLayer& layer = mPushedLayers.back(); + DrawTarget::SetPermitSubpixelAA(layer.mOldPermitSubpixelAA); + mPushedLayers.pop_back(); +} + +already_AddRefed +DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char* aData, + const IntSize& aSize, + int32_t aStride, + SurfaceFormat aFormat) const { + RefPtr surface = CreateDataSourceSurfaceWithStrideFromData( + aSize, aFormat, aStride, aData, aStride); + if (!surface) { + return nullptr; + } + + return OptimizeSourceSurface(surface); +} + +already_AddRefed DrawTargetRecording::OptimizeSourceSurface( + SourceSurface* aSurface) const { + // See if we have a previously optimized surface available. We have to do this + // check before the SurfaceType::RECORDING below, because aSurface might be a + // SurfaceType::RECORDING from another recorder we have previously optimized. + auto* userData = static_cast( + aSurface->GetUserData(reinterpret_cast(mRecorder.get()))); + if (userData) { + RefPtr strongRef(userData->optimizedSurface); + if (strongRef) { + return do_AddRef(strongRef); + } + } else { + if (!EnsureSurfaceStoredRecording(mRecorder, aSurface, + "OptimizeSourceSurface")) { + // Surface was already stored, but doesn't have UserData so must be one + // of our recording surfaces. + MOZ_ASSERT(aSurface->GetType() == SurfaceType::RECORDING); + return do_AddRef(aSurface); + } + + userData = static_cast( + aSurface->GetUserData(reinterpret_cast(mRecorder.get()))); + MOZ_ASSERT(userData, + "User data should always have been set by " + "EnsureSurfaceStoredRecording."); + } + + RefPtr retSurf = new SourceSurfaceRecording( + aSurface->GetSize(), aSurface->GetFormat(), mRecorder, aSurface); + mRecorder->RecordEvent(const_cast(this), + RecordedOptimizeSourceSurface(aSurface, retSurf)); + userData->optimizedSurface = retSurf; + + return retSurf.forget(); +} + +already_AddRefed +DrawTargetRecording::CreateSourceSurfaceFromNativeSurface( + const NativeSurface& aSurface) const { + MOZ_ASSERT(false); + return nullptr; +} + +already_AddRefed +DrawTargetRecording::CreateSimilarDrawTargetWithBacking( + const IntSize& aSize, SurfaceFormat aFormat) const { + RefPtr similarDT; + if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) { + // If the requested similar draw target is too big, then we should try to + // rasterize on the content side to avoid duplicating the effort when a + // blob image gets tiled. If we fail somehow to produce it, we can fall + // back to recording. + constexpr int32_t kRasterThreshold = 256 * 256 * 4; + int32_t stride = aSize.width * BytesPerPixel(aFormat); + int32_t surfaceBytes = aSize.height * stride; + if (surfaceBytes >= kRasterThreshold) { + auto surface = MakeRefPtr(); + if (surface->Init(aSize, stride, aFormat)) { + auto dt = MakeRefPtr(); + if (dt->Init(std::move(surface))) { + return dt.forget(); + } else { + MOZ_ASSERT_UNREACHABLE("Skia should initialize given surface!"); + } + } + } + } + + return CreateSimilarDrawTarget(aSize, aFormat); +} + +already_AddRefed DrawTargetRecording::CreateSimilarDrawTarget( + const IntSize& aSize, SurfaceFormat aFormat) const { + RefPtr similarDT; + if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) { + similarDT = + new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aSize), aFormat); + mRecorder->RecordEvent( + const_cast(this), + RecordedCreateSimilarDrawTarget(similarDT.get(), aSize, aFormat)); + } else if (XRE_IsContentProcess()) { + // Crash any content process that calls this function with arguments that + // would fail to create a similar draw target. We do this to root out bad + // callers. We don't want to crash any important processes though so for + // for those we'll just gracefully return nullptr. + MOZ_CRASH( + "Content-process DrawTargetRecording can't create requested similar " + "drawtarget"); + } + return similarDT.forget(); +} + +bool DrawTargetRecording::CanCreateSimilarDrawTarget( + const IntSize& aSize, SurfaceFormat aFormat) const { + return mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat); +} + +RefPtr DrawTargetRecording::CreateClippedDrawTarget( + const Rect& aBounds, SurfaceFormat aFormat) { + RefPtr similarDT; + similarDT = new DrawTargetRecording(this, mRect, aFormat); + mRecorder->RecordEvent( + this, RecordedCreateClippedDrawTarget(similarDT.get(), aBounds, aFormat)); + similarDT->SetTransform(mTransform); + return similarDT; +} + +already_AddRefed +DrawTargetRecording::CreateSimilarDrawTargetForFilter( + const IntSize& aMaxSize, SurfaceFormat aFormat, FilterNode* aFilter, + FilterNode* aSource, const Rect& aSourceRect, const Point& aDestPoint) { + RefPtr similarDT; + if (mFinalDT->CanCreateSimilarDrawTarget(aMaxSize, aFormat)) { + similarDT = new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aMaxSize), + aFormat); + mRecorder->RecordEvent( + this, RecordedCreateDrawTargetForFilter(similarDT.get(), aMaxSize, + aFormat, aFilter, aSource, + aSourceRect, aDestPoint)); + } else if (XRE_IsContentProcess()) { + // See CreateSimilarDrawTarget + MOZ_CRASH( + "Content-process DrawTargetRecording can't create requested clipped " + "drawtarget"); + } + return similarDT.forget(); +} + +already_AddRefed DrawTargetRecording::CreatePathBuilder( + FillRule aFillRule) const { + return MakeAndAddRef(mFinalDT->GetBackendType(), + aFillRule); +} + +already_AddRefed DrawTargetRecording::CreateGradientStops( + GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { + RefPtr retStops = new GradientStopsRecording(mRecorder); + + mRecorder->RecordEvent( + const_cast(this), + RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode)); + + return retStops.forget(); +} + +void DrawTargetRecording::SetTransform(const Matrix& aTransform) { + if (mTransform.ExactlyEquals(aTransform)) { + return; + } + DrawTarget::SetTransform(aTransform); + mRecorder->RecordEvent(this, RecordedSetTransform(aTransform)); +} + +void DrawTargetRecording::SetPermitSubpixelAA(bool aPermitSubpixelAA) { + if (aPermitSubpixelAA == mPermitSubpixelAA) { + return; + } + DrawTarget::SetPermitSubpixelAA(aPermitSubpixelAA); + mRecorder->RecordEvent(this, RecordedSetPermitSubpixelAA(aPermitSubpixelAA)); +} + +already_AddRefed DrawTargetRecording::EnsurePathStored( + const Path* aPath) { + RefPtr pathRecording; + if (aPath->GetBackendType() == BackendType::RECORDING) { + pathRecording = + const_cast(static_cast(aPath)); + if (!mRecorder->TryAddStoredObject(pathRecording)) { + // Path is already stored. + return pathRecording.forget(); + } + } else { + MOZ_ASSERT(!mRecorder->HasStoredObject(aPath)); + FillRule fillRule = aPath->GetFillRule(); + RefPtr builderRecording = + new PathBuilderRecording(mFinalDT->GetBackendType(), fillRule); + aPath->StreamToSink(builderRecording); + pathRecording = builderRecording->Finish().downcast(); + mRecorder->AddStoredObject(pathRecording); + } + + // It's important that AddStoredObject or TryAddStoredObject is called before + // this because that will run any pending processing required by recorded + // objects that have been deleted off the main thread. + mRecorder->RecordEvent(this, RecordedPathCreation(pathRecording.get())); + pathRecording->mStoredRecorders.push_back(mRecorder); + + return pathRecording.forget(); +} + +// This should only be called on the 'root' DrawTargetRecording. +// Calling it on a child DrawTargetRecordings will cause confusion. +void DrawTargetRecording::FlushItem(const IntRect& aBounds) { + mRecorder->FlushItem(aBounds); + // Reinitialize the recorder (FlushItem will write a new recording header) + // Tell the new recording about our draw target + // This code should match what happens in the DrawTargetRecording constructor. + MOZ_DIAGNOSTIC_ASSERT(mRecorder->GetRecorderType() != RecorderType::CANVAS); + mRecorder->RecordEvent( + RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect, + mFinalDT->GetFormat(), false, nullptr)); + // Add the current transform to the new recording + mRecorder->RecordEvent(this, + RecordedSetTransform(DrawTarget::GetTransform())); +} + +void DrawTargetRecording::EnsurePatternDependenciesStored( + const Pattern& aPattern) { + switch (aPattern.GetType()) { + case PatternType::COLOR: + // No dependencies here. + return; + case PatternType::LINEAR_GRADIENT: { + MOZ_ASSERT_IF( + static_cast(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast(&aPattern)->mStops)); + return; + } + case PatternType::RADIAL_GRADIENT: { + MOZ_ASSERT_IF( + static_cast(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast(&aPattern)->mStops)); + return; + } + case PatternType::CONIC_GRADIENT: { + MOZ_ASSERT_IF( + static_cast(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast(&aPattern)->mStops)); + return; + } + case PatternType::SURFACE: { + const SurfacePattern* pat = static_cast(&aPattern); + EnsureSurfaceStoredRecording(mRecorder, pat->mSurface, + "EnsurePatternDependenciesStored"); + return; + } + } +} + +} // namespace gfx +} // namespace mozilla -- cgit v1.2.3