From 0e8aa9df466bff9033f2ef2b5717247de31af501 Mon Sep 17 00:00:00 2001 From: Matt Woodrow Date: Tue, 9 Apr 2013 16:51:44 +1200 Subject: [PATCH] Bug 857895 - Run canvas rendering asynchronously on OSX. r=Bas,bholley --- dom/canvas/CanvasRenderingContext2D.cpp | 210 ++++++++++++++++++++++-- dom/canvas/CanvasRenderingContext2D.h | 78 ++++++++- dom/media/MediaPromise.h | 2 + dom/media/MediaTaskQueue.cpp | 1 + gfx/2d/2D.h | 12 +- gfx/2d/DrawCommand.h | 32 +++- gfx/2d/DrawTargetCapture.cpp | 14 ++ gfx/2d/DrawTargetCapture.h | 2 +- 8 files changed, 320 insertions(+), 31 deletions(-) diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 1b3eff6fda1..0f515197d47 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -19,6 +19,8 @@ #include "nsSVGEffects.h" #include "nsPresContext.h" #include "nsIPresShell.h" +#include "nsWidgetsCID.h" +#include "nsIAppShell.h" #include "nsIInterfaceRequestorUtils.h" #include "nsIFrame.h" @@ -114,6 +116,7 @@ #include "nsDeviceContext.h" #include "nsFontMetrics.h" #include "Units.h" +#include "mozilla/Services.h" #undef free // apparently defined by some windows header, clashing with a free() // method in SkTypes.h @@ -179,6 +182,64 @@ public: NS_IMPL_ISUPPORTS(Canvas2dPixelsReporter, nsIMemoryReporter) +class CanvasShutdownObserver : public nsIObserver +{ + virtual ~CanvasShutdownObserver() {} + +public: + NS_DECL_ISUPPORTS + + explicit CanvasShutdownObserver(CanvasRenderingContext2D* aCanvas) + : mCanvas(aCanvas) + { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false); + } + + void Shutdown() { + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + observerService->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID); + } + + NS_IMETHOD Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) override + { + mCanvas->ShutdownTaskQueue(); + return NS_OK; + } + +private: + CanvasRenderingContext2D* mCanvas; +}; + +NS_IMPL_ISUPPORTS(CanvasShutdownObserver, nsIObserver); + + +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); + +void +CanvasRenderingContext2D::RecordCommand() +{ + static uint32_t kBatchSize = 5; + if (++mPendingCommands > kBatchSize) { + mPendingCommands = 0; + FlushDelayedTarget(); + return; + } + + if (mScheduledFlush) { + return; + } + + mScheduledFlush = true; + nsCOMPtr appShell = do_GetService(kAppShellCID); + nsCOMPtr r = NS_NewRunnableMethod(this, &CanvasRenderingContext2D::StableStateReached); + appShell->RunInStableState(r); +} + class CanvasRadialGradient : public CanvasGradient { public: @@ -393,6 +454,11 @@ public: mCtx->CurrentState().filterAdditionalImages, mPostFilterBounds.TopLeft() - mOffset, DrawOptions(1.0f, mCompositionOp)); + + // DrawTargetCapture doesn't properly support filter nodes because they are + // mutable. Block until drawing is done to avoid races. + mCtx->FlushDelayedTarget(); + mCtx->FinishDelayedRendering(); } DrawTarget* DT() @@ -817,6 +883,9 @@ public: if (!context || !context->mTarget) return; + context->FlushDelayedTarget(); + context->FinishDelayedRendering(); + // Since SkiaGL default to store drawing command until flush // We will have to flush it before present. context->mTarget->Flush(); @@ -938,12 +1007,23 @@ CanvasRenderingContext2D::CanvasRenderingContext2D() , mZero(false), mOpaque(false) , mResetLayer(true) , mIPC(false) + , mPendingCommands(0) + , mScheduledFlush(false) , mDrawObserver(nullptr) , mIsEntireFrameInvalid(false) , mPredictManyRedrawCalls(false), mPathTransformWillUpdate(false) , mInvalidateCount(0) { sNumLivingContexts++; + EnsureMediaPromiseLog(); + +#ifdef XP_MACOSX + // Restrict async rendering to OSX for now until the failures on other + // platforms get resolved. + mTaskQueue = new MediaTaskQueue(SharedThreadPool::Get(NS_LITERAL_CSTRING("Canvas Rendering"), + 4)); + mShutdownObserver = new CanvasShutdownObserver(this); +#endif // The default is to use OpenGL mode if (!gfxPlatform::GetPlatform()->UseAcceleratedSkiaCanvas()) { @@ -957,6 +1037,9 @@ CanvasRenderingContext2D::CanvasRenderingContext2D() CanvasRenderingContext2D::~CanvasRenderingContext2D() { + if (mTaskQueue) { + ShutdownTaskQueue(); + } RemoveDrawObserver(); RemovePostRefreshObserver(); Reset(); @@ -979,6 +1062,19 @@ CanvasRenderingContext2D::~CanvasRenderingContext2D() RemoveDemotableContext(this); } +void +CanvasRenderingContext2D::ShutdownTaskQueue() +{ + mShutdownObserver->Shutdown(); + mShutdownObserver = nullptr; + FlushDelayedTarget(); + FinishDelayedRendering(); + mTaskQueue->BeginShutdown(); + mTaskQueue = nullptr; + mDelayedTarget = nullptr; +} + + JSObject* CanvasRenderingContext2D::WrapObject(JSContext *cx, JS::Handle aGivenProto) { @@ -1034,7 +1130,10 @@ CanvasRenderingContext2D::Reset() gCanvasAzureMemoryUsed -= mWidth * mHeight * 4; } + FinishDelayedRendering(); mTarget = nullptr; + mDelayedTarget = nullptr; + mFinalTarget = nullptr; // reset hit regions mHitRegionsOptions.ClearAndRetainStorage(); @@ -1101,6 +1200,8 @@ CanvasRenderingContext2D::StyleColorToString(const nscolor& aColor, nsAString& a nsresult CanvasRenderingContext2D::Redraw() { + RecordCommand(); + if (mIsEntireFrameInvalid) { return NS_OK; } @@ -1122,6 +1223,7 @@ CanvasRenderingContext2D::Redraw() void CanvasRenderingContext2D::Redraw(const mgfx::Rect &r) { + RecordCommand(); ++mInvalidateCount; if (mIsEntireFrameInvalid) { @@ -1144,6 +1246,18 @@ CanvasRenderingContext2D::Redraw(const mgfx::Rect &r) mCanvasElement->InvalidateCanvasContent(&r); } +TemporaryRef +CanvasRenderingContext2D::GetSurfaceSnapshot(bool* aPremultAlpha /* = nullptr */) +{ + EnsureTarget(); + if (aPremultAlpha) { + *aPremultAlpha = true; + } + FlushDelayedTarget(); + FinishDelayedRendering(); + return mFinalTarget->Snapshot(); +} + void CanvasRenderingContext2D::DidRefresh() { @@ -1161,6 +1275,7 @@ CanvasRenderingContext2D::RedrawUser(const gfxRect& r) { if (mIsEntireFrameInvalid) { ++mInvalidateCount; + RecordCommand(); return; } @@ -1186,7 +1301,7 @@ bool CanvasRenderingContext2D::SwitchRenderingMode(RenderingMode aRenderingMode) } #endif - RefPtr snapshot = mTarget->Snapshot(); + RefPtr snapshot = GetSurfaceSnapshot(); RefPtr oldTarget = mTarget; mTarget = nullptr; mResetLayer = true; @@ -1360,6 +1475,8 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); if (glue && glue->GetGrContext() && glue->GetGLContext()) { + // Don't use mFinalTarget (async canvas drawing) with SkiaGL, because we currently + // use a single GLContext and need them all to be on the same thread. mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format); if (mTarget) { AddDemotableContext(this); @@ -1370,18 +1487,32 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) } #endif if (!mTarget) { - mTarget = layerManager->CreateDrawTarget(size, format); + mFinalTarget = layerManager->CreateDrawTarget(size, format); } } else { - mTarget = layerManager->CreateDrawTarget(size, format); + mFinalTarget = layerManager->CreateDrawTarget(size, format); mode = RenderingMode::SoftwareBackendMode; } } else { - mTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format); + mFinalTarget = gfxPlatform::GetPlatform()->CreateOffscreenCanvasDrawTarget(size, format); mode = RenderingMode::SoftwareBackendMode; } } + // Restrict async canvas drawing to OSX for now since we get test failures + // on other platforms. +#ifdef XP_MACOSX + if (mFinalTarget) { + mTarget = mDelayedTarget = mFinalTarget->CreateCaptureDT(size); + } else { + mFinalTarget = mTarget; + } +#else + mFinalTarget = mTarget; +#endif + + mPendingCommands = 0; + if (mTarget) { static bool registered = false; if (!registered) { @@ -1415,7 +1546,7 @@ CanvasRenderingContext2D::EnsureTarget(RenderingMode aRenderingMode) Redraw(); } else { EnsureErrorTarget(); - mTarget = sErrorTarget; + mTarget = mFinalTarget = sErrorTarget; } return mode; @@ -1435,6 +1566,51 @@ CanvasRenderingContext2D::GetHeight() const } #endif +class DrawCaptureTask : public nsRunnable +{ +public: + DrawCaptureTask(DrawTargetCapture *aReplay, DrawTarget* aDest) + : mReplay(aReplay) + , mDest(aDest) + { + } + + NS_IMETHOD Run() + { + mDest->DrawCapturedDT(mReplay, Matrix()); + return NS_OK; + } + +private: + RefPtr mReplay; + RefPtr mDest; +}; + +void +CanvasRenderingContext2D::FlushDelayedTarget() +{ + if (!mDelayedTarget) { + return; + } + mPendingCommands = 0; + + nsCOMPtr task = new DrawCaptureTask(mDelayedTarget, mFinalTarget); + mTaskQueue->Dispatch(task.forget()); + + mDelayedTarget = mFinalTarget->CreateCaptureDT(IntSize(mWidth, mHeight)); + + mDelayedTarget->SetTransform(mTarget->GetTransform()); + mTarget = mDelayedTarget; +} + +void +CanvasRenderingContext2D::FinishDelayedRendering() +{ + if (mTaskQueue) { + mTaskQueue->AwaitIdle(); + } +} + NS_IMETHODIMP CanvasRenderingContext2D::SetDimensions(int32_t width, int32_t height) { @@ -1584,7 +1760,7 @@ CanvasRenderingContext2D::GetImageBuffer(uint8_t** aImageBuffer, *aFormat = 0; EnsureTarget(); - RefPtr snapshot = mTarget->Snapshot(); + RefPtr snapshot = GetSurfaceSnapshot(); if (!snapshot) { return; } @@ -2003,7 +2179,7 @@ CanvasRenderingContext2D::CreatePattern(const HTMLImageOrCanvasOrVideoElement& e // of animated images nsLayoutUtils::SurfaceFromElementResult res = nsLayoutUtils::SurfaceFromElement(htmlElement, - nsLayoutUtils::SFE_WANT_FIRST_FRAME, mTarget); + nsLayoutUtils::SFE_WANT_FIRST_FRAME, mFinalTarget); if (!res.mSourceSurface) { error.Throw(NS_ERROR_NOT_AVAILABLE); @@ -4314,7 +4490,7 @@ CanvasRenderingContext2D::DrawImage(const HTMLImageOrCanvasOrVideoElement& image nsLayoutUtils::SurfaceFromElementResult res = CachedSurfaceFromElement(element); if (!res.mSourceSurface) - res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mTarget); + res = nsLayoutUtils::SurfaceFromElement(element, sfeFlags, mFinalTarget); if (!res.mSourceSurface && !res.mDrawInfo.mImgContainer) { // The spec says to silently do nothing in the following cases: @@ -4658,7 +4834,12 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x, if (gfxPlatform::GetPlatform()->SupportsAzureContentForDrawTarget(mTarget) && GlobalAlpha() == 1.0f) { - thebes = new gfxContext(mTarget); + // Complete any async rendering and use synchronous rendering for DrawWindow + // until we're confident it works for all content. + FlushDelayedTarget(); + FinishDelayedRendering(); + + thebes = new gfxContext(mFinalTarget); thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21, matrix._22, matrix._31, matrix._32)); } else { @@ -4915,7 +5096,7 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx, IntRect srcReadRect = srcRect.Intersect(destRect); RefPtr readback; if (!srcReadRect.IsEmpty() && !mZero) { - RefPtr snapshot = mTarget->Snapshot(); + RefPtr snapshot = GetSurfaceSnapshot(); if (snapshot) { readback = snapshot->GetDataSurface(); } @@ -5289,7 +5470,7 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, return nullptr; } - mTarget->Flush(); + FlushDelayedTarget(); if (!mResetLayer && aOldLayer) { CanvasRenderingContext2DUserData* userData = @@ -5338,6 +5519,8 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, userData = new CanvasRenderingContext2DUserData(this); canvasLayer->SetDidTransactionCallback( CanvasRenderingContext2DUserData::DidTransactionCallback, userData); + canvasLayer->SetPreTransactionCallback( + CanvasRenderingContext2DUserData::PreTransactionCallback, userData); canvasLayer->SetUserData(&g2DContextLayerUserData, userData); CanvasLayer::Data data; @@ -5346,16 +5529,13 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder, GLuint skiaGLTex = SkiaGLTex(); if (skiaGLTex) { - canvasLayer->SetPreTransactionCallback( - CanvasRenderingContext2DUserData::PreTransactionCallback, userData); - SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue(); MOZ_ASSERT(glue); data.mGLContext = glue->GetGLContext(); data.mFrontbufferGLTex = skiaGLTex; } else { - data.mDrawTarget = mTarget; + data.mDrawTarget = mFinalTarget; } canvasLayer->Initialize(data); diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 0488bbd4088..4de928a4062 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -10,6 +10,7 @@ #include "nsIDOMCanvasRenderingContext2D.h" #include "nsICanvasRenderingContextInternal.h" #include "mozilla/RefPtr.h" +#include "mozilla/Monitor.h" #include "nsColor.h" #include "mozilla/dom/HTMLCanvasElement.h" #include "mozilla/dom/HTMLVideoElement.h" @@ -27,6 +28,7 @@ #include "mozilla/EnumeratedArray.h" #include "FilterSupport.h" #include "nsSVGEffects.h" +#include "MediaTaskQueue.h" class nsGlobalWindow; class nsXULElement; @@ -52,6 +54,7 @@ template class Optional; struct CanvasBidiProcessor; class CanvasRenderingContext2DUserData; class CanvasDrawObserver; +class CanvasShutdownObserver; /** ** CanvasRenderingContext2D @@ -442,14 +445,7 @@ public: const char16_t* aEncoderOptions, nsIInputStream **aStream) override; - mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override - { - EnsureTarget(); - if (aPremultAlpha) { - *aPremultAlpha = true; - } - return mTarget->Snapshot(); - } + mozilla::TemporaryRef GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override; NS_IMETHOD SetIsOpaque(bool isOpaque) override; bool GetIsOpaque() override { return mOpaque; } @@ -521,6 +517,7 @@ public: } friend class CanvasRenderingContext2DUserData; + friend class CanvasShutdownObserver; virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat) override; @@ -532,6 +529,21 @@ public: // return true and fills in the bound rect if element has a hit region. bool GetHitRegionRect(Element* aElement, nsRect& aRect) override; + /** + * Deferred rendering functions + */ + + /** + * Called when the event loop reaches a stable + * state, and trigger us to flush any outstanding + * commands to the rendering thread. + */ + void StableStateReached() + { + mScheduledFlush = false; + FlushDelayedTarget(); + } + protected: nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY, uint32_t aWidth, uint32_t aHeight, @@ -550,6 +562,8 @@ protected: nsresult InitializeWithTarget(mozilla::gfx::DrawTarget *surface, int32_t width, int32_t height); + void ShutdownTaskQueue(); + /** * The number of living nsCanvasRenderingContexts. When this goes down to * 0, we free the premultiply and unpremultiply tables, if they exist. @@ -713,6 +727,54 @@ protected: // sErrorTarget. mozilla::RefPtr mTarget; + /** + * Deferred rendering implementation + */ + + // If we are using deferred rendering, then this is the current + // deferred rendering target. It is the same pointer as mTarget. + mozilla::RefPtr mDelayedTarget; + + // If we are using deferred rendering, then this is the actual destination + // buffer. + mozilla::RefPtr mFinalTarget; + + /** + * Add the current DelayedDrawTarget to the rendering queue, + * schedule a rendering job if required, and create a new + * DelayedDrawTarget. + */ + void FlushDelayedTarget(); + + /** + * Make sure all commands have been flushed to + * the rendering thread, and block until they + * are completed. + */ + void FinishDelayedRendering(); + + /** + * Called when a command is added to the current + * delayed draw target. + * + * Either flushes the current batch of commands to + * the rendering thread, or ensures that this happens + * the next time the event loop reaches a stable state. + */ + void RecordCommand(); + + // The number of commands currently waiting to be sent + // to the rendering thread. + uint32_t mPendingCommands; + + // True if we have scheduled FlushDelayedTarget to be + // called in the next browser stable state. + bool mScheduledFlush; + + nsRefPtr mTaskQueue; + + nsRefPtr mShutdownObserver; + uint32_t SkiaGLTex() const; // This observes our draw calls at the beginning of the canvas diff --git a/dom/media/MediaPromise.h b/dom/media/MediaPromise.h index efd17839e73..d34f8c4f67f 100644 --- a/dom/media/MediaPromise.h +++ b/dom/media/MediaPromise.h @@ -29,6 +29,8 @@ namespace mozilla { extern PRLogModuleInfo* gMediaPromiseLog; +void EnsureMediaPromiseLog(); + #define PROMISE_LOG(x, ...) \ MOZ_ASSERT(gMediaPromiseLog); \ PR_LOG(gMediaPromiseLog, PR_LOG_DEBUG, (x, ##__VA_ARGS__)) diff --git a/dom/media/MediaTaskQueue.cpp b/dom/media/MediaTaskQueue.cpp index 4d31a93adff..8ea6924c6a3 100644 --- a/dom/media/MediaTaskQueue.cpp +++ b/dom/media/MediaTaskQueue.cpp @@ -27,6 +27,7 @@ MediaTaskQueue::~MediaTaskQueue() { MonitorAutoLock mon(mQueueMonitor); MOZ_ASSERT(mIsShutdown); + MOZ_DIAGNOSTIC_ASSERT(mTasks.empty()); MOZ_COUNT_DTOR(MediaTaskQueue); } diff --git a/gfx/2d/2D.h b/gfx/2d/2D.h index 9bae3a3f61a..dd505568f15 100644 --- a/gfx/2d/2D.h +++ b/gfx/2d/2D.h @@ -155,7 +155,7 @@ struct DrawSurfaceOptions { * matching DrawTarget. Not adhering to this condition will make a draw call * fail. */ -class GradientStops : public RefCounted +class GradientStops : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStops) @@ -318,7 +318,7 @@ class DrawTargetCaptureImpl; * which may be used as a source in a SurfacePattern or a DrawSurface call. * They cannot be drawn to directly. */ -class SourceSurface : public RefCounted +class SourceSurface : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface) @@ -476,7 +476,7 @@ class FlattenedPath; /** The path class is used to create (sets of) figures of any shape that can be * filled or stroked to a DrawTarget */ -class Path : public RefCounted +class Path : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(Path) @@ -577,7 +577,7 @@ struct GlyphBuffer * at a particular size. It is passed into text drawing calls to describe * the font used for the drawing call. */ -class ScaledFont : public RefCounted +class ScaledFont : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(ScaledFont) @@ -622,7 +622,7 @@ protected: * parameters. This is because different platforms have unique rendering * parameters. */ -class GlyphRenderingOptions : public RefCounted +class GlyphRenderingOptions : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GlyphRenderingOptions) @@ -641,7 +641,7 @@ class DrawTargetCapture; * may be used either through a Snapshot or by flushing the target and directly * accessing the backing store a DrawTarget was created with. */ -class DrawTarget : public RefCounted +class DrawTarget : public external::AtomicRefCounted { public: MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget) diff --git a/gfx/2d/DrawCommand.h b/gfx/2d/DrawCommand.h index 6f1466d8ea8..1adb07b30c6 100644 --- a/gfx/2d/DrawCommand.h +++ b/gfx/2d/DrawCommand.h @@ -148,7 +148,7 @@ class DrawFilterCommand : public DrawingCommand public: DrawFilterCommand(FilterNode* aFilter, const Rect& aSourceRect, const Point& aDestPoint, const DrawOptions& aOptions) - : DrawingCommand(CommandType::DRAWSURFACE) + : DrawingCommand(CommandType::DRAWFILTER) , mFilter(aFilter), mSourceRect(aSourceRect) , mDestPoint(aDestPoint), mOptions(aOptions) { @@ -166,6 +166,36 @@ private: DrawOptions mOptions; }; +class DrawSurfaceWithShadowCommand : public DrawingCommand +{ +public: + DrawSurfaceWithShadowCommand(SourceSurface* aSurface, const Point& aDest, + const Color& aColor, const Point& aOffset, + Float aSigma, CompositionOp aOperator) + : DrawingCommand(CommandType::DRAWSURFACEWITHSHADOW) + , mSurface(aSurface) + , mDest(aDest) + , mColor(aColor) + , mOffset(aOffset) + , mSigma(aSigma) + , mOperator(aOperator) + { + } + + virtual void ExecuteOnDT(DrawTarget* aDT, const Matrix&) + { + aDT->DrawSurfaceWithShadow(mSurface, mDest, mColor, mOffset, mSigma, mOperator); + } + +private: + RefPtr mSurface; + Point mDest; + Color mColor; + Point mOffset; + Float mSigma; + CompositionOp mOperator; +}; + class ClearRectCommand : public DrawingCommand { public: diff --git a/gfx/2d/DrawTargetCapture.cpp b/gfx/2d/DrawTargetCapture.cpp index d53a98ec770..6194ca27db9 100644 --- a/gfx/2d/DrawTargetCapture.cpp +++ b/gfx/2d/DrawTargetCapture.cpp @@ -30,6 +30,7 @@ DrawTargetCaptureImpl::Init(const IntSize& aSize, DrawTarget* aRefDT) } mRefDT = aRefDT; + mFormat = mRefDT->GetFormat(); mSize = aSize; return true; @@ -69,6 +70,18 @@ DrawTargetCaptureImpl::DrawFilter(FilterNode *aNode, AppendCommand(DrawFilterCommand)(aNode, aSourceRect, aDestPoint, aOptions); } +void +DrawTargetCaptureImpl::DrawSurfaceWithShadow(SourceSurface *aSurface, + const Point &aDest, + const Color &aColor, + const Point &aOffset, + Float aSigma, + CompositionOp aOperator) +{ + aSurface->GuaranteePersistance(); + AppendCommand(DrawSurfaceWithShadowCommand)(aSurface, aDest, aColor, aOffset, aSigma, aOperator); +} + void DrawTargetCaptureImpl::ClearRect(const Rect &aRect) { @@ -178,6 +191,7 @@ void DrawTargetCaptureImpl::SetTransform(const Matrix& aTransform) { AppendCommand(SetTransformCommand)(aTransform); + mTransform = aTransform; } void diff --git a/gfx/2d/DrawTargetCapture.h b/gfx/2d/DrawTargetCapture.h index 2ef8f0e6255..ea97066e290 100644 --- a/gfx/2d/DrawTargetCapture.h +++ b/gfx/2d/DrawTargetCapture.h @@ -45,7 +45,7 @@ public: const Color &aColor, const Point &aOffset, Float aSigma, - CompositionOp aOperator) { /* Not implemented */ } + CompositionOp aOperator); virtual void ClearRect(const Rect &aRect); virtual void MaskSurface(const Pattern &aSource,