Bug 857895 - Run canvas rendering asynchronously on OSX. r=Bas,bholley

This commit is contained in:
Matt Woodrow 2013-04-09 16:51:44 +12:00
parent 8f40253a31
commit 0e8aa9df46
8 changed files with 320 additions and 31 deletions

View File

@ -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<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
}
void Shutdown() {
nsCOMPtr<nsIObserverService> 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<nsIAppShell> appShell = do_GetService(kAppShellCID);
nsCOMPtr<nsIRunnable> 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<JSObject*> 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<SourceSurface>
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<SourceSurface> snapshot = mTarget->Snapshot();
RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot();
RefPtr<DrawTarget> 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<DrawTargetCapture> mReplay;
RefPtr<DrawTarget> mDest;
};
void
CanvasRenderingContext2D::FlushDelayedTarget()
{
if (!mDelayedTarget) {
return;
}
mPendingCommands = 0;
nsCOMPtr<nsIRunnable> 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<SourceSurface> snapshot = mTarget->Snapshot();
RefPtr<SourceSurface> 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<DataSourceSurface> readback;
if (!srcReadRect.IsEmpty() && !mZero) {
RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
RefPtr<SourceSurface> 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);

View File

@ -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<typename T> 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<mozilla::gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override
{
EnsureTarget();
if (aPremultAlpha) {
*aPremultAlpha = true;
}
return mTarget->Snapshot();
}
mozilla::TemporaryRef<mozilla::gfx::SourceSurface> 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<mozilla::gfx::DrawTarget> 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<mozilla::gfx::DrawTargetCapture> mDelayedTarget;
// If we are using deferred rendering, then this is the actual destination
// buffer.
mozilla::RefPtr<mozilla::gfx::DrawTarget> 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<MediaTaskQueue> mTaskQueue;
nsRefPtr<CanvasShutdownObserver> mShutdownObserver;
uint32_t SkiaGLTex() const;
// This observes our draw calls at the beginning of the canvas

View File

@ -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__))

View File

@ -27,6 +27,7 @@ MediaTaskQueue::~MediaTaskQueue()
{
MonitorAutoLock mon(mQueueMonitor);
MOZ_ASSERT(mIsShutdown);
MOZ_DIAGNOSTIC_ASSERT(mTasks.empty());
MOZ_COUNT_DTOR(MediaTaskQueue);
}

View File

@ -155,7 +155,7 @@ struct DrawSurfaceOptions {
* matching DrawTarget. Not adhering to this condition will make a draw call
* fail.
*/
class GradientStops : public RefCounted<GradientStops>
class GradientStops : public external::AtomicRefCounted<GradientStops>
{
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<SourceSurface>
class SourceSurface : public external::AtomicRefCounted<SourceSurface>
{
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<Path>
class Path : public external::AtomicRefCounted<Path>
{
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<ScaledFont>
class ScaledFont : public external::AtomicRefCounted<ScaledFont>
{
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<GlyphRenderingOptions>
class GlyphRenderingOptions : public external::AtomicRefCounted<GlyphRenderingOptions>
{
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<DrawTarget>
class DrawTarget : public external::AtomicRefCounted<DrawTarget>
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawTarget)

View File

@ -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<SourceSurface> mSurface;
Point mDest;
Color mColor;
Point mOffset;
Float mSigma;
CompositionOp mOperator;
};
class ClearRectCommand : public DrawingCommand
{
public:

View File

@ -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

View File

@ -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,