diff options
Diffstat (limited to 'gfx/2d/DrawTargetRecording.cpp')
-rw-r--r-- | gfx/2d/DrawTargetRecording.cpp | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/gfx/2d/DrawTargetRecording.cpp b/gfx/2d/DrawTargetRecording.cpp new file mode 100644 index 0000000000..d5ffede845 --- /dev/null +++ b/gfx/2d/DrawTargetRecording.cpp @@ -0,0 +1,770 @@ +/* -*- 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 <stdio.h> + +#include "Logging.h" +#include "Tools.h" +#include "Filters.h" +#include "mozilla/gfx/DataSurfaceHelpers.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<DrawEventRecorderPrivate> 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<SourceSurface> optimizedSurface; +}; + +static void RecordingSourceSurfaceUserDataFunc(void* aUserData) { + RecordingSourceSurfaceUserData* userData = + static_cast<RecordingSourceSurfaceUserData*>(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<UserDataKey*>(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<DataSourceSurface> GetDataSurface() override { + if (mOriginalSurface) { + return mOriginalSurface->GetDataSurface(); + } + + return nullptr; + } + + IntSize mSize; + SurfaceFormat mFormat; + RefPtr<DrawEventRecorderPrivate> 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<SourceSurface> 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<DrawEventRecorderPrivate> 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<DrawEventRecorderPrivate> mRecorder; +}; + +DrawTargetRecording::DrawTargetRecording(DrawEventRecorder* aRecorder, + DrawTarget* aDT, IntRect aRect, + bool aHasData) + : mRecorder(static_cast<DrawEventRecorderPrivate*>(aRecorder)), + mFinalDT(aDT), + mRect(aRect) { + RefPtr<SourceSurface> snapshot = aHasData ? mFinalDT->Snapshot() : nullptr; + mRecorder->RecordEvent( + RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect, + mFinalDT->GetFormat(), aHasData, snapshot)); + mFormat = mFinalDT->GetFormat(); +} + +DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording* aDT, + IntRect aRect, SurfaceFormat aFormat) + : mRecorder(aDT->mRecorder), mFinalDT(aDT->mFinalDT), mRect(aRect) { + mFormat = aFormat; +} + +DrawTargetRecording::~DrawTargetRecording() { + mRecorder->RecordEvent(RecordedDrawTargetDestruction(ReferencePtr(this))); +} + +void DrawTargetRecording::Link(const char* aDestination, const Rect& aRect) { + mRecorder->RecordEvent(RecordedLink(this, aDestination, aRect)); +} + +void DrawTargetRecording::Destination(const char* aDestination, + const Point& aPoint) { + mRecorder->RecordEvent(RecordedDestination(this, aDestination, aPoint)); +} + +void DrawTargetRecording::FillRect(const Rect& aRect, const Pattern& aPattern, + const DrawOptions& aOptions) { + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(RecordedFillRect(this, aRect, aPattern, aOptions)); +} + +void DrawTargetRecording::StrokeRect(const Rect& aRect, const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent( + RecordedStrokeRect(this, aRect, aPattern, aStrokeOptions, aOptions)); +} + +void DrawTargetRecording::StrokeLine(const Point& aBegin, const Point& aEnd, + const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(RecordedStrokeLine(this, aBegin, aEnd, aPattern, + aStrokeOptions, aOptions)); +} + +void DrawTargetRecording::Fill(const Path* aPath, const Pattern& aPattern, + const DrawOptions& aOptions) { + if (!aPath) { + return; + } + + RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath); + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions)); +} + +struct RecordingFontUserData { + void* refPtr; + void* unscaledFont; + RefPtr<DrawEventRecorderPrivate> recorder; +}; + +static void RecordingFontUserDataDestroyFunc(void* aUserData) { + RecordingFontUserData* userData = + static_cast<RecordingFontUserData*>(aUserData); + + userData->recorder->RecordEvent( + RecordedScaledFontDestruction(ReferencePtr(userData->refPtr))); + userData->recorder->RemoveScaledFont((ScaledFont*)userData->refPtr); + userData->recorder->DecrementUnscaledFontRefCount(userData->unscaledFont); + delete userData; +} + +void DrawTargetRecording::FillGlyphs(ScaledFont* aFont, + const GlyphBuffer& aBuffer, + const Pattern& aPattern, + const DrawOptions& aOptions) { + if (!aFont) { + return; + } + + EnsurePatternDependenciesStored(aPattern); + + UserDataKey* userDataKey = reinterpret_cast<UserDataKey*>(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); + } + + mRecorder->RecordEvent(RecordedFillGlyphs( + this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs)); +} + +void DrawTargetRecording::Mask(const Pattern& aSource, const Pattern& aMask, + const DrawOptions& aOptions) { + EnsurePatternDependenciesStored(aSource); + EnsurePatternDependenciesStored(aMask); + + mRecorder->RecordEvent(RecordedMask(this, aSource, aMask, aOptions)); +} + +void DrawTargetRecording::MaskSurface(const Pattern& aSource, + SourceSurface* aMask, Point aOffset, + const DrawOptions& aOptions) { + if (!aMask) { + return; + } + + EnsurePatternDependenciesStored(aSource); + EnsureSurfaceStoredRecording(mRecorder, aMask, "MaskSurface"); + + mRecorder->RecordEvent( + RecordedMaskSurface(this, aSource, aMask, aOffset, aOptions)); +} + +void DrawTargetRecording::Stroke(const Path* aPath, const Pattern& aPattern, + const StrokeOptions& aStrokeOptions, + const DrawOptions& aOptions) { + RefPtr<PathRecording> pathRecording = EnsurePathStored(aPath); + EnsurePatternDependenciesStored(aPattern); + + mRecorder->RecordEvent( + RecordedStroke(this, pathRecording, aPattern, aStrokeOptions, aOptions)); +} + +already_AddRefed<SourceSurface> DrawTargetRecording::Snapshot() { + RefPtr<SourceSurface> retSurf = + new SourceSurfaceRecording(mRect.Size(), mFormat, mRecorder); + + mRecorder->RecordEvent(RecordedSnapshot(retSurf, this)); + + return retSurf.forget(); +} + +already_AddRefed<SourceSurface> DrawTargetRecording::IntoLuminanceSource( + LuminanceType aLuminanceType, float aOpacity) { + RefPtr<SourceSurface> retSurf = + new SourceSurfaceRecording(mRect.Size(), SurfaceFormat::A8, mRecorder); + + mRecorder->RecordEvent( + RecordedIntoLuminanceSource(retSurf, this, aLuminanceType, aOpacity)); + + return retSurf.forget(); +} + +void DrawTargetRecording::Flush() { + mRecorder->RecordEvent(RecordedFlush(this)); +} + +void DrawTargetRecording::DetachAllSnapshots() { + mRecorder->RecordEvent(RecordedDetachAllSnapshots(this)); +} + +void DrawTargetRecording::DrawSurface(SourceSurface* aSurface, + const Rect& aDest, const Rect& aSource, + const DrawSurfaceOptions& aSurfOptions, + const DrawOptions& aOptions) { + if (!aSurface) { + return; + } + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurface"); + + mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, + aSurfOptions, aOptions)); +} + +void DrawTargetRecording::DrawDependentSurface(uint64_t aId, + const Rect& aDest) { + mRecorder->AddDependentSurface(aId); + mRecorder->RecordEvent(RecordedDrawDependentSurface(this, aId, aDest)); +} + +void DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface* aSurface, + const Point& aDest, + const ShadowOptions& aShadow, + CompositionOp aOp) { + if (!aSurface) { + return; + } + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "DrawSurfaceWithShadow"); + + mRecorder->RecordEvent( + RecordedDrawSurfaceWithShadow(this, aSurface, aDest, aShadow, aOp)); +} + +void DrawTargetRecording::DrawFilter(FilterNode* aNode, const Rect& aSourceRect, + const Point& aDestPoint, + const DrawOptions& aOptions) { + if (!aNode) { + return; + } + + MOZ_ASSERT(mRecorder->HasStoredObject(aNode)); + + mRecorder->RecordEvent( + RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions)); +} + +already_AddRefed<FilterNode> DrawTargetRecording::CreateFilter( + FilterType aType) { + RefPtr<FilterNode> retNode = new FilterNodeRecording(mRecorder); + + mRecorder->RecordEvent(RecordedFilterNodeCreation(this, retNode, aType)); + + return retNode.forget(); +} + +void DrawTargetRecording::ClearRect(const Rect& aRect) { + mRecorder->RecordEvent(RecordedClearRect(this, aRect)); +} + +void DrawTargetRecording::CopySurface(SourceSurface* aSurface, + const IntRect& aSourceRect, + const IntPoint& aDestination) { + if (!aSurface) { + return; + } + + EnsureSurfaceStoredRecording(mRecorder, aSurface, "CopySurface"); + + mRecorder->RecordEvent( + RecordedCopySurface(this, 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> pathRecording = EnsurePathStored(aPath); + + mRecorder->RecordEvent(RecordedPushClip(this, pathRecording)); +} + +void DrawTargetRecording::PushClipRect(const Rect& aRect) { + mRecorder->RecordEvent(RecordedPushClipRect(this, aRect)); +} + +void DrawTargetRecording::PopClip() { + mRecorder->RecordEvent(RecordedPopClip(static_cast<DrawTarget*>(this))); +} + +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(RecordedPushLayer(this, aOpaque, aOpacity, aMask, + aMaskTransform, aBounds, + aCopyBackground)); +} + +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( + RecordedPushLayerWithBlend(this, aOpaque, aOpacity, aMask, aMaskTransform, + aBounds, aCopyBackground, aCompositionOp)); +} + +void DrawTargetRecording::PopLayer() { + mRecorder->RecordEvent(RecordedPopLayer(static_cast<DrawTarget*>(this))); +} + +already_AddRefed<SourceSurface> +DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char* aData, + const IntSize& aSize, + int32_t aStride, + SurfaceFormat aFormat) const { + RefPtr<SourceSurface> surface = CreateDataSourceSurfaceWithStrideFromData( + aSize, aFormat, aStride, aData, aStride); + if (!surface) { + return nullptr; + } + + return OptimizeSourceSurface(surface); +} + +already_AddRefed<SourceSurface> 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<RecordingSourceSurfaceUserData*>( + aSurface->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))); + if (userData) { + RefPtr<SourceSurface> 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<RecordingSourceSurfaceUserData*>( + aSurface->GetUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()))); + MOZ_ASSERT(userData, + "User data should always have been set by " + "EnsureSurfaceStoredRecording."); + } + + RefPtr<SourceSurface> retSurf = new SourceSurfaceRecording( + aSurface->GetSize(), aSurface->GetFormat(), mRecorder, aSurface); + mRecorder->RecordEvent( + RecordedOptimizeSourceSurface(aSurface, this, retSurf)); + userData->optimizedSurface = retSurf; + + return retSurf.forget(); +} + +already_AddRefed<SourceSurface> +DrawTargetRecording::CreateSourceSurfaceFromNativeSurface( + const NativeSurface& aSurface) const { + MOZ_ASSERT(false); + return nullptr; +} + +already_AddRefed<DrawTarget> +DrawTargetRecording::CreateSimilarDrawTargetWithBacking( + const IntSize& aSize, SurfaceFormat aFormat) const { + RefPtr<DrawTarget> 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<SourceSurfaceSharedData>(); + if (surface->Init(aSize, stride, aFormat)) { + auto dt = MakeRefPtr<DrawTargetSkia>(); + if (dt->Init(std::move(surface))) { + return dt.forget(); + } else { + MOZ_ASSERT_UNREACHABLE("Skia should initialize given surface!"); + } + } + } + } + + return CreateSimilarDrawTarget(aSize, aFormat); +} + +already_AddRefed<DrawTarget> DrawTargetRecording::CreateSimilarDrawTarget( + const IntSize& aSize, SurfaceFormat aFormat) const { + RefPtr<DrawTarget> similarDT; + if (mFinalDT->CanCreateSimilarDrawTarget(aSize, aFormat)) { + similarDT = + new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aSize), aFormat); + mRecorder->RecordEvent( + RecordedCreateSimilarDrawTarget(this, 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<DrawTarget> DrawTargetRecording::CreateClippedDrawTarget( + const Rect& aBounds, SurfaceFormat aFormat) { + RefPtr<DrawTarget> similarDT; + similarDT = new DrawTargetRecording(this, mRect, aFormat); + mRecorder->RecordEvent( + RecordedCreateClippedDrawTarget(this, similarDT.get(), aBounds, aFormat)); + similarDT->SetTransform(mTransform); + return similarDT; +} + +already_AddRefed<DrawTarget> +DrawTargetRecording::CreateSimilarDrawTargetForFilter( + const IntSize& aMaxSize, SurfaceFormat aFormat, FilterNode* aFilter, + FilterNode* aSource, const Rect& aSourceRect, const Point& aDestPoint) { + RefPtr<DrawTarget> similarDT; + if (mFinalDT->CanCreateSimilarDrawTarget(aMaxSize, aFormat)) { + similarDT = new DrawTargetRecording(this, IntRect(IntPoint(0, 0), aMaxSize), + aFormat); + mRecorder->RecordEvent(RecordedCreateDrawTargetForFilter( + this, 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<PathBuilder> DrawTargetRecording::CreatePathBuilder( + FillRule aFillRule) const { + return MakeAndAddRef<PathBuilderRecording>(mFinalDT->GetBackendType(), + aFillRule); +} + +already_AddRefed<GradientStops> DrawTargetRecording::CreateGradientStops( + GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { + RefPtr<GradientStops> retStops = new GradientStopsRecording(mRecorder); + + mRecorder->RecordEvent(RecordedGradientStopsCreation(this, retStops, aStops, + aNumStops, aExtendMode)); + + return retStops.forget(); +} + +void DrawTargetRecording::SetTransform(const Matrix& aTransform) { + mRecorder->RecordEvent(RecordedSetTransform(this, aTransform)); + DrawTarget::SetTransform(aTransform); +} + +already_AddRefed<PathRecording> DrawTargetRecording::EnsurePathStored( + const Path* aPath) { + RefPtr<PathRecording> pathRecording; + if (aPath->GetBackendType() == BackendType::RECORDING) { + pathRecording = + const_cast<PathRecording*>(static_cast<const PathRecording*>(aPath)); + if (!mRecorder->TryAddStoredObject(pathRecording)) { + // Path is already stored. + return pathRecording.forget(); + } + } else { + MOZ_ASSERT(!mRecorder->HasStoredObject(aPath)); + FillRule fillRule = aPath->GetFillRule(); + RefPtr<PathBuilderRecording> builderRecording = + new PathBuilderRecording(mFinalDT->GetBackendType(), fillRule); + aPath->StreamToSink(builderRecording); + pathRecording = builderRecording->Finish().downcast<PathRecording>(); + 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(RecordedPathCreation(this, 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. + mRecorder->RecordEvent( + RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mRect, + mFinalDT->GetFormat(), false, nullptr)); + // Add the current transform to the new recording + mRecorder->RecordEvent( + RecordedSetTransform(this, 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<const LinearGradientPattern*>(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast<const LinearGradientPattern*>(&aPattern)->mStops)); + return; + } + case PatternType::RADIAL_GRADIENT: { + MOZ_ASSERT_IF( + static_cast<const RadialGradientPattern*>(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast<const RadialGradientPattern*>(&aPattern)->mStops)); + return; + } + case PatternType::CONIC_GRADIENT: { + MOZ_ASSERT_IF( + static_cast<const ConicGradientPattern*>(&aPattern)->mStops, + mRecorder->HasStoredObject( + static_cast<const ConicGradientPattern*>(&aPattern)->mStops)); + return; + } + case PatternType::SURFACE: { + const SurfacePattern* pat = static_cast<const SurfacePattern*>(&aPattern); + EnsureSurfaceStoredRecording(mRecorder, pat->mSurface, + "EnsurePatternDependenciesStored"); + return; + } + } +} + +} // namespace gfx +} // namespace mozilla |