/* -*- 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 "DrawTargetRecording.h" #include "PathRecording.h" #include #include "Logging.h" #include "Tools.h" #include "Filters.h" #include "mozilla/UniquePtr.h" #include "RecordingTypes.h" namespace mozilla { namespace gfx { struct RecordingSourceSurfaceUserData { void *refPtr; RefPtr recorder; }; void RecordingSourceSurfaceUserDataFunc(void *aUserData) { RecordingSourceSurfaceUserData *userData = static_cast(aUserData); userData->recorder->RemoveStoredObject(userData->refPtr); userData->recorder->RecordEvent( RecordedSourceSurfaceDestruction(userData->refPtr)); delete userData; } static void StoreSourceSurface(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface, DataSourceSurface *aDataSurf, const char *reason) { if (!aDataSurf) { gfxWarning() << "Recording failed to record SourceSurface for " << reason; // Insert a bogus source surface. int32_t stride = aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()); UniquePtr sourceData(new uint8_t[stride * aSurface->GetSize().height]()); aRecorder->RecordEvent( RecordedSourceSurfaceCreation(aSurface, sourceData.get(), stride, aSurface->GetSize(), aSurface->GetFormat())); } else { DataSourceSurface::ScopedMap map(aDataSurf, DataSourceSurface::READ); aRecorder->RecordEvent( RecordedSourceSurfaceCreation(aSurface, map.GetData(), map.GetStride(), aDataSurf->GetSize(), aDataSurf->GetFormat())); } } static void EnsureSurfaceStored(DrawEventRecorderPrivate *aRecorder, SourceSurface *aSurface, const char *reason) { if (aRecorder->HasStoredObject(aSurface)) { return; } RefPtr dataSurf = aSurface->GetDataSurface(); StoreSourceSurface(aRecorder, aSurface, dataSurf, reason); aRecorder->AddStoredObject(aSurface); RecordingSourceSurfaceUserData *userData = new RecordingSourceSurfaceUserData; userData->refPtr = aSurface; userData->recorder = aRecorder; aSurface->AddUserData(reinterpret_cast(aRecorder), userData, &RecordingSourceSurfaceUserDataFunc); return; } class SourceSurfaceRecording : public SourceSurface { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording) SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder) : mFinalSurface(aFinalSurface), mRecorder(aRecorder) { mRecorder->AddStoredObject(this); } ~SourceSurfaceRecording() { mRecorder->RemoveStoredObject(this); mRecorder->RecordEvent(RecordedSourceSurfaceDestruction(this)); } virtual SurfaceType GetType() const { return SurfaceType::RECORDING; } virtual IntSize GetSize() const { return mFinalSurface->GetSize(); } virtual SurfaceFormat GetFormat() const { return mFinalSurface->GetFormat(); } virtual already_AddRefed GetDataSurface() { return mFinalSurface->GetDataSurface(); } RefPtr mFinalSurface; RefPtr mRecorder; }; class GradientStopsRecording : public GradientStops { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsRecording) GradientStopsRecording(GradientStops *aFinalGradientStops, DrawEventRecorderPrivate *aRecorder) : mFinalGradientStops(aFinalGradientStops), mRecorder(aRecorder) { mRecorder->AddStoredObject(this); } ~GradientStopsRecording() { mRecorder->RemoveStoredObject(this); mRecorder->RecordEvent(RecordedGradientStopsDestruction(this)); } virtual BackendType GetBackendType() const { return BackendType::RECORDING; } RefPtr mFinalGradientStops; RefPtr mRecorder; }; static SourceSurface * GetSourceSurface(SourceSurface *aSurface) { if (aSurface->GetType() != SurfaceType::RECORDING) { return aSurface; } return static_cast(aSurface)->mFinalSurface; } static GradientStops * GetGradientStops(GradientStops *aStops) { if (aStops->GetBackendType() != BackendType::RECORDING) { return aStops; } return static_cast(aStops)->mFinalGradientStops; } class FilterNodeRecording : public FilterNode { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(FilterNodeRecording, override) using FilterNode::SetAttribute; FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder) : mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder) { mRecorder->AddStoredObject(this); } ~FilterNodeRecording() { mRecorder->RemoveStoredObject(this); mRecorder->RecordEvent(RecordedFilterNodeDestruction(this)); } static FilterNode* GetFilterNode(FilterNode* aNode) { if (aNode->GetBackendType() != FILTER_BACKEND_RECORDING) { gfxWarning() << "Non recording filter node used with recording DrawTarget!"; return aNode; } return static_cast(aNode)->mFinalFilterNode; } virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) override { EnsureSurfaceStored(mRecorder, aSurface, "SetInput"); mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface)); mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface)); } virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) override { MOZ_ASSERT(mRecorder->HasStoredObject(aFilter)); mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter)); mFinalFilterNode->SetInput(aIndex, GetFilterNode(aFilter)); } #define FORWARD_SET_ATTRIBUTE(type, argtype) \ virtual void SetAttribute(uint32_t aIndex, type aValue) override { \ mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aValue, RecordedFilterNodeSetAttribute::ARGTYPE_##argtype)); \ mFinalFilterNode->SetAttribute(aIndex, aValue); \ } 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 Color&, COLOR); #undef FORWARD_SET_ATTRIBUTE virtual void SetAttribute(uint32_t aIndex, const Float* aFloat, uint32_t aSize) override { mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize)); mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize); } virtual FilterBackend GetBackendType() override { return FILTER_BACKEND_RECORDING; } RefPtr mFinalFilterNode; RefPtr mRecorder; }; struct AdjustedPattern { explicit AdjustedPattern(const Pattern &aPattern) : mPattern(nullptr) { mOrigPattern = const_cast(&aPattern); } ~AdjustedPattern() { if (mPattern) { mPattern->~Pattern(); } } operator Pattern*() { switch(mOrigPattern->GetType()) { case PatternType::COLOR: return mOrigPattern; case PatternType::SURFACE: { SurfacePattern *surfPat = static_cast(mOrigPattern); mPattern = new (mSurfPat) SurfacePattern(GetSourceSurface(surfPat->mSurface), surfPat->mExtendMode, surfPat->mMatrix, surfPat->mSamplingFilter, surfPat->mSamplingRect); return mPattern; } case PatternType::LINEAR_GRADIENT: { LinearGradientPattern *linGradPat = static_cast(mOrigPattern); mPattern = new (mLinGradPat) LinearGradientPattern(linGradPat->mBegin, linGradPat->mEnd, GetGradientStops(linGradPat->mStops), linGradPat->mMatrix); return mPattern; } case PatternType::RADIAL_GRADIENT: { RadialGradientPattern *radGradPat = static_cast(mOrigPattern); mPattern = new (mRadGradPat) RadialGradientPattern(radGradPat->mCenter1, radGradPat->mCenter2, radGradPat->mRadius1, radGradPat->mRadius2, GetGradientStops(radGradPat->mStops), radGradPat->mMatrix); return mPattern; } default: return new (mColPat) ColorPattern(Color()); } return mPattern; } union { char mColPat[sizeof(ColorPattern)]; char mLinGradPat[sizeof(LinearGradientPattern)]; char mRadGradPat[sizeof(RadialGradientPattern)]; char mSurfPat[sizeof(SurfacePattern)]; }; Pattern *mOrigPattern; Pattern *mPattern; }; DrawTargetRecording::DrawTargetRecording(DrawEventRecorder *aRecorder, DrawTarget *aDT, bool aHasData) : mRecorder(static_cast(aRecorder)) , mFinalDT(aDT) { RefPtr snapshot = aHasData ? mFinalDT->Snapshot() : nullptr; mRecorder->RecordEvent(RecordedDrawTargetCreation(this, mFinalDT->GetBackendType(), mFinalDT->GetSize(), mFinalDT->GetFormat(), aHasData, snapshot)); mFormat = mFinalDT->GetFormat(); } DrawTargetRecording::DrawTargetRecording(const DrawTargetRecording *aDT, DrawTarget *aSimilarDT) : mRecorder(aDT->mRecorder) , mFinalDT(aSimilarDT) { mRecorder->RecordEvent(RecordedCreateSimilarDrawTarget(this, mFinalDT->GetSize(), mFinalDT->GetFormat())); mFormat = mFinalDT->GetFormat(); } DrawTargetRecording::~DrawTargetRecording() { mRecorder->RecordEvent(RecordedDrawTargetDestruction(this)); } void DrawTargetRecording::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) { EnsurePatternDependenciesStored(aPattern); mRecorder->RecordEvent(RecordedFillRect(this, aRect, aPattern, aOptions)); mFinalDT->FillRect(aRect, *AdjustedPattern(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)); mFinalDT->StrokeRect(aRect, *AdjustedPattern(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)); mFinalDT->StrokeLine(aBegin, aEnd, *AdjustedPattern(aPattern), aStrokeOptions, aOptions); } void DrawTargetRecording::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) { RefPtr pathRecording = EnsurePathStored(aPath); EnsurePatternDependenciesStored(aPattern); mRecorder->RecordEvent(RecordedFill(this, pathRecording, aPattern, aOptions)); mFinalDT->Fill(pathRecording->mPath, *AdjustedPattern(aPattern), aOptions); } struct RecordingFontUserData { void *refPtr; RefPtr recorder; }; void RecordingFontUserDataDestroyFunc(void *aUserData) { RecordingFontUserData *userData = static_cast(aUserData); userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr)); delete userData; } void DrawTargetRecording::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions, const GlyphRenderingOptions *aRenderingOptions) { EnsurePatternDependenciesStored(aPattern); if (!aFont->GetUserData(reinterpret_cast(mRecorder.get()))) { RecordedFontData fontData(aFont); 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(RecordedScaledFontCreation(aFont, fontDetails)); } else { // If that fails, record just the font description and try to load it from // the system on the other side. RecordedFontDescriptor fontDesc(aFont); if (fontDesc.IsValid()) { mRecorder->RecordEvent(fontDesc); } else { gfxWarning() << "DrawTargetRecording::FillGlyphs failed to serialise ScaledFont"; } } RecordingFontUserData *userData = new RecordingFontUserData; userData->refPtr = aFont; userData->recorder = mRecorder; aFont->AddUserData(reinterpret_cast(mRecorder.get()), userData, &RecordingFontUserDataDestroyFunc); } mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs)); mFinalDT->FillGlyphs(aFont, aBuffer, *AdjustedPattern(aPattern), aOptions, aRenderingOptions); } void DrawTargetRecording::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) { EnsurePatternDependenciesStored(aSource); EnsurePatternDependenciesStored(aMask); mRecorder->RecordEvent(RecordedMask(this, aSource, aMask, aOptions)); mFinalDT->Mask(*AdjustedPattern(aSource), *AdjustedPattern(aMask), aOptions); } void DrawTargetRecording::MaskSurface(const Pattern &aSource, SourceSurface *aMask, Point aOffset, const DrawOptions &aOptions) { EnsurePatternDependenciesStored(aSource); EnsureSurfaceStored(mRecorder, aMask, "MaskSurface"); mRecorder->RecordEvent(RecordedMaskSurface(this, aSource, aMask, aOffset, aOptions)); mFinalDT->MaskSurface(*AdjustedPattern(aSource), GetSourceSurface(aMask), aOffset, aOptions); } void DrawTargetRecording::Stroke(const Path *aPath, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aOptions) { RefPtr pathRecording = EnsurePathStored(aPath); EnsurePatternDependenciesStored(aPattern); mRecorder->RecordEvent(RecordedStroke(this, pathRecording, aPattern, aStrokeOptions, aOptions)); mFinalDT->Stroke(pathRecording->mPath, *AdjustedPattern(aPattern), aStrokeOptions, aOptions); } already_AddRefed DrawTargetRecording::Snapshot() { RefPtr surf = mFinalDT->Snapshot(); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); mRecorder->RecordEvent(RecordedSnapshot(retSurf, this)); return retSurf.forget(); } void DrawTargetRecording::DetachAllSnapshots() { mFinalDT->DetachAllSnapshots(); } void DrawTargetRecording::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { EnsureSurfaceStored(mRecorder, aSurface, "DrawSurface"); mRecorder->RecordEvent(RecordedDrawSurface(this, aSurface, aDest, aSource, aSurfOptions, aOptions)); mFinalDT->DrawSurface(GetSourceSurface(aSurface), aDest, aSource, aSurfOptions, aOptions); } void DrawTargetRecording::DrawSurfaceWithShadow(SourceSurface *aSurface, const Point &aDest, const Color &aColor, const Point &aOffset, Float aSigma, CompositionOp aOp) { EnsureSurfaceStored(mRecorder, aSurface, "DrawSurfaceWithShadow"); mRecorder->RecordEvent(RecordedDrawSurfaceWithShadow(this, aSurface, aDest, aColor, aOffset, aSigma, aOp)); mFinalDT->DrawSurfaceWithShadow(GetSourceSurface(aSurface), aDest, aColor, aOffset, aSigma, aOp); } void DrawTargetRecording::DrawFilter(FilterNode *aNode, const Rect &aSourceRect, const Point &aDestPoint, const DrawOptions &aOptions) { MOZ_ASSERT(mRecorder->HasStoredObject(aNode)); mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions)); mFinalDT->DrawFilter(FilterNodeRecording::GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions); } already_AddRefed DrawTargetRecording::CreateFilter(FilterType aType) { RefPtr node = mFinalDT->CreateFilter(aType); RefPtr retNode = new FilterNodeRecording(node, mRecorder); mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType)); return retNode.forget(); } void DrawTargetRecording::ClearRect(const Rect &aRect) { mRecorder->RecordEvent(RecordedClearRect(this, aRect)); mFinalDT->ClearRect(aRect); } void DrawTargetRecording::CopySurface(SourceSurface *aSurface, const IntRect &aSourceRect, const IntPoint &aDestination) { EnsureSurfaceStored(mRecorder, aSurface, "CopySurface"); mRecorder->RecordEvent(RecordedCopySurface(this, aSurface, aSourceRect, aDestination)); mFinalDT->CopySurface(GetSourceSurface(aSurface), aSourceRect, aDestination); } void DrawTargetRecording::PushClip(const Path *aPath) { RefPtr pathRecording = EnsurePathStored(aPath); mRecorder->RecordEvent(RecordedPushClip(this, pathRecording)); mFinalDT->PushClip(pathRecording->mPath); } void DrawTargetRecording::PushClipRect(const Rect &aRect) { mRecorder->RecordEvent(RecordedPushClipRect(this, aRect)); mFinalDT->PushClipRect(aRect); } void DrawTargetRecording::PopClip() { mRecorder->RecordEvent(RecordedPopClip(this)); mFinalDT->PopClip(); } void DrawTargetRecording::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, const Matrix& aMaskTransform, const IntRect& aBounds, bool aCopyBackground) { if (aMask) { EnsureSurfaceStored(mRecorder, aMask, "PushLayer"); } mRecorder->RecordEvent(RecordedPushLayer(this, aOpaque, aOpacity, aMask, aMaskTransform, aBounds, aCopyBackground)); mFinalDT->PushLayer(aOpaque, aOpacity, aMask, aMaskTransform, aBounds, aCopyBackground); } void DrawTargetRecording::PopLayer() { mRecorder->RecordEvent(RecordedPopLayer(this)); mFinalDT->PopLayer(); } already_AddRefed DrawTargetRecording::CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, int32_t aStride, SurfaceFormat aFormat) const { RefPtr surf = mFinalDT->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); mRecorder->RecordEvent(RecordedSourceSurfaceCreation(retSurf, aData, aStride, aSize, aFormat)); return retSurf.forget(); } already_AddRefed DrawTargetRecording::OptimizeSourceSurface(SourceSurface *aSurface) const { RefPtr surf = mFinalDT->OptimizeSourceSurface(aSurface); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); RefPtr dataSurf = surf->GetDataSurface(); if (!dataSurf) { // Let's try get it off the original surface. dataSurf = aSurface->GetDataSurface(); } StoreSourceSurface(mRecorder, retSurf, dataSurf, "OptimizeSourceSurface"); return retSurf.forget(); } already_AddRefed DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { RefPtr surf = mFinalDT->CreateSourceSurfaceFromNativeSurface(aSurface); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); RefPtr dataSurf = surf->GetDataSurface(); StoreSourceSurface(mRecorder, retSurf, dataSurf, "CreateSourceSurfaceFromNativeSurface"); return retSurf.forget(); } already_AddRefed DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { RefPtr similarDT = mFinalDT->CreateSimilarDrawTarget(aSize, aFormat); if (!similarDT) { return nullptr; } similarDT = new DrawTargetRecording(this, similarDT); return similarDT.forget(); } already_AddRefed DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const { RefPtr builder = mFinalDT->CreatePathBuilder(aFillRule); return MakeAndAddRef(builder, aFillRule); } already_AddRefed DrawTargetRecording::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops, ExtendMode aExtendMode) const { RefPtr stops = mFinalDT->CreateGradientStops(aStops, aNumStops, aExtendMode); RefPtr retStops = new GradientStopsRecording(stops, mRecorder); mRecorder->RecordEvent(RecordedGradientStopsCreation(retStops, aStops, aNumStops, aExtendMode)); return retStops.forget(); } void DrawTargetRecording::SetTransform(const Matrix &aTransform) { mRecorder->RecordEvent(RecordedSetTransform(this, aTransform)); DrawTarget::SetTransform(aTransform); mFinalDT->SetTransform(aTransform); } already_AddRefed DrawTargetRecording::EnsurePathStored(const Path *aPath) { RefPtr pathRecording; if (aPath->GetBackendType() == BackendType::RECORDING) { pathRecording = const_cast(static_cast(aPath)); if (mRecorder->HasStoredObject(aPath)) { return pathRecording.forget(); } } else { MOZ_ASSERT(!mRecorder->HasStoredObject(aPath)); FillRule fillRule = aPath->GetFillRule(); RefPtr builder = mFinalDT->CreatePathBuilder(fillRule); RefPtr builderRecording = new PathBuilderRecording(builder, fillRule); aPath->StreamToSink(builderRecording); pathRecording = builderRecording->Finish().downcast(); } mRecorder->RecordEvent(RecordedPathCreation(pathRecording)); mRecorder->AddStoredObject(pathRecording); pathRecording->mStoredRecorders.push_back(mRecorder); return pathRecording.forget(); } void DrawTargetRecording::EnsurePatternDependenciesStored(const Pattern &aPattern) { switch (aPattern.GetType()) { case PatternType::COLOR: // No dependencies here. return; case PatternType::LINEAR_GRADIENT: { MOZ_ASSERT(mRecorder->HasStoredObject(static_cast(&aPattern)->mStops)); return; } case PatternType::RADIAL_GRADIENT: { MOZ_ASSERT(mRecorder->HasStoredObject(static_cast(&aPattern)->mStops)); return; } case PatternType::SURFACE: { const SurfacePattern *pat = static_cast(&aPattern); EnsureSurfaceStored(mRecorder, pat->mSurface, "EnsurePatternDependenciesStored"); return; } } } } // namespace gfx } // namespace mozilla