Bug 1317636 - Part 2. Use CreateClipMask/PaintClipMask in GetClipMask. draft
authorcku <cku@mozilla.com>
Thu, 17 Nov 2016 14:50:35 +0800
changeset 440349 a4f277cf1b996103ef295b907e939f9ccf6616d1
parent 440348 c8a894bbd52eea70bb3e82e16e256e5f4515a888
child 440350 027806777c42bf3efcb753508e19021c57b169b5
push id36210
push userbmo:cku@mozilla.com
push dateThu, 17 Nov 2016 12:38:18 +0000
bugs1317636
milestone53.0a1
Bug 1317636 - Part 2. Use CreateClipMask/PaintClipMask in GetClipMask. MozReview-Commit-ID: EdjCzo9QCFe
layout/svg/nsSVGClipPathFrame.cpp
--- a/layout/svg/nsSVGClipPathFrame.cpp
+++ b/layout/svg/nsSVGClipPathFrame.cpp
@@ -259,186 +259,43 @@ already_AddRefed<SourceSurface>
 nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext,
                                 nsIFrame* aClippedFrame,
                                 const gfxMatrix& aMatrix,
                                 Matrix* aMaskTransform,
                                 SourceSurface* aExtraMask,
                                 const Matrix& aExtraMasksTransform,
                                 DrawResult* aResult)
 {
-  MOZ_ASSERT(!IsTrivial(), "Caller needs to use ApplyClipPath");
-
-  if (aResult) {
-    *aResult = DrawResult::SUCCESS;
-  }
-  DrawTarget& aReferenceDT = *aReferenceContext.GetDrawTarget();
-
-  // A clipPath can reference another clipPath.  We re-enter this method for
-  // each clipPath in a reference chain, so here we limit chain length:
-  static int16_t sRefChainLengthCounter = AutoReferenceLimiter::notReferencing;
-  AutoReferenceLimiter
-    refChainLengthLimiter(&sRefChainLengthCounter,
-                          MAX_SVG_CLIP_PATH_REFERENCE_CHAIN_LENGTH);
-  if (!refChainLengthLimiter.Reference()) {
-    return nullptr; // Reference chain is too long!
-  }
-
-  // And to prevent reference loops we check that this clipPath only appears
-  // once in the reference chain (if any) that we're currently processing:
-  AutoReferenceLimiter refLoopDetector(&mReferencing, 1);
-  if (!refLoopDetector.Reference()) {
-    return nullptr; // Reference loop!
-  }
-
-  IntRect devSpaceClipExtents;
-  {
-    gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext);
-
-    aReferenceContext.SetMatrix(gfxMatrix());
-    gfxRect rect = aReferenceContext.GetClipExtents();
-    devSpaceClipExtents = RoundedOut(ToRect(rect));
-    if (devSpaceClipExtents.IsEmpty()) {
-      // We don't need to create a mask surface, all drawing is clipped anyway.
-      return nullptr;
+  IntPoint offset;
+  RefPtr<DrawTarget> maskDT= CreateClipMask(aReferenceContext, offset);
+  if (!maskDT) {
+    if (aResult) {
+      *aResult = DrawResult::SUCCESS;
     }
+    return nullptr;
   }
 
-  RefPtr<DrawTarget> maskDT =
-    aReferenceDT.CreateSimilarDrawTarget(devSpaceClipExtents.Size(),
-                                         SurfaceFormat::A8);
-
-  gfxMatrix mat = aReferenceContext.CurrentMatrix() *
-                    gfxMatrix::Translation(-devSpaceClipExtents.TopLeft());
-
-  // Paint this clipPath's contents into maskDT:
-  {
-    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(maskDT);
-    if (!ctx) {
-      gfxCriticalError() << "SVGClipPath context problem " << gfx::hexa(maskDT);
-      return nullptr;
+  RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(maskDT);
+  if (!maskContext) {
+    gfxCriticalError() << "SVGClipPath context problem " << gfx::hexa(maskDT);
+    if (aResult) {
+      *aResult = DrawResult::TEMPORARY_ERROR;
     }
-    ctx->SetMatrix(mat);
-
-    // We need to set mMatrixForChildren here so that under the PaintSVG calls
-    // on our children (below) our GetCanvasTM() method will return the correct
-    // transform.
-    mMatrixForChildren = GetClipPathTransform(aClippedFrame) * aMatrix;
-
-    // Check if this clipPath is itself clipped by another clipPath:
-    nsSVGClipPathFrame* clipPathThatClipsClipPath =
-      nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
-    bool clippingOfClipPathRequiredMasking;
-    if (clipPathThatClipsClipPath) {
-      ctx->Save();
-      clippingOfClipPathRequiredMasking = !clipPathThatClipsClipPath->IsTrivial();
-      if (!clippingOfClipPathRequiredMasking) {
-        clipPathThatClipsClipPath->ApplyClipPath(*ctx, aClippedFrame, aMatrix);
-      } else {
-        Matrix maskTransform;
-        RefPtr<SourceSurface> mask =
-          clipPathThatClipsClipPath->GetClipMask(*ctx, aClippedFrame,
-                                                 aMatrix, &maskTransform);
-        ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
-                                   mask, maskTransform);
-        // The corresponding PopGroupAndBlend call below will mask the
-        // blend using |mask|.
-      }
-    }
-
-    // Paint our children into the mask:
-    for (nsIFrame* kid = mFrames.FirstChild(); kid;
-         kid = kid->GetNextSibling()) {
-      nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
-      if (SVGFrame) {
-        // The CTM of each frame referencing us can be different.
-        SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
+    return nullptr;
+  }
+  maskContext->SetMatrix(aReferenceContext.CurrentMatrix() *
+                         gfxMatrix::Translation(-offset));
 
-        bool isOK = true;
-        // Children of this clipPath may themselves be clipped.
-        nsSVGClipPathFrame *clipPathThatClipsChild =
-          nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK);
-        if (!isOK) {
-          continue;
-        }
-
-        bool childsClipPathRequiresMasking;
-
-        if (clipPathThatClipsChild) {
-          childsClipPathRequiresMasking = !clipPathThatClipsChild->IsTrivial();
-          ctx->Save();
-          if (!childsClipPathRequiresMasking) {
-            clipPathThatClipsChild->ApplyClipPath(*ctx, aClippedFrame, aMatrix);
-          } else {
-            Matrix maskTransform;
-            RefPtr<SourceSurface> mask =
-              clipPathThatClipsChild->GetClipMask(*ctx, aClippedFrame,
-                                                  aMatrix, &maskTransform);
-            ctx->PushGroupForBlendBack(gfxContentType::ALPHA, 1.0,
-                                       mask, maskTransform);
-            // The corresponding PopGroupAndBlend call below will mask the
-            // blend using |mask|.
-          }
-        }
-
-        gfxMatrix toChildsUserSpace = mMatrixForChildren;
-        nsIFrame* child = do_QueryFrame(SVGFrame);
-        nsIContent* childContent = child->GetContent();
-        if (childContent->IsSVGElement()) {
-          toChildsUserSpace =
-            static_cast<const nsSVGElement*>(childContent)->
-              PrependLocalTransformsTo(mMatrixForChildren, eUserSpaceToParent);
-        }
-
-        // Our children have NS_STATE_SVG_CLIPPATH_CHILD set on them, and
-        // nsSVGPathGeometryFrame::Render checks for that state bit and paints
-        // only the geometry (opaque black) if set.
-        DrawResult result = SVGFrame->PaintSVG(*ctx, toChildsUserSpace);
-        if (aResult) {
-          *aResult &= result;
-        }
-
-        if (clipPathThatClipsChild) {
-          if (childsClipPathRequiresMasking) {
-            ctx->PopGroupAndBlend();
-          }
-          ctx->Restore();
-        }
-      }
-    }
-
-
-    if (clipPathThatClipsClipPath) {
-      if (clippingOfClipPathRequiredMasking) {
-        ctx->PopGroupAndBlend();
-      }
-      ctx->Restore();
-    }
+  DrawResult result = PaintClipMask(*maskContext, aClippedFrame, aMatrix,
+                                    aMaskTransform, aExtraMask,
+                                    aExtraMasksTransform);
+  if (aResult) {
+    *aResult = result;
   }
 
-  // Moz2D transforms in the opposite direction to Thebes
-  mat.Invert();
-
-  if (aExtraMask) {
-    // We could potentially due this more efficiently with OPERATOR_IN
-    // but that operator does not work well on CG or D2D
-    RefPtr<SourceSurface> currentMask = maskDT->Snapshot();
-    Matrix transform = maskDT->GetTransform();
-    maskDT->SetTransform(Matrix());
-    maskDT->ClearRect(Rect(0, 0,
-                           devSpaceClipExtents.width,
-                           devSpaceClipExtents.height));
-    maskDT->SetTransform(aExtraMasksTransform * transform);
-    // draw currentMask with the inverse of the transform that we just so that
-    // it ends up in the same spot with aExtraMask transformed by aExtraMasksTransform
-    maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP, aExtraMasksTransform.Inverse() * ToMatrix(mat)),
-                        aExtraMask,
-                        Point(0, 0));
-  }
-
-  *aMaskTransform = ToMatrix(mat);
   return maskDT->Snapshot();
 }
 
 bool
 nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
                                           const gfxPoint &aPoint)
 {
   // A clipPath can reference another clipPath.  We re-enter this method for