/* -*- 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" namespace mozilla { namespace gfx { class SourceSurfaceRecording : public SourceSurface { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurfaceRecording) SourceSurfaceRecording(SourceSurface *aFinalSurface, DrawEventRecorderPrivate *aRecorder) : mFinalSurface(aFinalSurface), mRecorder(aRecorder) { } ~SourceSurfaceRecording() { 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 TemporaryRef 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) { } ~GradientStopsRecording() { 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) using FilterNode::SetAttribute; FilterNodeRecording(FilterNode *aFinalFilterNode, DrawEventRecorderPrivate *aRecorder) : mFinalFilterNode(aFinalFilterNode), mRecorder(aRecorder) { } ~FilterNodeRecording() { mRecorder->RecordEvent(RecordedFilterNodeDestruction(this)); } virtual void SetInput(uint32_t aIndex, SourceSurface *aSurface) { mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aSurface)); mFinalFilterNode->SetInput(aIndex, GetSourceSurface(aSurface)); } virtual void SetInput(uint32_t aIndex, FilterNode *aFilter) { FilterNode *finalNode = aFilter; if (aFilter->GetBackendType() != FILTER_BACKEND_RECORDING) { gfxWarning() << "Non recording filter node used with recording DrawTarget!"; } else { finalNode = static_cast(aFilter)->mFinalFilterNode; } mRecorder->RecordEvent(RecordedFilterNodeSetInput(this, aIndex, aFilter)); mFinalFilterNode->SetInput(aIndex, finalNode); } #define FORWARD_SET_ATTRIBUTE(type, argtype) \ virtual void SetAttribute(uint32_t aIndex, type aValue) { \ 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 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) { mRecorder->RecordEvent(RecordedFilterNodeSetAttribute(this, aIndex, aFloat, aSize)); mFinalFilterNode->SetAttribute(aIndex, aFloat, aSize); } virtual FilterBackend GetBackendType() MOZ_OVERRIDE { return FILTER_BACKEND_RECORDING; } RefPtr mFinalFilterNode; RefPtr mRecorder; }; 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; } struct AdjustedPattern { 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->mFilter); 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->GetType(), mFinalDT->GetSize(), mFinalDT->GetFormat(), aHasData, snapshot)); mFormat = mFinalDT->GetFormat(); } DrawTargetRecording::~DrawTargetRecording() { mRecorder->RecordEvent(RecordedDrawTargetDestruction(this)); } void DrawTargetRecording::FillRect(const Rect &aRect, const Pattern &aPattern, const DrawOptions &aOptions) { 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) { 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) { mRecorder->RecordEvent(RecordedStrokeLine(this, aBegin, aEnd, aPattern, aStrokeOptions, aOptions)); mFinalDT->StrokeLine(aBegin, aEnd, *AdjustedPattern(aPattern), aStrokeOptions, aOptions); } Path* DrawTargetRecording::GetPathForPathRecording(const Path *aPath) const { if (aPath->GetBackendType() != BackendType::RECORDING) { return nullptr; } return static_cast(aPath)->mPath; } void DrawTargetRecording::Fill(const Path *aPath, const Pattern &aPattern, const DrawOptions &aOptions) { EnsureStored(aPath); mRecorder->RecordEvent(RecordedFill(this, const_cast(aPath), aPattern, aOptions)); mFinalDT->Fill(GetPathForPathRecording(aPath), *AdjustedPattern(aPattern), aOptions); } struct RecordingFontUserData { void *refPtr; RefPtr recorder; }; void RecordingFontUserDataDestroyFunc(void *aUserData) { RecordingFontUserData *userData = static_cast(aUserData); // TODO support font in b2g recordings #ifndef MOZ_WIDGET_GONK userData->recorder->RecordEvent(RecordedScaledFontDestruction(userData->refPtr)); #endif delete userData; } void DrawTargetRecording::FillGlyphs(ScaledFont *aFont, const GlyphBuffer &aBuffer, const Pattern &aPattern, const DrawOptions &aOptions, const GlyphRenderingOptions *aRenderingOptions) { if (!aFont->GetUserData(reinterpret_cast(mRecorder.get()))) { // TODO support font in b2g recordings #ifndef MOZ_WIDGET_GONK mRecorder->RecordEvent(RecordedScaledFontCreation(aFont, aFont)); #endif RecordingFontUserData *userData = new RecordingFontUserData; userData->refPtr = aFont; userData->recorder = mRecorder; aFont->AddUserData(reinterpret_cast(mRecorder.get()), userData, &RecordingFontUserDataDestroyFunc); } // TODO support font in b2g recordings #ifndef MOZ_WIDGET_GONK mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs)); #endif mFinalDT->FillGlyphs(aFont, aBuffer, aPattern, aOptions, aRenderingOptions); } void DrawTargetRecording::Mask(const Pattern &aSource, const Pattern &aMask, const DrawOptions &aOptions) { 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) { 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) { EnsureStored(aPath); mRecorder->RecordEvent(RecordedStroke(this, const_cast(aPath), aPattern, aStrokeOptions, aOptions)); mFinalDT->Stroke(GetPathForPathRecording(aPath), *AdjustedPattern(aPattern), aStrokeOptions, aOptions); } TemporaryRef DrawTargetRecording::Snapshot() { RefPtr surf = mFinalDT->Snapshot(); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); mRecorder->RecordEvent(RecordedSnapshot(retSurf, this)); return retSurf; } void DrawTargetRecording::DrawSurface(SourceSurface *aSurface, const Rect &aDest, const Rect &aSource, const DrawSurfaceOptions &aSurfOptions, const DrawOptions &aOptions) { 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) { 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) { mRecorder->RecordEvent(RecordedDrawFilter(this, aNode, aSourceRect, aDestPoint, aOptions)); mFinalDT->DrawFilter(GetFilterNode(aNode), aSourceRect, aDestPoint, aOptions); } TemporaryRef DrawTargetRecording::CreateFilter(FilterType aType) { RefPtr node = mFinalDT->CreateFilter(aType); RefPtr retNode = new FilterNodeRecording(node, mRecorder); mRecorder->RecordEvent(RecordedFilterNodeCreation(retNode, aType)); return retNode; } 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) { mRecorder->RecordEvent(RecordedCopySurface(this, aSurface, aSourceRect, aDestination)); mFinalDT->CopySurface(GetSourceSurface(aSurface), aSourceRect, aDestination); } void DrawTargetRecording::PushClip(const Path *aPath) { EnsureStored(aPath); mRecorder->RecordEvent(RecordedPushClip(this, const_cast(aPath))); mFinalDT->PushClip(GetPathForPathRecording(aPath)); } void DrawTargetRecording::PushClipRect(const Rect &aRect) { mRecorder->RecordEvent(RecordedPushClipRect(this, aRect)); mFinalDT->PushClipRect(aRect); } void DrawTargetRecording::PopClip() { mRecorder->RecordEvent(RecordedPopClip(this)); mFinalDT->PopClip(); } TemporaryRef 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; } TemporaryRef 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(); } if (!dataSurf) { gfxWarning() << "Recording failed to record SourceSurface created from OptimizeSourceSurface"; // Insert a bogus source surface. uint8_t *sourceData = new uint8_t[surf->GetSize().width * surf->GetSize().height * BytesPerPixel(surf->GetFormat())]; memset(sourceData, 0, surf->GetSize().width * surf->GetSize().height * BytesPerPixel(surf->GetFormat())); mRecorder->RecordEvent( RecordedSourceSurfaceCreation(retSurf, sourceData, surf->GetSize().width * BytesPerPixel(surf->GetFormat()), surf->GetSize(), surf->GetFormat())); delete [] sourceData; } else { mRecorder->RecordEvent( RecordedSourceSurfaceCreation(retSurf, dataSurf->GetData(), dataSurf->Stride(), dataSurf->GetSize(), dataSurf->GetFormat())); } return retSurf; } TemporaryRef DrawTargetRecording::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const { RefPtr surf = mFinalDT->CreateSourceSurfaceFromNativeSurface(aSurface); RefPtr retSurf = new SourceSurfaceRecording(surf, mRecorder); RefPtr dataSurf = surf->GetDataSurface(); if (!dataSurf) { gfxWarning() << "Recording failed to record SourceSurface created from OptimizeSourceSurface"; // Insert a bogus source surface. uint8_t *sourceData = new uint8_t[surf->GetSize().width * surf->GetSize().height * BytesPerPixel(surf->GetFormat())]; memset(sourceData, 0, surf->GetSize().width * surf->GetSize().height * BytesPerPixel(surf->GetFormat())); mRecorder->RecordEvent( RecordedSourceSurfaceCreation(retSurf, sourceData, surf->GetSize().width * BytesPerPixel(surf->GetFormat()), surf->GetSize(), surf->GetFormat())); delete [] sourceData; } else { mRecorder->RecordEvent( RecordedSourceSurfaceCreation(retSurf, dataSurf->GetData(), dataSurf->Stride(), dataSurf->GetSize(), dataSurf->GetFormat())); } return retSurf; } TemporaryRef DrawTargetRecording::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const { RefPtr dt = mFinalDT->CreateSimilarDrawTarget(aSize, aFormat); RefPtr retDT = new DrawTargetRecording(mRecorder.get(), dt); return retDT; } TemporaryRef DrawTargetRecording::CreatePathBuilder(FillRule aFillRule) const { RefPtr builder = mFinalDT->CreatePathBuilder(aFillRule); return new PathBuilderRecording(builder, aFillRule); } TemporaryRef 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; } void DrawTargetRecording::SetTransform(const Matrix &aTransform) { mRecorder->RecordEvent(RecordedSetTransform(this, aTransform)); DrawTarget::SetTransform(aTransform); mFinalDT->SetTransform(aTransform); } void DrawTargetRecording::EnsureStored(const Path *aPath) { if (!mRecorder->HasStoredPath(aPath)) { if (aPath->GetBackendType() != BackendType::RECORDING) { gfxWarning() << "Cannot record this fill path properly!"; } else { PathRecording *recPath = const_cast(static_cast(aPath)); mRecorder->RecordEvent(RecordedPathCreation(recPath)); mRecorder->AddStoredPath(aPath); recPath->mStoredRecorders.push_back(mRecorder); } } } } }