Bug 1451499 Part 2: Stub in support for polygon with shape-margin == 0. r=jfkthame
authorBrad Werth <bwerth@mozilla.com>
Thu, 19 Apr 2018 11:27:28 -0700
changeset 472903 516cc3ad8538f315a475c6d4c449bd1822da191f
parent 472902 b1ff16c658636afb61c07765ea3c6470857bfefd
child 472904 126d2e51c09307338de9f095346202ee25b6ddd6
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1451499
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 1451499 Part 2: Stub in support for polygon with shape-margin == 0. r=jfkthame MozReview-Commit-ID: 2biFnthoWin
layout/generic/nsFloatManager.cpp
--- a/layout/generic/nsFloatManager.cpp
+++ b/layout/generic/nsFloatManager.cpp
@@ -558,16 +558,17 @@ public:
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateBasicShape(
     const UniquePtr<StyleBasicShape>& aBasicShape,
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
     const LogicalRect& aShapeBoxRect,
+    const LogicalRect& aMarginRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateInset(
     const UniquePtr<StyleBasicShape>& aBasicShape,
     nscoord aShapeMargin,
     nsIFrame* aFrame,
     const LogicalRect& aShapeBoxRect,
@@ -579,17 +580,20 @@ public:
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
     const LogicalRect& aShapeBoxRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreatePolygon(
     const UniquePtr<StyleBasicShape>& aBasicShape,
+    nscoord aShapeMargin,
+    nsIFrame* const aFrame,
     const LogicalRect& aShapeBoxRect,
+    const LogicalRect& aMarginRect,
     WritingMode aWM,
     const nsSize& aContainerSize);
 
   static UniquePtr<ShapeInfo> CreateImageShape(
     const UniquePtr<nsStyleImage>& aShapeImage,
     float aShapeImageThreshold,
     nscoord aShapeMargin,
     nsIFrame* const aFrame,
@@ -1216,28 +1220,36 @@ nsFloatManager::RoundedBoxShapeInfo::Lin
 // PolygonShapeInfo
 //
 // Implements shape-outside: polygon().
 //
 class nsFloatManager::PolygonShapeInfo final : public nsFloatManager::ShapeInfo
 {
 public:
   explicit PolygonShapeInfo(nsTArray<nsPoint>&& aVertices);
+  PolygonShapeInfo(nsTArray<nsPoint>&& aVertices,
+                   nscoord aShapeMargin,
+                   int32_t aAppUnitsPerDevPixel,
+                   const nsRect& aMarginRect);
 
   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 mBStart; }
   nscoord BEnd() const override { return mBEnd; }
   bool IsEmpty() const override { return mEmpty; }
 
   void Translate(nscoord aLineLeft, nscoord aBlockStart) override;
 
 private:
+  // Helper method for determining if the vertices define a float area at
+  // all, and to set mBStart and mBEnd based on the vertices' y extent.
+  void ComputeEmptinessAndExtent();
+
   // Helper method for implementing LineLeft() and LineRight().
   nscoord ComputeLineIntercept(
     const nscoord aBStart,
     const nscoord aBEnd,
     nscoord (*aCompareOp) (std::initializer_list<nscoord>),
     const nscoord aLineInterceptInitialValue) const;
 
   // Given a horizontal line y, and two points p1 and p2 forming a line
@@ -1245,32 +1257,113 @@ private:
   // assumes y and L do intersect, and L is *not* horizontal.
   static nscoord XInterceptAtY(const nscoord aY,
                                const nsPoint& aP1,
                                const nsPoint& aP2);
 
   // The vertices of the polygon in the float manager's coordinate space.
   nsTArray<nsPoint> mVertices;
 
+  // An interval is slice of the float area defined by this PolygonShapeInfo.
+  // These are only generated and used in float area calculations for
+  // shape-margin > 0. Each interval is a rectangle that is one device pixel
+  // deep in the block axis. The values are stored as block edges in the y
+  // coordinates, and inline edges as the x coordinates.
+
+  // The intervals are stored in ascending order on y.
+  nsTArray<nsRect> mIntervals;
+
   // If mEmpty is true, that means the polygon encloses no area.
   bool mEmpty = false;
 
   // Computed block start and block end value of the polygon shape.
   //
   // If mEmpty is false, their initial values nscoord_MAX and nscoord_MIN
   // are used as sentinels for computing min() and max() in the
   // constructor, and mBStart is guaranteed to be less than or equal to
   // mBEnd. If mEmpty is true, their values do not matter.
   nscoord mBStart = nscoord_MAX;
   nscoord mBEnd = nscoord_MIN;
 };
 
 nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(nsTArray<nsPoint>&& aVertices)
   : mVertices(aVertices)
 {
+  ComputeEmptinessAndExtent();
+}
+
+nsFloatManager::PolygonShapeInfo::PolygonShapeInfo(
+  nsTArray<nsPoint>&& aVertices,
+  nscoord aShapeMargin,
+  int32_t aAppUnitsPerDevPixel,
+  const nsRect& aMarginRect)
+  : mVertices(aVertices)
+{
+  MOZ_ASSERT(aShapeMargin > 0, "This constructor should only be used for a "
+                               "polygon with a positive shape-margin.");
+
+  ComputeEmptinessAndExtent();
+
+  // If we're empty, then the float area stays empty, even with a positive
+  // shape-margin.
+  if (mEmpty) {
+    return;
+  }
+
+  // Adjust our extents by aShapeMargin.
+  mBStart -= aShapeMargin;
+  mBEnd += aShapeMargin;
+
+  NS_ERROR("To be implemented for positive shape-margin.");
+}
+
+nscoord
+nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
+                                           const nscoord aBEnd) const
+{
+  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
+
+  // Use intervals if we have them.
+  if (!mIntervals.IsEmpty()) {
+    return LineEdge(mIntervals, aBStart, aBEnd, true);
+  }
+
+  // We want the line-left-most inline-axis coordinate where the
+  // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
+  // To get that, we start as line-right as possible (at nscoord_MAX). Then
+  // we iterate each line segment to compute its intersection point with the
+  // band (if any) and using std::min() successively to get the smallest
+  // inline-coordinates among those intersection points.
+  //
+  // Note: std::min<nscoord> means the function std::min() with template
+  // parameter nscoord, not the minimum value of nscoord.
+  return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
+}
+
+nscoord
+nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
+                                            const nscoord aBEnd) const
+{
+  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
+
+  // Use intervals if we have them.
+  if (!mIntervals.IsEmpty()) {
+    return LineEdge(mIntervals, aBStart, aBEnd, false);
+  }
+
+  // Similar to LineLeft(). Though here, we want the line-right-most
+  // inline-axis coordinate, so we instead start at nscoord_MIN and use
+  // std::max() to get the biggest inline-coordinate among those
+  // intersection points.
+  return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
+}
+
+void
+nsFloatManager::PolygonShapeInfo::ComputeEmptinessAndExtent()
+{
   // Polygons with fewer than three vertices result in an empty area.
   // https://drafts.csswg.org/css-shapes/#funcdef-polygon
   if (mVertices.Length() < 3) {
     mEmpty = true;
     return;
   }
 
   auto Determinant = [] (const nsPoint& aP0, const nsPoint& aP1) {
@@ -1307,47 +1400,16 @@ nsFloatManager::PolygonShapeInfo::Polygo
   // the float manager's writing mode.
   for (const nsPoint& vertex : mVertices) {
     mBStart = std::min(mBStart, vertex.y);
     mBEnd = std::max(mBEnd, vertex.y);
   }
 }
 
 nscoord
-nsFloatManager::PolygonShapeInfo::LineLeft(const nscoord aBStart,
-                                           const nscoord aBEnd) const
-{
-  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
-
-  // We want the line-left-most inline-axis coordinate where the
-  // (block-axis) aBStart/aBEnd band crosses a line segment of the polygon.
-  // To get that, we start as line-right as possible (at nscoord_MAX). Then
-  // we iterate each line segment to compute its intersection point with the
-  // band (if any) and using std::min() successively to get the smallest
-  // inline-coordinates among those intersection points.
-  //
-  // Note: std::min<nscoord> means the function std::min() with template
-  // parameter nscoord, not the minimum value of nscoord.
-  return ComputeLineIntercept(aBStart, aBEnd, std::min<nscoord>, nscoord_MAX);
-}
-
-nscoord
-nsFloatManager::PolygonShapeInfo::LineRight(const nscoord aBStart,
-                                            const nscoord aBEnd) const
-{
-  MOZ_ASSERT(!mEmpty, "Shouldn't be called if the polygon encloses no area.");
-
-  // Similar to LineLeft(). Though here, we want the line-right-most
-  // inline-axis coordinate, so we instead start at nscoord_MIN and use
-  // std::max() to get the biggest inline-coordinate among those
-  // intersection points.
-  return ComputeLineIntercept(aBStart, aBEnd, std::max<nscoord>, nscoord_MIN);
-}
-
-nscoord
 nsFloatManager::PolygonShapeInfo::ComputeLineIntercept(
   const nscoord aBStart,
   const nscoord aBEnd,
   nscoord (*aCompareOp) (std::initializer_list<nscoord>),
   const nscoord aLineInterceptInitialValue) const
 {
   MOZ_ASSERT(aBStart <= aBEnd,
              "The band's block start is greater than its block end?");
@@ -1396,16 +1458,19 @@ nsFloatManager::PolygonShapeInfo::Comput
 
 void
 nsFloatManager::PolygonShapeInfo::Translate(nscoord aLineLeft,
                                             nscoord aBlockStart)
 {
   for (nsPoint& vertex : mVertices) {
     vertex.MoveBy(aLineLeft, aBlockStart);
   }
+  for (nsRect& interval : mIntervals) {
+    interval.MoveBy(aLineLeft, aBlockStart);
+  }
   mBStart += aBlockStart;
   mBEnd += aBlockStart;
 }
 
 /* static */ nscoord
 nsFloatManager::PolygonShapeInfo::XInterceptAtY(const nscoord aY,
                                                 const nsPoint& aP1,
                                                 const nsPoint& aP2)
@@ -1966,17 +2031,17 @@ nsFloatManager::FloatInfo::FloatInfo(nsI
     }
 
     case StyleShapeSourceType::Shape: {
       const UniquePtr<StyleBasicShape>& basicShape = shapeOutside.GetBasicShape();
       // Initialize <shape-box>'s reference rect.
       LogicalRect shapeBoxRect =
         ShapeInfo::ComputeShapeBoxRect(shapeOutside, mFrame, aMarginRect, aWM);
       mShapeInfo = ShapeInfo::CreateBasicShape(basicShape, shapeMargin, mFrame,
-                                               shapeBoxRect, aWM,
+                                               shapeBoxRect, aMarginRect, aWM,
                                                aContainerSize);
       break;
     }
   }
 
   MOZ_ASSERT(mShapeInfo,
              "All shape-outside values except none should have mShapeInfo!");
 
@@ -2149,22 +2214,24 @@ nsFloatManager::ShapeInfo::CreateShapeBo
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateBasicShape(
   const UniquePtr<StyleBasicShape>& aBasicShape,
   nscoord aShapeMargin,
   nsIFrame* const aFrame,
   const LogicalRect& aShapeBoxRect,
+  const LogicalRect& aMarginRect,
   WritingMode aWM,
   const nsSize& aContainerSize)
 {
   switch (aBasicShape->GetShapeType()) {
     case StyleBasicShapeType::Polygon:
-      return CreatePolygon(aBasicShape, aShapeBoxRect, aWM, aContainerSize);
+      return CreatePolygon(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
+                           aMarginRect, aWM, aContainerSize);
     case StyleBasicShapeType::Circle:
     case StyleBasicShapeType::Ellipse:
       return CreateCircleOrEllipse(aBasicShape, aShapeMargin, aFrame,
                                    aShapeBoxRect, aWM,
                                    aContainerSize);
     case StyleBasicShapeType::Inset:
       return CreateInset(aBasicShape, aShapeMargin, aFrame, aShapeBoxRect,
                          aWM, aContainerSize);
@@ -2297,17 +2364,20 @@ nsFloatManager::ShapeInfo::CreateCircleO
   int32_t appUnitsPerDevPixel = dc->AppUnitsPerDevPixel();
   return MakeUnique<EllipseShapeInfo>(logicalCenter, radii, aShapeMargin,
                                       appUnitsPerDevPixel);
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreatePolygon(
   const UniquePtr<StyleBasicShape>& aBasicShape,
+  nscoord aShapeMargin,
+  nsIFrame* const aFrame,
   const LogicalRect& aShapeBoxRect,
+  const LogicalRect& aMarginRect,
   WritingMode aWM,
   const nsSize& aContainerSize)
 {
   // Use physical coordinates to compute each (xi, yi) vertex because CSS
   // represents them using physical coordinates.
   // https://drafts.csswg.org/css-shapes-1/#funcdef-polygon
   nsRect physicalShapeBoxRect =
     aShapeBoxRect.GetPhysicalRect(aWM, aContainerSize);
@@ -2316,17 +2386,27 @@ nsFloatManager::ShapeInfo::CreatePolygon
   nsTArray<nsPoint> vertices =
     ShapeUtils::ComputePolygonVertices(aBasicShape, physicalShapeBoxRect);
 
   // Convert all the physical vertices to logical.
   for (nsPoint& vertex : vertices) {
     vertex = ConvertToFloatLogical(vertex, aWM, aContainerSize);
   }
 
-  return MakeUnique<PolygonShapeInfo>(Move(vertices));
+  if (aShapeMargin == 0) {
+    return MakeUnique<PolygonShapeInfo>(Move(vertices));
+  }
+
+  nsRect marginRect = ConvertToFloatLogical(aMarginRect, aWM, aContainerSize);
+
+  // We have to use the full constructor for PolygonShapeInfo. This
+  // computes the float area using a rasterization method.
+  int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
+  return MakeUnique<PolygonShapeInfo>(Move(vertices), aShapeMargin,
+                                      appUnitsPerDevPixel, marginRect);
 }
 
 /* static */ UniquePtr<nsFloatManager::ShapeInfo>
 nsFloatManager::ShapeInfo::CreateImageShape(
   const UniquePtr<nsStyleImage>& aShapeImage,
   float aShapeImageThreshold,
   nscoord aShapeMargin,
   nsIFrame* const aFrame,