diff --git a/content/canvas/src/nsCanvasRenderingContext2D.cpp b/content/canvas/src/nsCanvasRenderingContext2D.cpp index 17683b0bbdea..f197f16ca9e1 100644 --- a/content/canvas/src/nsCanvasRenderingContext2D.cpp +++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp @@ -549,6 +549,19 @@ protected: PRUint32 mInvalidateCount; static const PRUint32 kCanvasMaxInvalidateCount = 100; + /** + * Returns true iff the the given operator should affect areas of the + * destination where the source is transparent. Among other things, this + * implies that a fully transparent source would still affect the canvas. + */ + PRBool OperatorAffectsUncoveredAreas(gfxContext::GraphicsOperator op) const + { + return op == gfxContext::OPERATOR_IN || + op == gfxContext::OPERATOR_OUT || + op == gfxContext::OPERATOR_DEST_IN || + op == gfxContext::OPERATOR_DEST_ATOP; + } + /** * Returns true iff a shadow should be drawn along with a * drawing operation. @@ -564,6 +577,22 @@ protected: (state.shadowOffset != gfxPoint(0, 0) || state.shadowBlur != 0); } + /** + * Checks the current state to determine if an intermediate surface would + * be necessary to complete a drawing operation. Does not check the + * condition pertaining to global alpha and patterns since that does not + * pertain to all drawing operations. + */ + PRBool NeedToUseIntermediateSurface() + { + // certain operators always need an intermediate surface, except + // with quartz since quartz does compositing differently than cairo + return OperatorAffectsUncoveredAreas(mThebes->CurrentOperator()); + + // XXX there are other unhandled cases but they should be investigated + // first to ensure we aren't using an intermediate surface unecessarily + } + /** * If the current operator is "source" then clear the destination before we * draw into it, to simulate the effect of an unbounded source operator. @@ -1932,7 +1961,8 @@ nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect) * - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha) * - certain operators are used */ - PRBool doUseIntermediateSurface = NeedIntermediateSurfaceToHandleGlobalAlpha(style); + PRBool doUseIntermediateSurface = NeedToUseIntermediateSurface() || + NeedIntermediateSurfaceToHandleGlobalAlpha(style); PRBool doDrawShadow = NeedToDrawShadow(); @@ -2751,7 +2781,7 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText, // don't need to take care of these with stroke since Stroke() does that PRBool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow(); PRBool doUseIntermediateSurface = aOp == TEXT_DRAW_OPERATION_FILL && - NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL); + (NeedToUseIntermediateSurface() || NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL)); // Clear the surface if we need to simulate unbounded SOURCE operator ClearSurfaceForUnboundedSource(); @@ -3559,11 +3589,25 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1, } } + PRBool doUseIntermediateSurface = NeedToUseIntermediateSurface(); + mThebes->SetPattern(pattern); DirtyAllStyles(); - /* Direct2D isn't very good at clipping so use Fill() when we can */ - if (CurrentState().globalAlpha == 1.0f && mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER) { + if (doUseIntermediateSurface) { + // draw onto a pushed group + mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA); + mThebes->Clip(clip); + + // don't want operators to be applied twice + mThebes->SetOperator(gfxContext::OPERATOR_SOURCE); + + mThebes->Paint(); + mThebes->PopGroupToSource(); + mThebes->Paint(CurrentState().globalAlpha); + } else if (CurrentState().globalAlpha == 1.0f && + mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER) { + /* Direct2D isn't very good at clipping so use Fill() when we can */ mThebes->NewPath(); mThebes->Rectangle(clip); mThebes->Fill(); diff --git a/content/canvas/test/Makefile.in b/content/canvas/test/Makefile.in index b9e3b7aa228c..abd0dbbcb716 100644 --- a/content/canvas/test/Makefile.in +++ b/content/canvas/test/Makefile.in @@ -76,6 +76,10 @@ _TEST_FILES_0 = \ test_2d.composite.image.destination-in.html \ test_2d.composite.image.source-in.html \ test_2d.composite.image.source-out.html \ + test_2d.composite.uncovered.image.destination-atop.html \ + test_2d.composite.uncovered.image.destination-in.html \ + test_2d.composite.uncovered.image.source-in.html \ + test_2d.composite.uncovered.image.source-out.html \ test_mozGetAsFile.html \ $(NULL) @@ -93,15 +97,6 @@ _TEST_FILES_0 = \ # test_2d.composite.clip.lighter.html \ # -# Temporarily disabled tests; unbounded operators changed behaviour, need to reevaluate tests. -# At one point we were passing these accidentally, because the UpdateSurfaceClip at the end -# of drawImage happened to clear the entire canvas with some operators, which is what these -# tests were checking. -# test_2d.composite.uncovered.image.source-in.html \ -# test_2d.composite.uncovered.image.destination-in.html \ -# test_2d.composite.uncovered.image.source-out.html \ -# test_2d.composite.uncovered.image.destination-atop.html \ - # Tests that fail on Mac (possibly because spec is underdefined?). Bug 407105 ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa) # XXX vlad don't test these anywhere, cairo behaviour changed