Bug 1224207 - (WIP) Pass all test case. draft
authorcku <cku@mozilla.com>
Fri, 20 Jan 2017 01:09:32 +0800
changeset 465702 52de577df73f33c8cd875f7de53e2716e7123365
parent 463342 96cb95af530477edb66ae48d98c18533476e57bb
child 543220 d780c37e0c1885b2b94e5a6c76a08a2ab0227465
push id42677
push userbmo:cku@mozilla.com
push dateTue, 24 Jan 2017 17:50:36 +0000
bugs1224207
milestone53.0a1
Bug 1224207 - (WIP) Pass all test case. 1. Pass offset in device pixel unit 2. Keep old context of the basic manager before replacing. MozReview-Commit-ID: IiFOynEw2Hq
layout/painting/nsDisplayList.cpp
layout/reftests/svg/filter-scaled-02.html
layout/svg/nsFilterInstance.cpp
layout/svg/nsFilterInstance.h
layout/svg/nsSVGIntegrationUtils.cpp
layout/svg/nsSVGUtils.cpp
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7161,24 +7161,17 @@ ComputeMaskGeometry(PaintFramesParams& a
   nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
 
   if (maskFrames.Length() == 0) {
     return;
   }
 
   gfxContext& ctx = aParams.ctx;
   nsIFrame* frame = aParams.frame;
-
   nsPoint offsetToUserSpace = ComputeOffsetToUserSpace(aParams);
-  gfxPoint devPixelOffsetToUserSpace =
-    nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
-                                   frame->PresContext()->AppUnitsPerDevPixel());
-
-  gfxContextMatrixAutoSaveRestore matSR(&ctx);
-  ctx.SetMatrix(ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
 
   // Convert boaderArea and dirtyRect to user space.
   int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
   nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
   nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
 
   // Union all mask layer rectangles in user space.
   gfxRect maskInUserSpace;
@@ -7199,26 +7192,26 @@ ComputeMaskGeometry(PaintFramesParams& a
                                        appUnitsPerDevPixel,
                                        &clipState);
       currentMaskSurfaceRect = clipState.mDirtyRectGfx;
     }
 
     maskInUserSpace = maskInUserSpace.Union(currentMaskSurfaceRect);
   }
 
-  ctx.Save();
-
+  gfxContextAutoSaveRestore autoSR(&ctx);
+  gfxPoint devPixelOffsetToUserSpace =
+    nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
+                                  frame->PresContext()->AppUnitsPerDevPixel());
+  ctx.SetMatrix(ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
   if (!maskInUserSpace.IsEmpty()) {
     ctx.Clip(maskInUserSpace);
   }
 
-  IntRect result = ComputeClipExtsInDeviceSpace(ctx);
-  ctx.Restore();
-
-  aParams.maskRect = result;
+  aParams.maskRect = ComputeClipExtsInDeviceSpace(ctx);
 }
 
 nsDisplayMask::nsDisplayMask(nsDisplayListBuilder* aBuilder,
                              nsIFrame* aFrame, nsDisplayList* aList,
                              bool aHandleOpacity,
                              const DisplayItemScrollClip* aScrollClip)
   : nsDisplaySVGEffects(aBuilder, aFrame, aList, aHandleOpacity, aScrollClip)
 {
@@ -7618,16 +7611,17 @@ nsDisplayFilter::PaintAsLayer(nsDisplayL
   nsRect borderArea = nsRect(ToReferenceFrame(), mFrame->GetSize());
   nsSVGIntegrationUtils::PaintFramesParams params(*aCtx->ThebesContext(),
                                                   mFrame,  mVisibleRect,
                                                   borderArea, aBuilder,
                                                   aManager,
                                                   mHandleOpacity);
 
   image::DrawResult result = nsSVGIntegrationUtils::PaintFilter(params);
+
   nsDisplayFilterGeometry::UpdateDrawResult(this, result);
 }
 
 #ifdef MOZ_DUMP_PAINTING
 void
 nsDisplayFilter::PrintEffects(nsACString& aTo)
 {
   nsIFrame* firstFrame =
--- a/layout/reftests/svg/filter-scaled-02.html
+++ b/layout/reftests/svg/filter-scaled-02.html
@@ -17,9 +17,9 @@
      </filter>
      <rect fill="green" x="120" y="100" width="100" height="120"
            filter="url(#filter)"/>
      <rect fill="none" stroke="black" stroke-width="4"
            x="120" y="100" width="100" height="120"/>
     </svg>
    </div>
   </body>
-</html>
+</html>
\ No newline at end of file
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -53,35 +53,67 @@ static UniquePtr<UserSpaceMetrics>
 UserSpaceMetricsForFrame(nsIFrame* aFrame)
 {
   if (aFrame->GetContent()->IsSVGElement()) {
     nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
     return MakeUnique<SVGElementMetrics>(element);
   }
   return MakeUnique<NonSVGFrameUserSpaceMetrics>(aFrame);
 }
+/*
+// Move to nsXXXXUtils!!
+static IntRect
+ComputeClipExtsInDeviceSpace(gfxContext& aCtx)
+{
+  gfxContextMatrixAutoSaveRestore matRestore(&aCtx);
 
+  // Get the clip extents in device space.
+  aCtx.SetMatrix(gfxMatrix());
+  gfxRect clippedFrameSurfaceRect = aCtx.GetClipExtents();
+  clippedFrameSurfaceRect.RoundOut();
+
+  IntRect result;
+  ToRect(clippedFrameSurfaceRect).ToIntRect(&result);
+  return mozilla::gfx::Factory::CheckSurfaceSize(result.Size()) ? result
+                                                                : IntRect();
+}*/
 nsresult
 nsFilterInstance::PaintFilteredFrame(nsIFrame *aFilteredFrame,
-                                     DrawTarget* aDrawTarget,
-                                     const gfxMatrix& aTransform,
+                                     gfxContext* aContext,
+                                     const gfxMatrix& aPaintSourceTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion *aDirtyArea)
 {
   auto& filterChain = aFilteredFrame->StyleEffects()->mFilters;
   UniquePtr<UserSpaceMetrics> metrics = UserSpaceMetricsForFrame(aFilteredFrame);
+
+  //IntRect intBounds = ComputeClipExtsInDeviceSpace(*aContext);
+  //int32_t A2D = aFilteredFrame->PresContext()->AppUnitsPerDevPixel();
+  //nsRect bounds = ToAppUnits(intBounds, A2D);
+
+  // What I changed here
+  // 1. aDirtyArea: I do not pass this parameter from nsSVGIntegrationUtils,
+  //    so that this mPostFilterDirtyRegion and mPreFilterDirtyRegion will
+  //    be computed from bounds.
+  // 2. bounds is in device space.
+  // 3. aTransform passed from nsSVGIntegrationUtils is aContext's
+  //    CurrentMatrix
+
   // 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);
+  nsFilterInstance instance(aFilteredFrame,
+                            aFilteredFrame->GetContent(), *metrics,
+                            filterChain, /* InputIsTainted */ true,
+                            aPaintCallback, aPaintSourceTransform,
+                            aDirtyArea, nullptr, nullptr,
+                            nullptr);
   if (!instance.IsInitialized()) {
     return NS_OK;
   }
-  return instance.Render(aDrawTarget);
+  return instance.Render(aContext);
 }
 
 nsRegion
 nsFilterInstance::GetPostFilterDirtyArea(nsIFrame *aFilteredFrame,
                                          const nsRegion& aPreFilterDirtyRegion)
 {
   if (aPreFilterDirtyRegion.IsEmpty()) {
     return nsRegion();
@@ -153,32 +185,46 @@ nsFilterInstance::GetPostFilterBounds(ns
                             aOverrideBBox);
   if (!instance.IsInitialized()) {
     return nsRect();
   }
 
   return instance.ComputePostFilterExtents();
 }
 
+/* static */
+gfxMatrix
+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);
+}
+
+
 nsFilterInstance::nsFilterInstance(nsIFrame *aTargetFrame,
                                    nsIContent* aTargetContent,
                                    const UserSpaceMetrics& aMetrics,
                                    const nsTArray<nsStyleFilter>& aFilterChain,
                                    bool aFilterInputIsTainted,
                                    nsSVGFilterPaintCallback *aPaintCallback,
-                                   const gfxMatrix& aPaintTransform,
+                                   const gfxMatrix& aPaintSourceTransform,
                                    const nsRegion *aPostFilterDirtyRegion,
                                    const nsRegion *aPreFilterDirtyRegion,
                                    const nsRect *aPreFilterVisualOverflowRectOverride,
                                    const gfxRect *aOverrideBBox)
   : mTargetFrame(aTargetFrame)
   , mTargetContent(aTargetContent)
   , mMetrics(aMetrics)
   , mPaintCallback(aPaintCallback)
-  , mPaintTransform(aPaintTransform)
+  , mPaintTransform(aPaintSourceTransform)
   , mInitialized(false)
 {
   if (aOverrideBBox) {
     mTargetBBox = *aOverrideBBox;
   } else {
     MOZ_ASSERT(mTargetFrame, "Need to supply a frame when there's no aOverrideBBox");
     mTargetBBox = nsSVGUtils::GetBBox(mTargetFrame);
   }
@@ -199,17 +245,17 @@ nsFilterInstance::nsFilterInstance(nsIFr
   // Get various transforms:
 
   gfxMatrix filterToUserSpace(mFilterSpaceToUserSpaceScale.width, 0.0f,
                               0.0f, mFilterSpaceToUserSpaceScale.height,
                               0.0f, 0.0f);
 
   // Only used (so only set) when we paint:
   if (mPaintCallback) {
-    mFilterSpaceToDeviceSpaceTransform = filterToUserSpace * mPaintTransform;
+    mFilterSpaceToDeviceSpaceTransform = GetCSSPxToDevPxMatrix(mTargetFrame);
   }
 
   mFilterSpaceToFrameSpaceInCSSPxTransform =
     filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
   // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
   mFrameSpaceInCSSPxToFilterSpaceTransform =
     mFilterSpaceToFrameSpaceInCSSPxTransform;
   mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
@@ -240,34 +286,30 @@ nsFilterInstance::nsFilterInstance(nsIFr
   mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
 
   mInitialized = true;
 }
 
 nsresult
 nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
 {
-  gfxMatrix canvasTransform;
   if (mTargetFrame) {
-    canvasTransform = nsSVGUtils::GetCanvasTM(mTargetFrame);
-    if (canvasTransform.IsSingular()) {
+    mUserSpaceToFilterSpaceScale = mPaintTransform.ScaleFactors(true);
+    if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
+      mUserSpaceToFilterSpaceScale.height <= 0.0f) {
       // Nothing should be rendered.
       return NS_ERROR_FAILURE;
     }
+  } else {
+    mUserSpaceToFilterSpaceScale = gfxSize(1.0, 1.0);
   }
 
-  mUserSpaceToFilterSpaceScale = canvasTransform.ScaleFactors(true);
-  if (mUserSpaceToFilterSpaceScale.width <= 0.0f ||
-      mUserSpaceToFilterSpaceScale.height <= 0.0f) {
-    // Nothing should be rendered.
-    return NS_ERROR_FAILURE;
-  }
-
-  mFilterSpaceToUserSpaceScale = gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
-                                         1.0f / mUserSpaceToFilterSpaceScale.height);
+  mFilterSpaceToUserSpaceScale =
+    gfxSize(1.0f / mUserSpaceToFilterSpaceScale.width,
+            1.0f / mUserSpaceToFilterSpaceScale.height);
   return NS_OK;
 }
 
 gfxRect
 nsFilterInstance::UserSpaceToFilterSpace(const gfxRect& aUserSpaceRect) const
 {
   gfxRect filterSpaceRect = aUserSpaceRect;
   filterSpaceRect.Scale(mUserSpaceToFilterSpaceScale.width,
@@ -361,79 +403,81 @@ nsFilterInstance::ComputeNeededBoxes()
   sourceGraphicNeededRegion.And(sourceGraphicNeededRegion, mTargetBounds);
 
   mSourceGraphic.mNeededBounds = sourceGraphicNeededRegion.GetBounds();
   mFillPaint.mNeededBounds = fillPaintNeededRegion.GetBounds();
   mStrokePaint.mNeededBounds = strokePaintNeededRegion.GetBounds();
 }
 
 nsresult
-nsFilterInstance::BuildSourcePaint(SourceInfo *aSource,
-                                   DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaint(SourceInfo *aSource)
 {
   MOZ_ASSERT(mTargetFrame);
   nsIntRect neededRect = aSource->mNeededBounds;
 
   RefPtr<DrawTarget> offscreenDT =
     gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
       neededRect.Size(), SurfaceFormat::B8G8R8A8);
   if (!offscreenDT || !offscreenDT->IsValid()) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
   if (!deviceToFilterSpace.Invert()) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!mPaintTransform.IsSingular()) {
-    RefPtr<gfxContext> gfx = gfxContext::CreateOrNull(offscreenDT);
-    MOZ_ASSERT(gfx); // already checked the draw target above
-    gfx->Save();
-    gfx->Multiply(mPaintTransform *
-                  deviceToFilterSpace *
-                  gfxMatrix::Translation(-neededRect.TopLeft()));
+  if (!deviceToFilterSpace.IsSingular()) {
+    RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
+    MOZ_ASSERT(ctx); // already checked the draw target above
+    //gfx->Multiply(mPaintTransform *
+    //              deviceToFilterSpace *
+    //              gfxMatrix::Translation(-neededRect.TopLeft()));
+    gfxContextAutoSaveRestore saver(ctx);
+
+    // deviceToFilterSpace = filterToUserSpace * mPaintTransform
+    ctx->SetMatrix(mPaintTransform *
+                   gfxMatrix::Translation(-neededRect.TopLeft()));
     GeneralPattern pattern;
     if (aSource == &mFillPaint) {
-      nsSVGUtils::MakeFillPatternFor(mTargetFrame, gfx, &pattern);
+      nsSVGUtils::MakeFillPatternFor(mTargetFrame, ctx, &pattern);
     } else if (aSource == &mStrokePaint) {
-      nsSVGUtils::MakeStrokePatternFor(mTargetFrame, gfx, &pattern);
+      nsSVGUtils::MakeStrokePatternFor(mTargetFrame, ctx, &pattern);
     }
     if (pattern.GetPattern()) {
       offscreenDT->FillRect(ToRect(FilterSpaceToUserSpace(ThebesRect(neededRect))),
                             pattern);
     }
-    gfx->Restore();
   }
 
   aSource->mSourceSurface = offscreenDT->Snapshot();
   aSource->mSurfaceRect = neededRect;
 
   return NS_OK;
 }
 
 nsresult
-nsFilterInstance::BuildSourcePaints(DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourcePaints()
 {
   nsresult rv = NS_OK;
 
   if (!mFillPaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mFillPaint, aTargetDT);
+    rv = BuildSourcePaint(&mFillPaint);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!mStrokePaint.mNeededBounds.IsEmpty()) {
-    rv = BuildSourcePaint(&mStrokePaint, aTargetDT);
+    rv = BuildSourcePaint(&mStrokePaint);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return  rv;
 }
 
 nsresult
-nsFilterInstance::BuildSourceImage(DrawTarget* aTargetDT)
+nsFilterInstance::BuildSourceImage()
 {
   MOZ_ASSERT(mTargetFrame);
 
   nsIntRect neededRect = mSourceGraphic.mNeededBounds;
   if (neededRect.IsEmpty()) {
     return NS_OK;
   }
 
@@ -456,64 +500,71 @@ nsFilterInstance::BuildSourceImage(DrawT
   //
   // (In theory it would be better to minimize error by having filtered SVG
   // graphics temporarily paint to user space when painting the sources and
   // only set a user space to filter space transform on the gfxContext
   // (since that would eliminate the transform multiplications from user
   // space to device space and back again). However, that would make the
   // code more complex while being hard to get right without introducing
   // subtle bugs, and in practice it probably makes no real difference.)
-  gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
-  if (!deviceToFilterSpace.Invert()) {
-    return NS_ERROR_FAILURE;
-  }
   RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(offscreenDT);
   MOZ_ASSERT(ctx); // already checked the draw target above
-  ctx->SetMatrix(
-    ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
-                         PreMultiply(deviceToFilterSpace));
+  gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
+  deviceToFilterSpace.Invert();
+ //   ctx->SetMatrix(
+ //   ctx->CurrentMatrix().Translate(-neededRect.TopLeft()).
+ //                        PreMultiply(deviceToFilterSpace));*/
+  gfxMatrix toBBoxTM =
+    deviceToFilterSpace * mPaintTransform * gfxMatrix::Translation(-neededRect.TopLeft());
+  ctx->SetMatrix(toBBoxTM);
 
   DrawResult result =
     mPaintCallback->Paint(*ctx, mTargetFrame, mPaintTransform, &dirty);
 
   mSourceGraphic.mSourceSurface = offscreenDT->Snapshot();
   mSourceGraphic.mSurfaceRect = neededRect;
 
   return (result == DrawResult::SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
-nsFilterInstance::Render(DrawTarget* aDrawTarget)
+nsFilterInstance::Render(gfxContext* aContext)
 {
   MOZ_ASSERT(mTargetFrame, "Need a frame for rendering");
 
   nsIntRect filterRect =
     mPostFilterDirtyRegion.GetBounds().Intersect(OutputFilterSpaceBounds());
   gfxMatrix ctm = GetFilterSpaceToDeviceSpaceTransform();
-
   if (filterRect.IsEmpty() || ctm.IsSingular()) {
     return NS_OK;
   }
 
-  AutoRestoreTransform autoRestoreTransform(aDrawTarget);
-  Matrix newTM = ToMatrix(ctm).PreTranslate(filterRect.x, filterRect.y) *
-                 aDrawTarget->GetTransform();
-  aDrawTarget->SetTransform(newTM);
-
   ComputeNeededBoxes();
 
-  nsresult rv = BuildSourceImage(aDrawTarget);
+  nsresult rv = BuildSourceImage();
+  if (NS_FAILED(rv))
+    return rv;
+  rv = BuildSourcePaints();
   if (NS_FAILED(rv))
     return rv;
-  rv = BuildSourcePaints(aDrawTarget);
-  if (NS_FAILED(rv))
-    return rv;
+  gfxMatrix deviceToFilterSpace = GetFilterSpaceToDeviceSpaceTransform();
+
+  gfxContextMatrixAutoSaveRestore saver(aContext);
+  gfxMatrix filterTM = mPaintTransform;
+  filterTM.Invert();
+
+  gfxMatrix toBoundingBox =
+   gfxMatrix::Translation(filterRect.x, filterRect.y) *
+   deviceToFilterSpace *
+   filterTM *
+   aContext->CurrentMatrix();
+  aContext->SetMatrix(toBoundingBox);
 
   FilterSupport::RenderFilterDescription(
-    aDrawTarget, mFilterDescription, IntRectToRect(filterRect),
+    aContext->GetDrawTarget(), mFilterDescription, IntRectToRect(filterRect),
     mSourceGraphic.mSourceSurface, mSourceGraphic.mSurfaceRect,
     mFillPaint.mSourceSurface, mFillPaint.mSurfaceRect,
     mStrokePaint.mSourceSurface, mStrokePaint.mSurfaceRect,
     mInputImages, Point(0, 0));
 
   return NS_OK;
 }
 
@@ -564,18 +615,27 @@ nsFilterInstance::OutputFilterSpaceBound
 nsIntRect
 nsFilterInstance::FrameSpaceToFilterSpace(const nsRect* aRect) const
 {
   nsIntRect rect = OutputFilterSpaceBounds();
   if (aRect) {
     if (aRect->IsEmpty()) {
       return nsIntRect();
     }
+
+    int32_t A2D = mTargetFrame->PresContext()->AppUnitsPerDevPixel();
+    gfxRect devBounds =
+      mFilterSpaceToDeviceSpaceTransform.Transform(
+        nsLayoutUtils::RectToGfxRect(*aRect, A2D));
+
+    nsRect devRect = nsRect(devBounds.x * A2D, devBounds.y * A2D,
+                         devBounds.width * A2D, devBounds.height * A2D);
+
     gfxRect rectInCSSPx =
-      nsLayoutUtils::RectToGfxRect(*aRect, nsPresContext::AppUnitsPerCSSPixel());
+      nsLayoutUtils::RectToGfxRect(devRect, nsPresContext::AppUnitsPerCSSPixel());
     gfxRect rectInFilterSpace =
       mFrameSpaceInCSSPxToFilterSpaceTransform.TransformBounds(rectInCSSPx);
     rectInFilterSpace.RoundOut();
     nsIntRect intRect;
     if (gfxUtils::GfxRectToIntRect(rectInFilterSpace, &intRect)) {
       rect = intRect;
     }
   }
--- a/layout/svg/nsFilterInstance.h
+++ b/layout/svg/nsFilterInstance.h
@@ -79,18 +79,18 @@ 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 nsresult PaintFilteredFrame(nsIFrame *aFilteredFrame,
-                                     DrawTarget* aDrawTarget,
-                                     const gfxMatrix& aTransform,
+                                     gfxContext* aContext,
+                                     const gfxMatrix& aPaintSourceTransform,
                                      nsSVGFilterPaintCallback *aPaintCallback,
                                      const nsRegion* aDirtyArea);
 
   /**
    * 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
    *   changed, relative to aFilteredFrame, in app units.
@@ -150,28 +150,16 @@ public:
                    bool aFilterInputIsTainted,
                    nsSVGFilterPaintCallback *aPaintCallback,
                    const gfxMatrix& aPaintTransform,
                    const nsRegion *aPostFilterDirtyRegion = nullptr,
                    const nsRegion *aPreFilterDirtyRegion = nullptr,
                    const nsRect *aOverridePreFilterVisualOverflowRect = nullptr,
                    const gfxRect *aOverrideBBox = nullptr);
 
-  /**
-   * Returns true if the filter instance was created successfully.
-   */
-  bool IsInitialized() const { return mInitialized; }
-
-  /**
-   * Draws the filter output into aDrawTarget. The area that
-   * needs to be painted must have been specified before calling this method
-   * by passing it as the aPostFilterDirtyRegion argument to the
-   * nsFilterInstance constructor.
-   */
-  nsresult Render(DrawTarget* aDrawTarget);
 
   const FilterDescription& ExtractDescriptionAndAdditionalImages(nsTArray<RefPtr<SourceSurface>>& aOutAdditionalImages)
   {
     mInputImages.SwapElements(aOutAdditionalImages);
     return mFilterDescription;
   }
 
   /**
@@ -205,16 +193,29 @@ public:
   /**
    * Returns the transform from filter space to outer-<svg> device space.
    */
   gfxMatrix GetFilterSpaceToDeviceSpaceTransform() const {
     return mFilterSpaceToDeviceSpaceTransform;
   }
 
 private:
+  /**
+   * Returns true if the filter instance was created successfully.
+   */
+  bool IsInitialized() const { return mInitialized; }
+
+  /**
+   * Draws the filter output into aDrawTarget. The area that
+   * needs to be painted must have been specified before calling this method
+   * by passing it as the aPostFilterDirtyRegion argument to the
+   * nsFilterInstance constructor.
+   */
+  nsresult Render(gfxContext* aContext);
+
   struct SourceInfo {
     // Specifies which parts of the source need to be rendered.
     // Set by ComputeNeededBoxes().
     nsIntRect mNeededBounds;
 
     // The surface that contains the input rendering.
     // Set by BuildSourceImage / BuildSourcePaint.
     RefPtr<SourceSurface> mSourceSurface;
@@ -223,31 +224,30 @@ private:
     // Set by BuildSourceImage / BuildSourcePaint.
     IntRect mSurfaceRect;
   };
 
   /**
    * Creates a SourceSurface for either the FillPaint or StrokePaint graph
    * nodes
    */
-  nsresult BuildSourcePaint(SourceInfo *aPrimitive,
-                            DrawTarget* aTargetDT);
+  nsresult BuildSourcePaint(SourceInfo *aPrimitive);
 
   /**
    * Creates a SourceSurface for either the FillPaint and StrokePaint graph
    * nodes, fills its contents and assigns it to mFillPaint.mSourceSurface and
    * mStrokePaint.mSourceSurface respectively.
    */
-  nsresult BuildSourcePaints(DrawTarget* aTargetDT);
+  nsresult BuildSourcePaints();
 
   /**
    * Creates the SourceSurface for the SourceGraphic graph node, paints its
    * contents, and assigns it to mSourceGraphic.mSourceSurface.
    */
-  nsresult BuildSourceImage(DrawTarget* aTargetDT);
+  nsresult BuildSourceImage();
 
   /**
    * Build the list of FilterPrimitiveDescriptions that describes the filter's
    * filter primitives and their connections. This populates
    * mPrimitiveDescriptions and mInputImages. aFilterInputIsTainted describes
    * whether the SourceGraphic is tainted.
    */
   nsresult BuildPrimitives(const nsTArray<nsStyleFilter>& aFilterChain,
@@ -374,17 +374,18 @@ private:
 
   SourceInfo mSourceGraphic;
   SourceInfo mFillPaint;
   SourceInfo mStrokePaint;
 
   /**
    * The transform to the SVG user space of mTargetFrame.
    */
-  gfxMatrix               mPaintTransform;
+  const gfxMatrix         mPaintTransform;
+  const gfxMatrix         mFilterTransform;
 
   nsTArray<RefPtr<SourceSurface>> mInputImages;
   nsTArray<FilterPrimitiveDescription> mPrimitiveDescriptions;
   FilterDescription mFilterDescription;
   bool mInitialized;
 };
 
 #endif
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -398,42 +398,42 @@ nsSVGIntegrationUtils::HitTestFrameForEf
   return nsSVGUtils::HitTestClip(firstFrame, userSpacePt);
 }
 
 class RegularFramePaintCallback : public nsSVGFilterPaintCallback
 {
 public:
   RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
                             LayerManager* aManager,
-                            const nsPoint& aOffset)
+                            const gfxPoint& aUserSpaceToFrameSpace)
     : mBuilder(aBuilder), mLayerManager(aManager),
-      mOffset(aOffset) {}
+      mUserSpaceToFrameSpace(aUserSpaceToFrameSpace)
+    {}
 
   virtual DrawResult Paint(gfxContext& aContext, nsIFrame *aTarget,
                            const gfxMatrix& aTransform,
                            const nsIntRect* aDirtyRect) override
   {
     BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
+    RefPtr<gfxContext> oldCtx = basic->GetTarget();
     basic->SetTarget(&aContext);
 
-    gfxPoint devPixelOffset =
-      nsLayoutUtils::PointToGfxPoint(-mOffset,
-                                     aTarget->PresContext()->AppUnitsPerDevPixel());
+    gfxContextMatrixAutoSaveRestore autoSR(&aContext);
+    aContext.SetMatrix(aContext.CurrentMatrix().Translate(-mUserSpaceToFrameSpace));
 
-    gfxContextMatrixAutoSaveRestore autoSR(&aContext);
-    aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffset));
-
-    mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
+    mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
+                                  mBuilder);
+    basic->SetTarget(oldCtx);
     return DrawResult::SUCCESS;
   }
 
 private:
   nsDisplayListBuilder* mBuilder;
   LayerManager* mLayerManager;
-  nsPoint mOffset;
+  gfxPoint mUserSpaceToFrameSpace;
 };
 
 typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
 
 /**
  * Paint css-positioned-mask onto a given target(aMaskDT).
  */
 static DrawResult
@@ -648,27 +648,88 @@ ValidateSVGFrame(nsIFrame* aFrame)
       // The SVG spec says not to draw _anything_
       return false;
     }
   }
 
   return true;
 }
 
+struct OffsetValues {
+  nsPoint  offsetToBoundingBox;
+  gfxPoint offsetToBoundingBoxInDevPx;
+  nsPoint  offsetToUserSpace;
+  gfxPoint offsetToUserSpaceInDevPx;
+};
+
+OffsetValues
+ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
+{
+  OffsetValues result;
+  result.offsetToBoundingBox =
+    aParams.builder->ToReferenceFrame(aFrame) -
+    nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
+  if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
+    /* Snap the offset if the reference frame is not a SVG frame,
+     * since other frames will be snapped to pixel when rendering. */
+    result.offsetToBoundingBox =
+      nsPoint(
+        aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x),
+        aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y));
+  }
+
+  // After applying only "aOffsetToBoundingBox", aParams.ctx would have its
+  // origin at the top left corner of frame's bounding box (over all
+  // continuations).
+  // However, SVG painting needs the origin to be located at the origin of the
+  // SVG frame's "user space", i.e. the space in which, for example, the
+  // frame's BBox lives.
+  // SVG geometry frames and foreignObject frames apply their own offsets, so
+  // their position is relative to their user space. So for these frame types,
+  // if we want aParams.ctx to be in user space, we first need to subtract the
+  // frame's position so that SVG painting can later add it again and the
+  // frame is painted in the right place.
+
+  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
+  nsPoint toUserSpace =
+    nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
+            nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
+
+  result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
+
+#ifdef DEBUG
+  bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
+  NS_ASSERTION(hasSVGLayout ||
+               result.offsetToBoundingBox == result.offsetToUserSpace,
+               "For non-SVG frames there shouldn't be any additional offset");
+#endif
+
+  result.offsetToUserSpaceInDevPx =
+    nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
+                                   aFrame->PresContext()->AppUnitsPerDevPixel());
+  result.offsetToBoundingBoxInDevPx =
+    nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
+                                   aFrame->PresContext()->AppUnitsPerDevPixel());
+
+  return result;
+}
+
 /**
  * Setup transform matrix of a gfx context by a specific frame. Depend on
  * aClipCtx, this function may clip that context by the visual overflow area
  * of aFrame.
  *
  * @param aFrame is the target frame.
  * @param aOffsetToBoundingBox returns the offset between the reference frame
  *        and the bounding box of aFrame.
  * @oaram aOffsetToUserSpace returns the offset between the reference frame and
  *        the user space coordinate of aFrame.
  */
+// TODO: rename FrameSapceToUserSpace, ,move context.SetMatrix( out?
+
 static void
 SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
                    nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace)
 {
   aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
                          nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
   if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
     /* Snap the offset if the reference frame is not a SVG frame,
@@ -1093,35 +1154,44 @@ nsSVGIntegrationUtils::PaintFilter(const
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
   if (effectProperties.HasInvalidFilter()) {
     return DrawResult::NOT_READY;
   }
 
   gfxContext& context = aParams.ctx;
+  OffsetValues offsets = ComputeEffectOffset(firstFrame, aParams);
+  gfxPoint filterPosInDevPx =
+    context.CurrentMatrix().Transform(offsets.offsetToUserSpaceInDevPx);
+
   nsPoint offsetToBoundingBox;
   nsPoint offsetToUserSpace;
-
   gfxContextAutoSaveRestore autoSR(&context);
   SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
                      offsetToUserSpace);
 
   if (opacity != 1.0f) {
     context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
                                   nullptr, Matrix());
   }
 
   /* Paint the child and apply filters */
+  gfxMatrix cssTpDevPxMatrix =
+    nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
+  gfxMatrix filterMatrix =
+    context.CurrentMatrix() * gfxMatrix::Translation(-filterPosInDevPx) * cssTpDevPxMatrix;
+
+  //gfxPoint xx =
+  //  cssTpDevPxMatrix.Transform(offsets.offsetToUserSpaceInDevPx);
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
-                                     offsetToUserSpace);
-  nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
-  gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
-  nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
-                                       tm, &callback, &dirtyRegion);
+                                     offsets.offsetToUserSpaceInDevPx);
+
+  nsFilterInstance::PaintFilteredFrame(frame, &context, filterMatrix,
+                                       &callback, nullptr);
 
   if (opacity != 1.0f) {
     context.PopGroupAndBlend();
   }
 
   return DrawResult::SUCCESS;
 }
 
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -854,19 +854,18 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
                                       aDirtyRect->width, aDirtyRect->height));
       tmpDirtyRegion =
         nsLayoutUtils::RoundGfxRectToAppRect(
           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
         aFrame->GetPosition();
       dirtyRegion = &tmpDirtyRegion;
     }
     SVGPaintCallback paintCallback;
-    nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
-                                         aTransform, &paintCallback,
-                                         dirtyRegion);
+    nsFilterInstance::PaintFilteredFrame(aFrame, target, aTransform,
+                                         &paintCallback, dirtyRegion);
   } else {
     result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
   }
 
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     aContext.PopClip();
   }