Bug 1323962 - Should support CSS transform for mask r=longsonr
authorviolet <violet.bugreport@gmail.com>
Thu, 18 Apr 2019 17:19:42 +0000
changeset 470168 be0c4eed11e43d177ded8e8ddf0ab5379c9f3f44
parent 470167 da464aa8549f6031a68ed6577a1eb890a936a19e
child 470169 141cbcdbcef86709c3419cf1cba5aba72a090f02
push id112843
push useraiakab@mozilla.com
push dateFri, 19 Apr 2019 09:50:22 +0000
treeherdermozilla-inbound@c06f27cbfe40 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslongsonr
bugs1323962
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1323962 - Should support CSS transform for mask r=longsonr SVGElement::PrependLocalTransformsTo doesn't take CSS transform into account, we should use nsIFrame::GetTransformMatrix instead. Differential Revision: https://phabricator.services.mozilla.com/D27659
layout/reftests/svg/reftest.list
layout/reftests/svg/test_bug1323962-ref.html
layout/reftests/svg/test_bug1323962.html
layout/svg/nsSVGMaskFrame.cpp
layout/svg/nsSVGUtils.cpp
layout/svg/nsSVGUtils.h
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -139,16 +139,17 @@ fuzzy-if(d2d||skiaContent,0-1,0-10000) =
 == dynamic-inner-svg-01.svg pass.svg
 == dynamic-link-style-01.svg pass.svg
 == dynamic-marker-01.svg pass.svg
 == dynamic-marker-02.svg dynamic-marker-02-ref.svg
 == dynamic-marker-03.svg pass.svg
 == dynamic-mask-01.svg pass.svg
 == dynamic-mask-contents-01.svg pass.svg
 == dynamic-mask-pre-effects-bbox.html dynamic-mask-pre-effects-bbox-ref.html
+== test_bug1323962.html test_bug1323962-ref.html
 == dynamic-opacity-property-01.svg pass.svg
 == dynamic-pattern-01.svg pass.svg
 == dynamic-pattern-02.svg pass.svg
 == dynamic-pattern-contents-01.svg pass.svg
 == dynamic-pattern-contents-02.svg pass.svg
 == dynamic-rect-01.svg dynamic-rect-01-ref.svg
 fuzzy-if(d2d&&layersGPUAccelerated,0-3,0-1200) == dynamic-rect-02.svg dynamic-rect-02-ref.svg # bug 776038 for Win7, Win8
 == dynamic-rect-03.svg dynamic-rect-03-ref.svg
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962-ref.html
@@ -0,0 +1,8 @@
+<svg style="width: 500px; height: 500px; border: 1px solid green;">
+  <defs>
+    <mask id="mask">
+      <rect id="square" x="200px" y="250px" width="100px" height="150px" fill="#ffffff" />
+    </mask>
+  </defs>
+  <rect mask="url(#mask)" width="500px" height="500px" fill="red" />
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/test_bug1323962.html
@@ -0,0 +1,13 @@
+<style>
+  #square{
+    transform: translate(100px, 100px) scale(2,3);
+  }
+</style>
+<svg style="width: 500px; height: 500px; border: 1px solid green;">
+  <defs>
+    <mask id="mask">
+      <rect id="square" x="50px" y="50px" width="50px" height="50px" fill="#ffffff" />
+    </mask>
+  </defs>
+  <rect mask="url(#mask)" width="500px" height="500px" fill="red" />
+</svg>
--- a/layout/svg/nsSVGMaskFrame.cpp
+++ b/layout/svg/nsSVGMaskFrame.cpp
@@ -107,26 +107,25 @@ already_AddRefed<SourceSurface> nsSVGMas
   RefPtr<gfxContext> tmpCtx = gfxContext::CreateOrNull(maskDT);
   MOZ_ASSERT(tmpCtx);  // already checked the draw target above
   tmpCtx->SetMatrix(maskSurfaceMatrix);
 
   mMatrixForChildren =
       GetMaskTransform(aParams.maskedFrame) * aParams.toUserSpace;
 
   for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) {
+    gfxMatrix m = mMatrixForChildren;
+
     // The CTM of each frame referencing us can be different
     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
     if (SVGFrame) {
       SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
+      m = nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) * m;
     }
-    gfxMatrix m = mMatrixForChildren;
-    if (kid->GetContent()->IsSVGElement()) {
-      m = static_cast<SVGElement*>(kid->GetContent())
-              ->PrependLocalTransformsTo(m, eUserSpaceToParent);
-    }
+
     nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
   }
 
   RefPtr<SourceSurface> surface;
   if (maskType == NS_STYLE_MASK_TYPE_LUMINANCE) {
     if (StyleSVG()->mColorInterpolation ==
         NS_STYLE_COLOR_INTERPOLATION_LINEARRGB) {
       maskType = NS_STYLE_COLOR_INTERPOLATION_LINEARRGB;
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -1691,8 +1691,33 @@ nsRect nsSVGUtils::ToCanvasBounds(const 
 gfxMatrix nsSVGUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) {
   int32_t appUnitsPerDevPixel =
       aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
   float devPxPerCSSPx =
       1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
 
   return gfxMatrix(devPxPerCSSPx, 0.0, 0.0, devPxPerCSSPx, 0.0, 0.0);
 }
+
+gfxMatrix nsSVGUtils::GetTransformMatrixInUserSpace(const nsIFrame* aFrame,
+                                                    const nsIFrame* aAncestor) {
+  // We check element instead of aFrame directly because SVG element
+  // may have non-SVG frame, <tspan> for example.
+  MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsSVGElement(),
+             "Only use this wrapper for SVG elements");
+
+  Matrix mm;
+  auto trans = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
+  trans.ProjectTo2D();
+  trans.CanDraw2D(&mm);
+  gfxMatrix ret = ThebesMatrix(mm);
+
+  float devPixelPerCSSPixel = float(AppUnitsPerCSSPixel()) /
+                              aFrame->PresContext()->AppUnitsPerDevPixel();
+
+  // The matrix obtained by nsIFrame::GetTransformMatrix is "from
+  // device space to device space", we need to change it to be "from
+  // user space to user space".
+  ret.PreScale(devPixelPerCSSPixel, devPixelPerCSSPixel);
+  ret.PostScale(1.f / devPixelPerCSSPixel, 1.f / devPixelPerCSSPixel);
+
+  return ret;
+}
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -589,11 +589,18 @@ class nsSVGUtils {
    * its usual dev pixels to SVG user units/CSS px to keep the SVG code happy.
    */
   static gfxMatrix GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame);
 
   static bool IsInSVGTextSubtree(const nsIFrame* aFrame) {
     // Returns true if the frame is an SVGTextFrame or one of its descendants.
     return aFrame->GetStateBits() & NS_FRAME_IS_SVG_TEXT;
   }
+
+  /**
+   * A simple wrapper of nsLayoutUtils::GetTransformToAncestor to avoid
+   * boilerplate code for changing unit and matrix format.
+   */
+  static gfxMatrix GetTransformMatrixInUserSpace(const nsIFrame* aFrame,
+                                                 const nsIFrame* aAncestor);
 };
 
 #endif