diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index c2113fec9254..2aa3094013de 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -411,8 +411,10 @@ public: LayerManager *aManager); void MarkContextClean(); NS_IMETHOD SetIsIPC(PRBool isIPC); - // this rect is in CSS pixels + // this rect is in canvas device space NS_IMETHOD Redraw(const gfxRect &r); + // this rect is in mThebes's current user space + NS_IMETHOD RedrawUser(const gfxRect &r); // nsISupports interface + CC NS_DECL_CYCLE_COLLECTING_ISUPPORTS @@ -503,7 +505,13 @@ protected: * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever * Redraw is called, reset to false when Render is called. */ - PRBool mIsEntireFrameInvalid; + PRPackedBool mIsEntireFrameInvalid; + /** + * When this is set, the first call to Redraw(gfxRect) should set + * mIsEntireFrameInvalid since we expect it will be followed by + * many more Redraw calls. + */ + PRPackedBool mPredictManyRedrawCalls; /** * Number of times we've invalidated before calling redraw @@ -603,7 +611,7 @@ protected: * Draws the current path in the given style. Takes care of * any shadow drawing and will use intermediate surfaces as needed. * - * If dirtyRect is given, it will contain the device-space dirty + * If dirtyRect is given, it will contain the user-space dirty * rectangle of the draw operation. */ nsresult DrawPath(Style style, gfxRect *dirtyRect = nsnull); @@ -821,7 +829,8 @@ nsCanvasRenderingContext2D::nsCanvasRenderingContext2D() : mValid(PR_FALSE), mOpaque(PR_FALSE), mResetLayer(PR_TRUE) , mIPC(PR_FALSE) , mCanvasElement(nsnull) - , mSaveCount(0), mIsEntireFrameInvalid(PR_FALSE), mInvalidateCount(0) + , mSaveCount(0), mIsEntireFrameInvalid(PR_FALSE) + , mPredictManyRedrawCalls(PR_FALSE), mInvalidateCount(0) , mLastStyle(STYLE_MAX), mStyleStack(20) { sNumLivingContexts++; @@ -851,6 +860,7 @@ nsCanvasRenderingContext2D::Reset() mThebes = nsnull; mValid = PR_FALSE; mIsEntireFrameInvalid = PR_FALSE; + mPredictManyRedrawCalls = PR_FALSE; return NS_OK; } @@ -1014,6 +1024,10 @@ nsCanvasRenderingContext2D::ApplyStyle(Style aWhichStyle, nsresult nsCanvasRenderingContext2D::Redraw() { + if (mIsEntireFrameInvalid) + return NS_OK; + mIsEntireFrameInvalid = PR_TRUE; + if (!mCanvasElement) { NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!"); return NS_OK; @@ -1023,11 +1037,6 @@ nsCanvasRenderingContext2D::Redraw() nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement()); #endif - if (mIsEntireFrameInvalid) - return NS_OK; - - mIsEntireFrameInvalid = PR_TRUE; - HTMLCanvasElement()->InvalidateFrame(); return NS_OK; @@ -1036,9 +1045,16 @@ nsCanvasRenderingContext2D::Redraw() NS_IMETHODIMP nsCanvasRenderingContext2D::Redraw(const gfxRect& r) { + ++mInvalidateCount; + if (mIsEntireFrameInvalid) return NS_OK; + if (mPredictManyRedrawCalls || + mInvalidateCount > kCanvasMaxInvalidateCount) { + return Redraw(); + } + if (!mCanvasElement) { NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!"); return NS_OK; @@ -1048,14 +1064,22 @@ nsCanvasRenderingContext2D::Redraw(const gfxRect& r) nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement()); #endif - if (++mInvalidateCount > kCanvasMaxInvalidateCount) - return Redraw(); - HTMLCanvasElement()->InvalidateFrame(&r); return NS_OK; } +NS_IMETHODIMP +nsCanvasRenderingContext2D::RedrawUser(const gfxRect& r) +{ + if (mIsEntireFrameInvalid) { + ++mInvalidateCount; + return NS_OK; + } + + return Redraw(mThebes->UserToDevice(r)); +} + NS_IMETHODIMP nsCanvasRenderingContext2D::SetDimensions(PRInt32 width, PRInt32 height) { @@ -1914,8 +1938,6 @@ nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect) // just use the clip extents *dirtyRect = mThebes->GetClipExtents(); } - - *dirtyRect = mThebes->UserToDevice(*dirtyRect); } return NS_OK; @@ -1939,8 +1961,7 @@ nsCanvasRenderingContext2D::ClearRect(float x, float y, float w, float h) mThebes->Rectangle(gfxRect(x, y, w, h)); mThebes->Fill(); - gfxRect dirty = mThebes->UserToDevice(mThebes->GetUserPathExtent()); - return Redraw(dirty); + return RedrawUser(mThebes->GetUserPathExtent()); } nsresult @@ -1959,7 +1980,7 @@ nsCanvasRenderingContext2D::DrawRect(const gfxRect& rect, Style style) if (NS_FAILED(rv)) return rv; - return Redraw(dirty); + return RedrawUser(dirty); } NS_IMETHODIMP @@ -1999,7 +2020,7 @@ nsCanvasRenderingContext2D::Fill() nsresult rv = DrawPath(STYLE_FILL, &dirty); if (NS_FAILED(rv)) return rv; - return Redraw(dirty); + return RedrawUser(dirty); } NS_IMETHODIMP @@ -2009,7 +2030,7 @@ nsCanvasRenderingContext2D::Stroke() nsresult rv = DrawPath(STYLE_STROKE, &dirty); if (NS_FAILED(rv)) return rv; - return Redraw(dirty); + return RedrawUser(dirty); } NS_IMETHODIMP @@ -2848,7 +2869,7 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, mThebes->Paint(CurrentState().StyleIsColor(STYLE_FILL) ? 1.0 : CurrentState().globalAlpha); if (aOp == nsCanvasRenderingContext2D::TEXT_DRAW_OPERATION_FILL && !doDrawShadow) - return Redraw(mThebes->UserToDevice(boundingBox)); + return RedrawUser(boundingBox); return Redraw(); } @@ -3511,10 +3532,7 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1, mThebes->Paint(CurrentState().globalAlpha); } - if (!mIsEntireFrameInvalid) { - gfxRect dirty = mThebes->UserToDevice(clip); - Redraw(dirty); - } + RedrawUser(clip); } FINISH: @@ -3669,9 +3687,7 @@ nsCanvasRenderingContext2D::DrawWindow(nsIDOMWindow* aWindow, float aX, float aY // note that aX and aY are coordinates in the document that // we're drawing; aX and aY are drawn to 0,0 in current user // space. - gfxRect damageRect = mThebes->UserToDevice(gfxRect(0, 0, aW, aH)); - - Redraw(damageRect); + RedrawUser(gfxRect(0, 0, aW, aH)); return rv; } @@ -4107,6 +4123,9 @@ nsCanvasRenderingContext2D::GetCanvasLayer(CanvasLayer *aOldLayer, void nsCanvasRenderingContext2D::MarkContextClean() { + if (mInvalidateCount > 0) { + mPredictManyRedrawCalls = mInvalidateCount > kCanvasMaxInvalidateCount; + } mIsEntireFrameInvalid = PR_FALSE; mInvalidateCount = 0; }