Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame. draft
authorcku <cku@mozilla.com>
Fri, 25 Aug 2017 17:51:58 +0800
changeset 662747 063987f16a399ca2489f52969ca5f7a96ade76c7
parent 662746 a9a5f3f5b9e5d84f1e35ab99fb6e68b3aeb9b011
child 662748 2a15887f66a80a7b6ff0e6aa216629fbf2a5723e
push id79187
push userbmo:cku@mozilla.com
push dateTue, 12 Sep 2017 03:20:38 +0000
bugs1385239
milestone57.0a1
Bug 1385239 - Part 2. Remove aTransform parameter from PaintFilteredFrame. There are two callers of nsFilterInstance::PaintFilteredFrame: 1. nsSVGUtils::PaintFrameWithEffects at [1] This function is used while painting a filtered element on a path which display item painting is not supported yet, such as drawing elements inside a indirect painted SVG object, such as a mask, a clipPath or a gradient object. Let's say we have a masked element, which refers to an SVG mask, and there is a filtered element inside that SVG mask. Using nsFilterInstance::PaintFilteredFrame to paint that filtered frame in the mask, we have to pass a gfxContext and a transform matrix to it. The transform of the gfxContext 'target' that we pass in consists of a transform from the referenced frame, of the masked frame, to the masked frame. We also pass in a transform matrix 'aTransform', this matrix contains a transform from the the masked frame to the filtered frame in *device units*, which means it contains css-to-dev-px scaling factor. 2. nsSVGIntegrationUtils::PaintFilter at [2] This function is used by normal display item painting. The same, we pass a gfxContext 'context' and a transform matrix 'tm' into nsFilterInstance::PaintFilteredFrame. The transform matrix of 'context' consists of a transform from the referenced frame, of the filtered frame, to this filtered frame, but the scale factor was taken out . The transform matrix 'tm' we pass in contains scale transform from the referenced frame to the filtered frame in *device unit*. Inside nsFilterInstance::PaintFilteredFrame, we treat the transform matrix of 'aCtx' and 'aTransform' as parameters we pass in in #2 caller. So it can be failed in #1. For example, if the filtered frame inside a masked frame has a translation transform applied, since that translation was put in 'aTransfrom', but we only use the scale factor of 'aTransform' in nsFilterInstance::PaintFilteredFrame, translation factor disappears. In this patch, I unified the definition of parameters of nsFilterInstance::PaintFilteredFrame: 1. nsFilterInstance::PaintFilteredFrame(aCtx): the transform matrix of aCtx should be a transform from the referenced frame to the filtered frame in *css units*. Originally, the aCtx we passed in #1 is in device units, which should be fixed; the aCtx we passed in #2 does not even include css scaling factor, need be fixed too. 2. nsFilterInstance::PaintFilteredFrame(aTransform): this transform matrix should contain only scaling factor in device units. And I removed it in the end since I found we can get this value easily right inside the callee. [1] https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGUtils.cpp#l857 [2] https://hg.mozilla.org/mozilla-central/file/ef585ac7c476/layout/svg/nsSVGIntegrationUtils.cpp#l1114 MozReview-Commit-ID: gRV128NyQv
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -57,29 +57,45 @@ UserSpaceMetricsForFrame(nsIFrame* aFram
     return MakeUnique<SVGElementMetrics>(element);
   }
   return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
 }
 
 void
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                      gfxContext* aCtx,
-                                     const gfxMatrix& aTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea,
                                      imgDrawingParams& aImgParams)
 {
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+
+  gfxContextMatrixAutoSaveRestore autoSR(aCtx);
+  gfxSize scaleFactors = aCtx->CurrentMatrix().ScaleFactors(true);
+  gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
+                        0.0f, scaleFactors.height,
+                        0.0f, 0.0f);
+
+  gfxMatrix reverseScaleMatrix = scaleMatrix;
+  DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
+  MOZ_ASSERT(invertible);
+  // Pull scale vector out of aCtx's transform, put all scale factors, which
+  // includes css and css-to-dev-px scale, into scaleMatrixInDevUnits.
+  aCtx->SetMatrix(reverseScaleMatrix * aCtx->CurrentMatrix());
+
+  gfxMatrix scaleMatrixInDevUnits =
+    scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(aFilteredFrame);
+
   // Hardcode InputIsTainted to true because we don't want JS to be able to
   // read the rendered contents of aFilteredFrame.
   nsFilterInstance instance(aFilteredFrame, aFilteredFrame->GetContent(),
                             *metrics, filterChain, /* InputIsTainted */ true,
-                            aPaintCallback, aTransform, aDirtyArea, nullptr,
-                            nullptr, nullptr);
+                            aPaintCallback, scaleMatrixInDevUnits,
+                            aDirtyArea, nullptr, nullptr, nullptr);
   if (instance.IsInitialized()) {
     instance.Render(aCtx, aImgParams);
   }
 }
 
 nsRegion
 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPreFilterDirtyRegion)
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -80,17 +80,16 @@ public:
   /**
    * Paint the given filtered frame.
    * @param aDirtyArea The area than needs to be painted, in aFilteredFrame's
    *   frame space (i.e. relative to its origin, the top-left corner of its
    *   border box).
    */
   static void PaintFilteredFrame(nsIFrame *aFilteredFrame,
                                  gfxContext* aCtx,
-                                 const gfxMatrix& aTransform,
                                  nsSVGFilterPaintCallback *aPaintCallback,
                                  const nsRegion* aDirtyArea,
                                  imgDrawingParams& aImgParams);
 
   /**
    * Returns the post-filter area that could be dirtied when the given
    * pre-filter area of aFilteredFrame changes.
    * @param aPreFilterDirtyRegion The pre-filter area of aFilteredFrame that has
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -1089,30 +1089,19 @@ nsSVGIntegrationUtils::PaintFilter(const
     context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
                                   nullptr, Matrix());
   }
 
   /* Paint the child and apply filters */
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
                                      offsets.offsetToUserSpaceInDevPx);
   nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
-  gfxSize scaleFactors = context.CurrentMatrix().ScaleFactors(true);
-  gfxMatrix scaleMatrix(scaleFactors.width, 0.0f,
-                        0.0f, scaleFactors.height,
-                        0.0f, 0.0f);
-  gfxMatrix reverseScaleMatrix = scaleMatrix;
-  DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
-  MOZ_ASSERT(invertible);
-  context.SetMatrix(reverseScaleMatrix * context.CurrentMatrix());
 
-  gfxMatrix tm =
-    scaleMatrix * nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
-  nsFilterInstance::PaintFilteredFrame(frame, &context,
-                                       tm, &callback, &dirtyRegion,
-                                       aParams.imgParams);
+  nsFilterInstance::PaintFilteredFrame(frame, &context, &callback,
+                                       &dirtyRegion, aParams.imgParams);
 
   if (opacity != 1.0f) {
     context.PopGroupAndBlend();
   }
 }
 
 class PaintFrameCallback : public gfxDrawingCallback {
 public:
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -848,19 +848,29 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRegion =
         nsLayoutUtils::RoundGfxRectToAppRect(
           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
         aFrame->GetPosition();
       dirtyRegion = &tmpDirtyRegion;
     }
 
+    gfxContextMatrixAutoSaveRestore autoSR(target);
+
+    // 'target' is currently scaled such that its user space units are CSS
+    // pixels (SVG user space units). But PaintFilteredFrame expects it to be
+    // scaled in such a way that its user space units are device pixels. So we
+    // have to adjust the scale.
+    gfxMatrix reverseScaleMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aFrame);
+    DebugOnly<bool> invertible = reverseScaleMatrix.Invert();
+    target->SetMatrix(reverseScaleMatrix * aTransform *
+                      target->CurrentMatrix());
+
     SVGPaintCallback paintCallback;
-    nsFilterInstance::PaintFilteredFrame(aFrame, target,
-                                         aTransform, &paintCallback,
+    nsFilterInstance::PaintFilteredFrame(aFrame, target, &paintCallback,
                                          dirtyRegion, aImgParams);
   } else {
      svgFrame->PaintSVG(*target, aTransform, aImgParams, aDirtyRect);
   }
 
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     aContext.PopClip();
   }