Bug 1265342 Part 6a: Implement shape-margin for shape-outside: inset, for some special cases with shape-margin > 0. draft
authorBrad Werth <bwerth@mozilla.com>
Fri, 13 Apr 2018 11:29:14 -0700
changeset 787954 70a84de16cc8a89ffa4256dc6485d24df3f28afe
parent 787953 c898ec3dddef1c061a7b5f79714f085f10726e06
child 787955 cd2ea0ddb67a5d4efcbbca0aa83544c82631ed3e
push id107854
push userbwerth@mozilla.com
push dateWed, 25 Apr 2018 18:14:35 +0000
bugs1265342
milestone61.0a1
Bug 1265342 Part 6a: Implement shape-margin for shape-outside: inset, for some special cases with shape-margin > 0. MozReview-Commit-ID: AEOksiuM0GJ
layout/generic/nsFloatManager.cpp
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -562,16 +562,18 @@ public:
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
     const LogicalRect& aShapeBoxRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateInset(
     const UniquePtr<StyleBasicShape>& aBasicShape,
+    nscoord aShapeMargin,
+    nsIFrame* aFrame,
     const LogicalRect& aShapeBoxRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateCircleOrEllipse(
     const UniquePtr<StyleBasicShape>& aBasicShape,
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
@@ -1009,41 +1011,61 @@ nsFloatManager::EllipseShapeInfo::LineRi
 //
 class nsFloatManager::RoundedBoxShapeInfo final : public nsFloatManager::ShapeInfo
 {
 public:
   RoundedBoxShapeInfo(const nsRect& aRect,
                       UniquePtr<nscoord[]> aRadii)
     : mRect(aRect)
     , mRadii(Move(aRadii))
+    , mShapeMargin(0)
   {}
 
+  RoundedBoxShapeInfo(const nsRect& aRect,
+                      UniquePtr<nscoord[]> aRadii,
+                      nscoord aShapeMargin,
+                      int32_t aAppUnitsPerDevPixel);
+
   nscoord LineLeft(const nscoord aBStart,
                    const nscoord aBEnd) const override;
   nscoord LineRight(const nscoord aBStart,
                     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(); };
+  bool IsEmpty() const override { return mRect.IsEmpty(); }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override
   {
     mRect.MoveBy(aLineLeft, aBlockStart);
   }
 
 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.
-  UniquePtr<nscoord[]> mRadii;
+  const UniquePtr<nscoord[]> mRadii;
+
+  // A shape-margin value extends the boundaries of the float area.
+  const nscoord mShapeMargin;
 };
 
+nsFloatManager::RoundedBoxShapeInfo::RoundedBoxShapeInfo(const nsRect& aRect,
+  UniquePtr<nscoord[]> aRadii,
+  nscoord aShapeMargin,
+  int32_t aAppUnitsPerDevPixel)
+  : mRect(aRect)
+  , mRadii(Move(aRadii))
+  , mShapeMargin(aShapeMargin)
+{
+
+}
+
 nscoord
 nsFloatManager::RoundedBoxShapeInfo::LineLeft(const nscoord aBStart,
                                               const nscoord aBEnd) const
 {
   if (!mRadii) {
     return mRect.x;
   }
 
@@ -2058,24 +2080,27 @@ nsFloatManager::ShapeInfo::CreateBasicSh
     case StyleBasicShapeType::Polygon:
       return CreatePolygon(aBasicShape, aShapeBoxRect, aWM, aContainerSize);
     case StyleBasicShapeType::Circle:
     case StyleBasicShapeType::Ellipse:
       return CreateCircleOrEllipse(aBasicShape, aShapeMargin, aFrame,
                                    aShapeBoxRect, aWM,
                                    aContainerSize);
     case StyleBasicShapeType::Inset:
-      return CreateInset(aBasicShape, aShapeBoxRect, aWM, aContainerSize);
+      return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
+                         aWM, aContainerSize);
   }
   return nullptr;
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateInset(
   const UniquePtr<StyleBasicShape>& aBasicShape,
+  nscoord aShapeMargin,
+  nsIFrame* aFrame,
   const LogicalRect& aShapeBoxRect,
   WritingMode aWM,
   const nsSize& aContainerSize)
 {
   // Use physical coordinates to compute inset() because the top, right,
   // bottom and left offsets are physical.
   // https://drafts.csswg.org/css-shapes-1/#funcdef-inset
   nsRect physicalShapeBoxRect =
@@ -2085,24 +2110,65 @@ nsFloatManager::ShapeInfo::CreateInset(
 
   nsRect logicalInsetRect =
     ConvertToFloatLogical(LogicalRect(aWM, insetRect, aContainerSize),
                           aWM, aContainerSize);
   nscoord physicalRadii[8];
   bool hasRadii =
     ShapeUtils::ComputeInsetRadii(aBasicShape, insetRect, physicalShapeBoxRect,
                                   physicalRadii);
-  if (!hasRadii) {
+
+  // With a zero shape-margin, we will be able to use the fast constructor.
+  if (aShapeMargin == 0) {
+    if (!hasRadii) {
+      return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
+                                             UniquePtr<nscoord[]>());
+    }
     return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
-                                           UniquePtr<nscoord[]>());
+                                           ConvertToFloatLogical(physicalRadii,
+                                                                 aWM));
   }
 
+  // With a positive shape-margin, we might still be able to use the fast
+  // constructor. With no radii, we can build a rounded box by inflating
+  // logicalInsetRect, and supplying aShapeMargin as the radius for all
+  // corners.
+  if (!hasRadii) {
+    logicalInsetRect.Inflate(aShapeMargin);
+    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]) {
+    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
+  // slow constructor.
+  nsDeviceContext* dc = aFrame->PresContext()->DeviceContext();
+  int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
   return MakeUnique<RoundedBoxShapeInfo>(logicalInsetRect,
                                          ConvertToFloatLogical(physicalRadii,
-                                                               aWM));
+                                                               aWM),
+                                         aShapeMargin, appUnitsPerDevPixel);
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateCircleOrEllipse(
   const UniquePtr<StyleBasicShape>& aBasicShape,
   nscoord aShapeMargin,
   nsIFrame* const aFrame,
   const LogicalRect& aShapeBoxRect,