[CSS Shapes] Improve the performance of image valued shapes with large shape-margins
https://bugs.webkit.org/show_bug.cgi?id=122613

Reviewed by Andreas Kling.

The cost of computing the shape-margin boundary of an image-valued shape-outside
is now proportional to (2 * shape-margin + image.height) rather than
(2 * shape-margin * image.height). The performance improvement comes from skipping
sequences of rounded-rectangle intervals that will not contribute to the final
result. Each non-empty row in the original image contributes one rounded-rectangle
whose corner radius is shape-margin, height is 2 * shape-margin, and width is
2 * shape-margin plus the width of the limits of the intervals on the row.

Renamed private method RasterShape::getIntervals() to intervalsAt() to be a little
more consistent with WebKit naming conventions.

There are no new tests since is just an internal refactoring.

* rendering/shapes/RasterShape.cpp:
(WebCore::MarginIntervalGenerator::set): Changed the x1,x2 parameters to an IntShapeInterval.
(WebCore::RasterShapeIntervals::contains): Refactor for the getIntervals() => intervalsAt() rename.
(WebCore::RasterShapeIntervals::getIntervalX1Values): Ditto.
(WebCore::RasterShapeIntervals::getIncludedIntervals): Ditto.
(WebCore::RasterShapeIntervals::getExcludedIntervals): Ditto.
(WebCore::RasterShapeIntervals::computeShapeMarginIntervals): Performance tuning.
* rendering/shapes/RasterShape.h:
(WebCore::RasterShapeIntervals::intervalsAt): Renamed getIntervals().
(WebCore::RasterShapeIntervals::limitIntervalAt): Return the min/max limits of the intervals at Y.
* rendering/shapes/ShapeInterval.h:
(WebCore::ShapeInterval::isEmpty): Added.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@157574 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index a85bada..47ca430 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,36 @@
+2013-10-17  Hans Muller  <hmuller@adobe.com>
+
+        [CSS Shapes] Improve the performance of image valued shapes with large shape-margins
+        https://bugs.webkit.org/show_bug.cgi?id=122613
+
+        Reviewed by Andreas Kling.
+
+        The cost of computing the shape-margin boundary of an image-valued shape-outside
+        is now proportional to (2 * shape-margin + image.height) rather than
+        (2 * shape-margin * image.height). The performance improvement comes from skipping
+        sequences of rounded-rectangle intervals that will not contribute to the final
+        result. Each non-empty row in the original image contributes one rounded-rectangle
+        whose corner radius is shape-margin, height is 2 * shape-margin, and width is
+        2 * shape-margin plus the width of the limits of the intervals on the row.
+
+        Renamed private method RasterShape::getIntervals() to intervalsAt() to be a little
+        more consistent with WebKit naming conventions.
+
+        There are no new tests since is just an internal refactoring.
+
+        * rendering/shapes/RasterShape.cpp:
+        (WebCore::MarginIntervalGenerator::set): Changed the x1,x2 parameters to an IntShapeInterval.
+        (WebCore::RasterShapeIntervals::contains): Refactor for the getIntervals() => intervalsAt() rename.
+        (WebCore::RasterShapeIntervals::getIntervalX1Values): Ditto.
+        (WebCore::RasterShapeIntervals::getIncludedIntervals): Ditto.
+        (WebCore::RasterShapeIntervals::getExcludedIntervals): Ditto.
+        (WebCore::RasterShapeIntervals::computeShapeMarginIntervals): Performance tuning.
+        * rendering/shapes/RasterShape.h:
+        (WebCore::RasterShapeIntervals::intervalsAt): Renamed getIntervals().
+        (WebCore::RasterShapeIntervals::limitIntervalAt): Return the min/max limits of the intervals at Y.
+        * rendering/shapes/ShapeInterval.h:
+        (WebCore::ShapeInterval::isEmpty): Added.
+
 2013-10-15  Philippe Normand  <pnormand@igalia.com>
 
         [GTK] Add URLMediaStream in the build
diff --git a/Source/WebCore/rendering/shapes/RasterShape.cpp b/Source/WebCore/rendering/shapes/RasterShape.cpp
index dbeda00..3aa065d 100644
--- a/Source/WebCore/rendering/shapes/RasterShape.cpp
+++ b/Source/WebCore/rendering/shapes/RasterShape.cpp
@@ -37,7 +37,7 @@
 class MarginIntervalGenerator {
 public:
     MarginIntervalGenerator(unsigned radius);
-    void set(int y, int x1, int x2);
+    void set(int y, const IntShapeInterval&);
     IntShapeInterval intervalAt(int y) const;
 
 private:
@@ -58,12 +58,12 @@
         m_xIntercepts[y] = sqrt(static_cast<double>(radiusSquared - y * y));
 }
 
-void MarginIntervalGenerator::set(int y, int x1, int x2)
+void MarginIntervalGenerator::set(int y, const IntShapeInterval& interval)
 {
-    ASSERT(y >= 0 && x1 >= 0 && x2 > x1);
+    ASSERT(y >= 0 && interval.x1() >= 0);
     m_y = y;
-    m_x1 = x1;
-    m_x2 = x2;
+    m_x1 = interval.x1();
+    m_x2 = interval.x2();
 }
 
 IntShapeInterval MarginIntervalGenerator::intervalAt(int y) const
@@ -114,7 +114,7 @@
 
     const IntShapeInterval& rectInterval = IntShapeInterval(rect.x(), rect.maxX());
     for (int y = rect.y(); y < rect.maxY(); y++) {
-        if (!shapeIntervalsContain(getIntervals(y), rectInterval))
+        if (!shapeIntervalsContain(intervalsAt(y), rectInterval))
             return false;
     }
 
@@ -133,14 +133,14 @@
     ASSERT(y1 >= 0 && y2 > y1);
 
     for (int y = y1; y < y2;  y++) {
-        if (getIntervals(y).isEmpty())
+        if (intervalsAt(y).isEmpty())
             return false;
     }
 
-    appendX1Values(getIntervals(y1), minIntervalWidth, result);
+    appendX1Values(intervalsAt(y1), minIntervalWidth, result);
     for (int y = y1 + 1; y < y2;  y++) {
-        if (getIntervals(y) != getIntervals(y - 1))
-            appendX1Values(getIntervals(y), minIntervalWidth, result);
+        if (intervalsAt(y) != intervalsAt(y - 1))
+            appendX1Values(intervalsAt(y), minIntervalWidth, result);
     }
 
     return true;
@@ -186,14 +186,14 @@
         return;
 
     for (int y = y1; y < y2;  y++) {
-        if (getIntervals(y).isEmpty())
+        if (intervalsAt(y).isEmpty())
             return;
     }
 
-    result = getIntervals(y1);
+    result = intervalsAt(y1);
     for (int y = y1 + 1; y < y2 && !result.isEmpty();  y++) {
         IntShapeIntervals intervals;
-        IntShapeInterval::intersectShapeIntervals(result, getIntervals(y), intervals);
+        IntShapeInterval::intersectShapeIntervals(result, intervalsAt(y), intervals);
         result.swap(intervals);
     }
 }
@@ -206,33 +206,47 @@
         return;
 
     for (int y = y1; y < y2;  y++) {
-        if (getIntervals(y).isEmpty())
+        if (intervalsAt(y).isEmpty())
             return;
     }
 
-    result = getIntervals(y1);
+    result = intervalsAt(y1);
     for (int y = y1 + 1; y < y2;  y++) {
         IntShapeIntervals intervals;
-        IntShapeInterval::uniteShapeIntervals(result, getIntervals(y), intervals);
+        IntShapeInterval::uniteShapeIntervals(result, intervalsAt(y), intervals);
         result.swap(intervals);
     }
 }
 
 // Currently limited to computing the margin boundary for shape-outside for floats, see https://bugs.webkit.org/show_bug.cgi?id=116348.
+
 PassOwnPtr<RasterShapeIntervals> RasterShapeIntervals::computeShapeMarginIntervals(unsigned margin) const
 {
     OwnPtr<RasterShapeIntervals> result = adoptPtr(new RasterShapeIntervals(size() + margin));
-    MarginIntervalGenerator intervalGenerator(margin);
+    MarginIntervalGenerator marginIntervalGenerator(margin);
 
     for (int y = bounds().y(); y < bounds().maxY(); ++y) {
-        const IntShapeIntervals& intervalsAtY = getIntervals(y);
-        if (intervalsAtY.isEmpty())
+        const IntShapeInterval& intervalAtY = limitIntervalAt(y);
+        if (intervalAtY.isEmpty())
             continue;
+
+        marginIntervalGenerator.set(y, intervalAtY);
         int marginY0 = std::max(0, clampToPositiveInteger(y - margin));
         int marginY1 = std::min(result->size() - 1, clampToPositiveInteger(y + margin));
-        intervalGenerator.set(y, intervalsAtY[0].x1(), intervalsAtY.last().x2());
-        for (int marginY = marginY0; marginY <= marginY1; ++marginY)
-            result->uniteMarginInterval(marginY, intervalGenerator.intervalAt(marginY));
+
+        for (int marginY = y - 1; marginY >= marginY0; --marginY) {
+            if (limitIntervalAt(marginY).contains(intervalAtY))
+                break;
+            result->uniteMarginInterval(marginY, marginIntervalGenerator.intervalAt(marginY));
+        }
+
+        result->uniteMarginInterval(y, marginIntervalGenerator.intervalAt(y));
+
+        for (int marginY = y + 1; marginY <= marginY1; ++marginY) {
+            if (marginY < size() && limitIntervalAt(marginY).contains(intervalAtY))
+                break;
+            result->uniteMarginInterval(marginY, marginIntervalGenerator.intervalAt(marginY));
+        }
     }
 
     return result.release();
diff --git a/Source/WebCore/rendering/shapes/RasterShape.h b/Source/WebCore/rendering/shapes/RasterShape.h
index aa5b8ab..e448949 100644
--- a/Source/WebCore/rendering/shapes/RasterShape.h
+++ b/Source/WebCore/rendering/shapes/RasterShape.h
@@ -57,12 +57,18 @@
 private:
     int size() const { return m_intervalLists.size(); }
 
-    const IntShapeIntervals& getIntervals(int y) const
+    const IntShapeIntervals& intervalsAt(int y) const
     {
         ASSERT(y >= 0 && y < size());
         return m_intervalLists[y];
     }
 
+    IntShapeInterval limitIntervalAt(int y) const
+    {
+        const IntShapeIntervals& intervals = intervalsAt(y);
+        return intervals.size() ? IntShapeInterval(intervals[0].x1(), intervals.last().x2()) : IntShapeInterval();
+    }
+
     bool contains(const IntRect&) const;
     bool getIntervalX1Values(int minY, int maxY, int minIntervalWidth, Vector<int>& result) const;
     void uniteMarginInterval(int y, const IntShapeInterval&);
diff --git a/Source/WebCore/rendering/shapes/ShapeInterval.h b/Source/WebCore/rendering/shapes/ShapeInterval.h
index 84a5a9a..9efa0fe 100644
--- a/Source/WebCore/rendering/shapes/ShapeInterval.h
+++ b/Source/WebCore/rendering/shapes/ShapeInterval.h
@@ -48,6 +48,7 @@
     T x1() const { return m_x1; }
     T x2() const { return m_x2; }
     T width() const { return m_x2 - m_x1; }
+    bool isEmpty() const { return m_x1 == m_x2; }
 
     void setX1(T x1)
     {