Bug 1265342 Part 6b: Implement shape-margin for shape-outside: inset, for general case of shape-margin > 0. r=dholbert
☠☠ backed out by 723a9f786923 ☠ ☠
authorBrad Werth <bwerth@mozilla.com>
Tue, 17 Apr 2018 11:39:50 -0700
changeset 468976 7109288f10b3e516bf891f6f72c5c711cd993d40
parent 468975 048cce2fb99f082e994622a7ea6bcb9c6ea71475
child 468977 0791ff3d91947249711a0d880582003d5cec7b13
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1265342
milestone61.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 1265342 Part 6b: Implement shape-margin for shape-outside: inset, for general case of shape-margin > 0. r=dholbert MozReview-Commit-ID: 4pjALPSIBhI
layout/generic/nsFloatManager.cpp
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -1030,74 +1030,176 @@ public:
                     const nscoord aBEnd) const override;
   nscoord BStart() const override { return mRect.y; }
   nscoord BEnd() const override { return mRect.YMost(); }
   bool IsEmpty() const override { return mRect.IsEmpty(); }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
   {
     mRect.MoveBy(aLineLeft, aBlockStart);
+
+    if (mShapeMargin > 0) {
+      MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalTopRightCorner &&
+                 mLogicalBottomLeftCorner && mLogicalBottomRightCorner,
+                 "If we have positive shape-margin, we should have corners.");
+      mLogicalTopLeftCorner->Translate(aLineLeft, aBlockStart);
+      mLogicalTopRightCorner->Translate(aLineLeft, aBlockStart);
+      mLogicalBottomLeftCorner->Translate(aLineLeft, aBlockStart);
+      mLogicalBottomRightCorner->Translate(aLineLeft, aBlockStart);
+    }
+  }
+
+  static bool EachCornerHasBalancedRadii(const nscoord* aRadii) {
+    return (aRadii[eCornerTopLeftX] == aRadii[eCornerTopLeftY] &&
+            aRadii[eCornerTopRightX] == aRadii[eCornerTopRightY] &&
+            aRadii[eCornerBottomLeftX] == aRadii[eCornerBottomLeftY] &&
+            aRadii[eCornerBottomRightX] == aRadii[eCornerBottomRightY]);
   }
 
 private:
   // The rect of the rounded box shape in the float manager's coordinate
   // space.
   nsRect mRect;
   // The half corner radii of the reference box. It's an nscoord[8] array
   // in the float manager's coordinate space. If there are no radii, it's
   // nullptr.
   const UniquePtr<nscoord[]> mRadii;
 
-  // A shape-margin value extends the boundaries of the float area.
+  // A shape-margin value extends the boundaries of the float area. When our
+  // first constructor is used, it is for the creation of rounded boxes that
+  // can ignore shape-margin -- either because it was specified as zero or
+  // because the box shape and radii can be inflated to account for it. When
+  // our second constructor is used, we store the shape-margin value here.
   const nscoord mShapeMargin;
+
+  // If our second constructor is called (which implies mShapeMargin > 0),
+  // we will construct EllipseShapeInfo objects for each corner. We use the
+  // float logical naming here, where LogicalTopLeftCorner means the BStart
+  // LineLeft corner, and similarly for the other corners.
+  UniquePtr<EllipseShapeInfo> mLogicalTopLeftCorner;
+  UniquePtr<EllipseShapeInfo> mLogicalTopRightCorner;
+  UniquePtr<EllipseShapeInfo> mLogicalBottomLeftCorner;
+  UniquePtr<EllipseShapeInfo> mLogicalBottomRightCorner;
 };
 
 nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
   UniquePtr<nscoord[]> aRadii,
   nscoord aShapeMargin,
   int32_t aAppUnitsPerDevPixel)
   : mRect(aRect)
   , mRadii(Move(aRadii))
   , mShapeMargin(aShapeMargin)
 {
+  MOZ_ASSERT(mShapeMargin > 0 && !EachCornerHasBalancedRadii(mRadii.get()),
+             "Slow constructor should only be used for for shape-margin > 0 "
+             "and radii with elliptical corners.");
 
+  // Before we inflate mRect by mShapeMargin, construct each of our corners.
+  // If we do it in this order, it's a bit simpler to calculate the center
+  // of each of the corners.
+  mLogicalTopLeftCorner = MakeUnique<EllipseShapeInfo>(
+    nsPoint(mRect.X() + mRadii[eCornerTopLeftX],
+            mRect.Y() + mRadii[eCornerTopLeftY]),
+    nsSize(mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY]),
+    mShapeMargin, aAppUnitsPerDevPixel);
+
+  mLogicalTopRightCorner = MakeUnique<EllipseShapeInfo>(
+    nsPoint(mRect.XMost() - mRadii[eCornerTopRightX],
+            mRect.Y() + mRadii[eCornerTopRightY]),
+    nsSize(mRadii[eCornerTopRightX], mRadii[eCornerTopRightY]),
+    mShapeMargin, aAppUnitsPerDevPixel);
+
+  mLogicalBottomLeftCorner = MakeUnique<EllipseShapeInfo>(
+    nsPoint(mRect.X() + mRadii[eCornerBottomLeftX],
+            mRect.YMost() - mRadii[eCornerBottomLeftY]),
+    nsSize(mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY]),
+    mShapeMargin, aAppUnitsPerDevPixel);
+
+  mLogicalBottomRightCorner = MakeUnique<EllipseShapeInfo>(
+    nsPoint(mRect.XMost() - mRadii[eCornerBottomRightX],
+            mRect.YMost() - mRadii[eCornerBottomRightY]),
+    nsSize(mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY]),
+    mShapeMargin, aAppUnitsPerDevPixel);
+
+  // Now we inflate our mRect by mShapeMargin.
+  mRect.Inflate(mShapeMargin);
 }
 
 nscoord
 nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
                                               const nscoord aBEnd) const
 {
-  if (!mRadii) {
-    return mRect.x;
+  if (mShapeMargin == 0) {
+    if (!mRadii) {
+      return mRect.x;
+    }
+
+    nscoord lineLeftDiff =
+      ComputeEllipseLineInterceptDiff(
+        mRect.y, mRect.YMost(),
+        mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
+        mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
+        aBStart, aBEnd);
+    return mRect.x + lineLeftDiff;
   }
 
-  nscoord lineLeftDiff =
-    ComputeEllipseLineInterceptDiff(
-      mRect.y, mRect.YMost(),
-      mRadii[eCornerTopLeftX], mRadii[eCornerTopLeftY],
-      mRadii[eCornerBottomLeftX], mRadii[eCornerBottomLeftY],
-      aBStart, aBEnd);
-  return mRect.x + lineLeftDiff;
+  MOZ_ASSERT(mLogicalTopLeftCorner && mLogicalBottomLeftCorner,
+             "If we have positive shape-margin, we should have corners.");
+
+  // Determine if aBEnd is within our top corner.
+  if (aBEnd < mLogicalTopLeftCorner->BEnd()) {
+    return mLogicalTopLeftCorner->LineLeft(aBStart, aBEnd);
+  }
+
+  // Determine if aBStart is within our bottom corner.
+  if (aBStart >= mLogicalBottomLeftCorner->BStart()) {
+    return mLogicalBottomLeftCorner->LineLeft(aBStart, aBEnd);
+  }
+
+  // Either aBStart or aBEnd or both are within the flat part of our left
+  // edge. Because we've already inflated our mRect to encompass our
+  // mShapeMargin, we can just return the edge.
+  return mRect.X();
 }
 
 nscoord
 nsFloatManager::RoundedBoxShapeInfo::LineRight(const nscoord aBStart,
                                                const nscoord aBEnd) const
 {
-  if (!mRadii) {
-    return mRect.XMost();
+  if (mShapeMargin == 0) {
+    if (!mRadii) {
+      return mRect.XMost();
+    }
+
+    nscoord lineRightDiff =
+      ComputeEllipseLineInterceptDiff(
+        mRect.y, mRect.YMost(),
+        mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
+        mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
+        aBStart, aBEnd);
+    return mRect.XMost() - lineRightDiff;
   }
 
-  nscoord lineRightDiff =
-    ComputeEllipseLineInterceptDiff(
-      mRect.y, mRect.YMost(),
-      mRadii[eCornerTopRightX], mRadii[eCornerTopRightY],
-      mRadii[eCornerBottomRightX], mRadii[eCornerBottomRightY],
-      aBStart, aBEnd);
-  return mRect.XMost() - lineRightDiff;
+  MOZ_ASSERT(mLogicalTopRightCorner && mLogicalBottomRightCorner,
+             "If we have positive shape-margin, we should have corners.");
+
+  // Determine if aBEnd is within our top corner.
+  if (aBEnd < mLogicalTopRightCorner->BEnd()) {
+    return mLogicalTopRightCorner->LineRight(aBStart, aBEnd);
+  }
+
+  // Determine if aBStart is within our bottom corner.
+  if (aBStart >= mLogicalBottomRightCorner->BStart()) {
+    return mLogicalBottomRightCorner->LineRight(aBStart, aBEnd);
+  }
+
+  // Either aBStart or aBEnd or both are within the flat part of our right
+  // edge. Because we've already inflated our mRect to encompass our
+  // mShapeMargin, we can just return the edge.
+  return mRect.XMost();
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // PolygonShapeInfo
 //
 // Implements shape-outside: polygon().
 //
 class nsFloatManager::PolygonShapeInfo final : public nsFloatManager::ShapeInfo
@@ -2136,32 +2238,29 @@ nsFloatManager::ShapeInfo::CreateInset(
     auto logicalRadii = MakeUnique<nscoord[]>(8);
     for (int32_t i = 0; i < 8; ++i) {
       logicalRadii[i] = aShapeMargin;
     }
     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
                                            Move(logicalRadii));
   }
 
-  // If we have radii, and each pair is equal, we can inflate both
-  // logicalInsetRect and all the radii and use the fast constructor.
-  if (physicalRadii[0] == physicalRadii[1] &&
-      physicalRadii[2] == physicalRadii[3] &&
-      physicalRadii[4] == physicalRadii[5] &&
-      physicalRadii[6] == physicalRadii[7]) {
+  // If we have radii, and they have balanced/equal corners, we can inflate
+  // both logicalInsetRect and all the radii and use the fast constructor.
+  if (RoundedBoxShapeInfo::EachCornerHasBalancedRadii(physicalRadii)) {
     logicalInsetRect.Inflate(aShapeMargin);
     for (nscoord& r : physicalRadii) {
       r += aShapeMargin;
     }
     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
                                            ConvertToFloatLogical(physicalRadii,
                                                                  aWM));
   }
 
-  // With positive shape-margin and unequal radii pairs, we have to use the
+  // With positive shape-margin and elliptical radii, we have to use the
   // slow constructor.
   nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
   int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
   return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
                                          ConvertToFloatLogical(physicalRadii,
                                                                aWM),
                                          aShapeMargin, appUnitsPerDevPixel);
 }