Bug 1224207 - Part 1. Split SetupContext into ComputeEffectOffset and TranslateToUserSpace. draft
authorcku <cku@mozilla.com>
Mon, 23 Jan 2017 02:36:41 +0800
changeset 480540 704eeec79846952d7c7d1be510a7b19fac4723f6
parent 480508 3a95aa4246653a7863914ffec032897d13359fb0
child 480541 410a11d200f71c77b48ebce5a93dfcdd44023590
push id44575
push userbmo:cku@mozilla.com
push dateWed, 08 Feb 2017 13:39:48 +0000
bugs1224207
milestone54.0a1
Bug 1224207 - Part 1. Split SetupContext into ComputeEffectOffset and TranslateToUserSpace. We need ComputeEffectOffset along in the following patch. MozReview-Commit-ID: GoIZ07IqoQ3
layout/svg/nsSVGIntegrationUtils.cpp
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -648,71 +648,96 @@ ValidateSVGFrame(nsIFrame* aFrame)
       // The SVG spec says not to draw _anything_
       return false;
     }
   }
 
   return true;
 }
 
-/**
- * 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.
- */
-static void
-SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
-                   nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace)
+struct EffectOffsets {
+  // The offset between the reference frame and the bounding box of the
+  // target frame in app unit.
+  nsPoint  offsetToBoundingBox;
+  // The offset between the reference frame and the bounding box of the
+  // target frame in device unit.
+  gfxPoint offsetToBoundingBoxInDevPx;
+  // The offset between the reference frame and the bounding box of the
+  // target frame in app unit.
+  nsPoint  offsetToUserSpace;
+  // The offset between the reference frame and the bounding box of the
+  // target frame in device unit.
+  gfxPoint offsetToUserSpaceInDevPx;
+};
+
+EffectOffsets
+ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
 {
-  aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
-                         nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
+  EffectOffsets 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. */
-    aOffsetToBoundingBox = nsPoint(
-      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
-      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
+    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)));
 
-  aOffsetToUserSpace = aOffsetToBoundingBox - toUserSpace;
+  result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
 
 #ifdef DEBUG
   bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
-  NS_ASSERTION(hasSVGLayout || aOffsetToBoundingBox == aOffsetToUserSpace,
+  NS_ASSERTION(hasSVGLayout ||
+               result.offsetToBoundingBox == result.offsetToUserSpace,
                "For non-SVG frames there shouldn't be any additional offset");
 #endif
 
-  gfxPoint devPixelOffsetToUserSpace =
-    nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
+  result.offsetToUserSpaceInDevPx =
+    nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
+                                   aFrame->PresContext()->AppUnitsPerDevPixel());
+  result.offsetToBoundingBoxInDevPx =
+    nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
                                    aFrame->PresContext()->AppUnitsPerDevPixel());
-  gfxContext& context = aParams.ctx;
-  context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
+
+  return result;
+}
+
+/**
+ * Setup transform matrix of a gfx context by a specific frame. Move the
+ * origin of aParams.ctx to the user space of aFrame.
+ */
+static EffectOffsets
+MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams)
+{
+  EffectOffsets offset = ComputeEffectOffset(aFrame, aParams);
+
+  aParams.ctx.SetMatrix(
+    aParams.ctx.CurrentMatrix().Translate(offset.offsetToUserSpaceInDevPx));
+
+  return offset;
 }
 
 bool
 nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
 {
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   nsSVGEffects::EffectProperties effectProperties =
@@ -793,26 +818,23 @@ nsSVGIntegrationUtils::PaintMask(const P
   bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
                            (maskFrames.Length() != 1);
   if (shouldPushOpacity) {
     ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
     autoPop.SetContext(&ctx);
   }
 
   gfxContextMatrixAutoSaveRestore matSR;
-  nsPoint offsetToBoundingBox;
-  nsPoint offsetToUserSpace;
 
   // Paint clip-path-basic-shape onto ctx
   gfxContextAutoSaveRestore basicShapeSR;
   if (maskUsage.shouldApplyBasicShape) {
     matSR.SetContext(&ctx);
 
-    SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                       offsetToUserSpace);
+    MoveContextOriginToUserSpace(firstFrame, aParams);
 
     basicShapeSR.SetContext(&ctx);
     nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame);
     if (!maskUsage.shouldGenerateMaskLayer) {
       // Only have basic-shape clip-path effect. Fill clipped region by
       // opaque white.
       ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0));
       ctx.Fill();
@@ -821,34 +843,33 @@ nsSVGIntegrationUtils::PaintMask(const P
     }
   }
 
   // Paint mask onto ctx.
   if (maskUsage.shouldGenerateMaskLayer) {
     matSR.Restore();
     matSR.SetContext(&ctx);
 
-    SetupContextMatrix(frame, aParams, offsetToBoundingBox,
-                       offsetToUserSpace);
+    EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
     result = PaintMaskSurface(aParams, maskTarget,
                               shouldPushOpacity ?  1.0 : maskUsage.opacity,
                               firstFrame->StyleContext(), maskFrames,
-                              ctx.CurrentMatrix(), offsetToUserSpace);
+                              ctx.CurrentMatrix(),
+                              offsets.offsetToUserSpace);
     if (result != DrawResult::SUCCESS) {
       return result;
     }
   }
 
   // Paint clip-path onto ctx.
   if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) {
     matSR.Restore();
     matSR.SetContext(&ctx);
 
-    SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                       offsetToUserSpace);
+    MoveContextOriginToUserSpace(firstFrame, aParams);
     Matrix clipMaskTransform;
     gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
 
     nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
     RefPtr<SourceSurface> maskSurface =
       maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr;
     result =
       clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix,
@@ -903,19 +924,16 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
 
   gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
   nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
 
-  nsPoint offsetToBoundingBox;
-  nsPoint offsetToUserSpace;
-
   bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
                              maskUsage.shouldGenerateClipMaskLayer ||
                              maskUsage.shouldGenerateMaskLayer);
   bool shouldPushMask = false;
 
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
   if (shouldGenerateMask) {
@@ -926,22 +944,21 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
     bool opacityApplied = false;
 
     if (maskUsage.shouldGenerateMaskLayer) {
       matSR.SetContext(&context);
 
       // For css-mask, we want to generate a mask for each continuation frame,
       // so we setup context matrix by the position of the current frame,
       // instead of the first continuation frame.
-      SetupContextMatrix(frame, aParams, offsetToBoundingBox,
-                         offsetToUserSpace);
+      EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
       MaskPaintResult paintResult =
         CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
                                   firstFrame->StyleContext(),
-                                  maskFrames, offsetToUserSpace);
+                                  maskFrames, offsets.offsetToUserSpace);
 
       if (paintResult.transparentBlackMask) {
         return paintResult.result;
       }
 
       result &= paintResult.result;
       maskSurface = paintResult.maskSurface;
       if (maskSurface) {
@@ -951,18 +968,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
         opacityApplied = paintResult.opacityApplied;
       }
     }
 
     if (maskUsage.shouldGenerateClipMaskLayer) {
       matSR.Restore();
       matSR.SetContext(&context);
 
-      SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                         offsetToUserSpace);
+      MoveContextOriginToUserSpace(firstFrame, aParams);
       Matrix clipMaskTransform;
       DrawResult clipMaskResult;
       RefPtr<SourceSurface> clipMaskSurface;
       Tie(clipMaskResult, clipMaskSurface) =
         clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
                                    &clipMaskTransform, maskSurface,
                                    maskTransform);
 
@@ -979,18 +995,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
     }
 
     // opacity != 1.0f.
     if (!maskUsage.shouldGenerateClipMaskLayer &&
         !maskUsage.shouldGenerateMaskLayer) {
       MOZ_ASSERT(maskUsage.opacity != 1.0f);
 
       matSR.SetContext(&context);
-      SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                         offsetToUserSpace);
+      MoveContextOriginToUserSpace(firstFrame, aParams);
       shouldPushMask = true;
     }
 
     if (shouldPushMask) {
       if (aParams.layerManager->GetRoot()->GetContentFlags() &
           Layer::CONTENT_COMPONENT_ALPHA) {
         context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
                                            opacityApplied
@@ -1006,18 +1021,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
   }
 
   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
    * we can just do normal painting and get it clipped appropriately.
    */
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     gfxContextMatrixAutoSaveRestore matSR(&context);
 
-    SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                       offsetToUserSpace);
+    MoveContextOriginToUserSpace(firstFrame, aParams);
 
     MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
                !maskUsage.shouldApplyBasicShape);
     if (maskUsage.shouldApplyClipPath) {
       clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
     } else {
       nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
     }
@@ -1093,32 +1107,29 @@ nsSVGIntegrationUtils::PaintFilter(const
   nsSVGEffects::EffectProperties effectProperties =
     nsSVGEffects::GetEffectProperties(firstFrame);
 
   if (effectProperties.HasInvalidFilter()) {
     return DrawResult::NOT_READY;
   }
 
   gfxContext& context = aParams.ctx;
-  nsPoint offsetToBoundingBox;
-  nsPoint offsetToUserSpace;
 
   gfxContextAutoSaveRestore autoSR(&context);
-  SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
-                     offsetToUserSpace);
+  EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams);
 
   if (opacity != 1.0f) {
     context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
                                   nullptr, Matrix());
   }
 
   /* Paint the child and apply filters */
   RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
-                                     offsetToUserSpace);
-  nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
+                                     offsets.offsetToUserSpace);
+  nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
   gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
   DrawResult result =
     nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
                                          tm, &callback, &dirtyRegion);
 
   if (opacity != 1.0f) {
     context.PopGroupAndBlend();
   }