Changeset 147111 in webkit


Ignore:
Timestamp:
Mar 28, 2013 7:20:13 AM (11 years ago)
Author:
hmuller@adobe.com
Message:

[CSS Exclusions] Add support for the simple case of padding a polygonal shape-inside
https://bugs.webkit.org/show_bug.cgi?id=112592

Reviewed by Dirk Schulze.

Source/WebCore:

First pass at computing the padded or inset boundary of a polygon. This version does not handle
self-intersecting polygons, or values of shape-padding large enough to change the shape of the
original polygon. The implementation computes an offset edge for each polgon edge, where the offset
edge is parallel to the original edge and separated by shape-padding. The padded polygon's vertices
are the intersections of each pair of adjacent offset edges. When adjacent offset edges do not intersect,
because they share a reflex vertex in the original polygon, an approximation to a circular arc
connects the offset edges.

Tests: fast/exclusions/shape-inside/shape-inside-polygon-padding-001.html

fast/exclusions/shape-inside/shape-inside-polygon-padding-002.html
fast/exclusions/shape-inside/shape-inside-polygon-padding-003.html

  • rendering/ExclusionPolygon.cpp:

(WebCore::isReflexVertex): Just moved this function earlier in the file.
(WebCore::inwardEdgeNormal): Unit vector that's perpindicular to the edge and that points inwards.
(WebCore::outwardEdgeNormal): Unit vector that's perpindicular to the edge and that points outwards.
(WebCore::appendArc): Append a linear approximation to a circular arc to a vector of vertices.
(WebCore::computeShapePaddingBounds): Return a polygon whose edges are all inset by shape-padding from m_polygon.
(WebCore::computeShapeMarginBounds): Just a stub, see bug 112917.
(WebCore::ExclusionPolygon::shapePaddingBounds): Lazily use computeShapePaddingBounds() to initialize m_paddingBounds.
(WebCore::ExclusionPolygon::shapeMarginBounds): Lazily use computeShapeMarginBounds() to initialize m_marginBounds.
(WebCore::ExclusionPolygon::getIncludedIntervals): Now based on the value of shapePaddingBounds().
(WebCore::ExclusionPolygon::firstIncludedIntervalLogicalTop): Now based on the value of shapePaddingBounds().

  • rendering/ExclusionPolygon.h:

(WebCore::ExclusionPolygon::ExclusionPolygon):

LayoutTests:

Verify that the CSS shape-padding property works correctly for some simple polygonal shapes:
a rectangle, a rectangle rotated 45 degress, and rectangular shape with a single reflex vertex.

  • fast/exclusions/shape-inside/shape-inside-polygon-padding-001-expected.html: Added.
  • fast/exclusions/shape-inside/shape-inside-polygon-padding-001.html: Added.
  • fast/exclusions/shape-inside/shape-inside-polygon-padding-002-expected.html: Added.
  • fast/exclusions/shape-inside/shape-inside-polygon-padding-002.html: Added.
  • fast/exclusions/shape-inside/shape-inside-polygon-padding-003-expected.html: Added.
  • fast/exclusions/shape-inside/shape-inside-polygon-padding-003.html: Added.
Location:
trunk
Files:
6 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r147102 r147111  
     12013-03-28  Hans Muller  <hmuller@adobe.com>
     2
     3        [CSS Exclusions] Add support for the simple case of padding a polygonal shape-inside
     4        https://bugs.webkit.org/show_bug.cgi?id=112592
     5
     6        Reviewed by Dirk Schulze.
     7
     8        Verify that the CSS shape-padding property works correctly for some simple polygonal shapes:
     9        a rectangle, a rectangle rotated 45 degress, and rectangular shape with a single reflex vertex.
     10
     11        * fast/exclusions/shape-inside/shape-inside-polygon-padding-001-expected.html: Added.
     12        * fast/exclusions/shape-inside/shape-inside-polygon-padding-001.html: Added.
     13        * fast/exclusions/shape-inside/shape-inside-polygon-padding-002-expected.html: Added.
     14        * fast/exclusions/shape-inside/shape-inside-polygon-padding-002.html: Added.
     15        * fast/exclusions/shape-inside/shape-inside-polygon-padding-003-expected.html: Added.
     16        * fast/exclusions/shape-inside/shape-inside-polygon-padding-003.html: Added.
     17
    1182013-03-28  Vsevolod Vlasov  <vsevik@chromium.org>
    219
  • trunk/Source/WebCore/ChangeLog

    r147110 r147111  
     12013-03-28  Hans Muller  <hmuller@adobe.com>
     2
     3        [CSS Exclusions] Add support for the simple case of padding a polygonal shape-inside
     4        https://bugs.webkit.org/show_bug.cgi?id=112592
     5
     6        Reviewed by Dirk Schulze.
     7
     8        First pass at computing the padded or inset boundary of a polygon.  This version does not handle
     9        self-intersecting polygons, or values of shape-padding large enough to change the shape of the
     10        original polygon. The implementation computes an offset edge for each polgon edge, where the offset
     11        edge is parallel to the original edge and separated by shape-padding. The padded polygon's vertices
     12        are the intersections of each pair of adjacent offset edges. When adjacent offset edges do not intersect,
     13        because they share a reflex vertex in the original polygon, an approximation to a circular arc
     14        connects the offset edges.
     15
     16        Tests: fast/exclusions/shape-inside/shape-inside-polygon-padding-001.html
     17               fast/exclusions/shape-inside/shape-inside-polygon-padding-002.html
     18               fast/exclusions/shape-inside/shape-inside-polygon-padding-003.html
     19
     20        * rendering/ExclusionPolygon.cpp:
     21        (WebCore::isReflexVertex): Just moved this function earlier in the file.
     22        (WebCore::inwardEdgeNormal): Unit vector that's perpindicular to the edge and that points inwards.
     23        (WebCore::outwardEdgeNormal): Unit vector that's perpindicular to the edge and that points outwards.
     24        (WebCore::appendArc): Append a linear approximation to a circular arc to a vector of vertices.
     25        (WebCore::computeShapePaddingBounds): Return a polygon whose edges are all inset by shape-padding from m_polygon.
     26        (WebCore::computeShapeMarginBounds): Just a stub, see bug 112917.
     27        (WebCore::ExclusionPolygon::shapePaddingBounds): Lazily use computeShapePaddingBounds() to initialize m_paddingBounds.
     28        (WebCore::ExclusionPolygon::shapeMarginBounds): Lazily use computeShapeMarginBounds() to initialize m_marginBounds.
     29        (WebCore::ExclusionPolygon::getIncludedIntervals): Now based on the value of shapePaddingBounds().
     30        (WebCore::ExclusionPolygon::firstIncludedIntervalLogicalTop): Now based on the value of shapePaddingBounds().
     31        * rendering/ExclusionPolygon.h:
     32        (WebCore::ExclusionPolygon::ExclusionPolygon):
     33
    1342013-03-28  Chris Hutten-Czapski  <chutten@blackberry.com>
    235
  • trunk/Source/WebCore/rendering/ExclusionPolygon.cpp

    r145411 r147111  
    5353}
    5454
     55static inline bool isReflexVertex(const FloatPoint& prevVertex, const FloatPoint& vertex, const FloatPoint& nextVertex)
     56{
     57    return leftSide(prevVertex, nextVertex, vertex) < 0;
     58}
     59
    5560static bool computeXIntersection(const FloatPolygonEdge* edgePointer, float y, EdgeIntersection& result)
    5661{
     
    8691
    8792    return true;
     93}
     94
     95static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge)
     96{
     97    FloatSize edgeDelta = edge.vertex2() - edge.vertex1();
     98    if (!edgeDelta.width())
     99        return FloatSize((edgeDelta.height() > 0 ? -1 : 1), 0);
     100    if (!edgeDelta.height())
     101        return FloatSize(0, (edgeDelta.width() > 0 ? 1 : -1));
     102    float edgeLength = edgeDelta.diagonalLength();
     103    return FloatSize(-edgeDelta.height() / edgeLength, edgeDelta.width() / edgeLength);
     104}
     105
     106static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge)
     107{
     108    return -inwardEdgeNormal(edge);
     109}
     110
     111static inline void appendArc(Vector<FloatPoint>& vertices, const FloatPoint& arcCenter, float arcRadius, const FloatPoint& startArcVertex, const FloatPoint& endArcVertex)
     112{
     113    float startAngle = atan2(startArcVertex.y() - arcCenter.y(), startArcVertex.x() - arcCenter.x());
     114    float endAngle = atan2(endArcVertex.y() - arcCenter.y(), endArcVertex.x() - arcCenter.x());
     115    if (startAngle < 0)
     116        startAngle += piFloat * 2;
     117    if (endAngle < 0)
     118        endAngle += piFloat * 2;
     119    const float arcSegmentCount = 5; // An odd number so that one arc vertex will be eactly arcRadius from arcCenter.
     120    float angle5 = ((startAngle > endAngle) ? (startAngle - endAngle) : (startAngle + piFloat * 2 - endAngle)) / arcSegmentCount;
     121
     122    vertices.append(startArcVertex);
     123    for (unsigned i = 1; i < arcSegmentCount; ++i) {
     124        float angle = startAngle - angle5 * i;
     125        vertices.append(arcCenter + FloatPoint(cos(angle) * arcRadius, sin(angle) * arcRadius));
     126    }
     127    vertices.append(endArcVertex);
     128}
     129
     130static inline FloatPolygon *computeShapePaddingBounds(const FloatPolygon& polygon, float padding, WindRule fillRule)
     131{
     132    Vector<FloatPoint>* paddedVertices = new Vector<FloatPoint>();
     133    FloatPoint intersection;
     134
     135    for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) {
     136        const FloatPolygonEdge& thisEdge = polygon.edgeAt(i);
     137        const FloatPolygonEdge& prevEdge = thisEdge.previousEdge();
     138        OffsetPolygonEdge thisOffsetEdge(thisEdge, inwardEdgeNormal(thisEdge) * padding);
     139        OffsetPolygonEdge prevOffsetEdge(prevEdge, inwardEdgeNormal(prevEdge) * padding);
     140
     141        if (prevOffsetEdge.intersection(thisOffsetEdge, intersection))
     142            paddedVertices->append(intersection);
     143        else if (isReflexVertex(prevEdge.vertex1(), thisEdge.vertex1(), thisEdge.vertex2()))
     144            appendArc(*paddedVertices, thisEdge.vertex1(), padding, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1());
     145    }
     146
     147    return new FloatPolygon(adoptPtr(paddedVertices), fillRule);
     148}
     149
     150// FIXME: this is just a stub (bug 112917)
     151static inline FloatPolygon *computeShapeMarginBounds(const FloatPolygon& polygon, float margin, WindRule fillRule)
     152{
     153    UNUSED_PARAM(margin);
     154
     155    Vector<FloatPoint>* marginVertices = new Vector<FloatPoint>(polygon.numberOfVertices());
     156    for (unsigned i = 0; i < polygon.numberOfVertices(); ++i)
     157        (*marginVertices)[i] = polygon.vertexAt(i);
     158    return new FloatPolygon(adoptPtr(marginVertices), fillRule);
     159}
     160
     161const FloatPolygon& ExclusionPolygon::shapePaddingBounds() const
     162{
     163    ASSERT(shapePadding() >= 0);
     164    if (!shapePadding())
     165        return m_polygon;
     166
     167    if (!m_paddingBounds)
     168        m_paddingBounds = adoptPtr(computeShapePaddingBounds(m_polygon, shapePadding(), m_polygon.fillRule()));
     169
     170    return *m_paddingBounds;
     171}
     172
     173const FloatPolygon& ExclusionPolygon::shapeMarginBounds() const
     174{
     175    ASSERT(shapeMargin() >= 0);
     176    if (!shapeMargin())
     177        return m_polygon;
     178
     179    if (!m_marginBounds)
     180        m_marginBounds = adoptPtr(computeShapeMarginBounds(m_polygon, shapeMargin(), m_polygon.fillRule()));
     181
     182    return *m_marginBounds;
    88183}
    89184
     
    261356void ExclusionPolygon::getIncludedIntervals(float logicalTop, float logicalHeight, SegmentList& result) const
    262357{
    263     if (isEmpty())
     358    const FloatPolygon& polygon = shapePaddingBounds();
     359    if (polygon.isEmpty())
    264360        return;
    265361
     
    268364
    269365    Vector<ExclusionInterval> y1XIntervals, y2XIntervals;
    270     computeXIntersections(m_polygon, y1, true, y1XIntervals);
    271     computeXIntersections(m_polygon, y2, false, y2XIntervals);
     366    computeXIntersections(polygon, y1, true, y1XIntervals);
     367    computeXIntersections(polygon, y2, false, y2XIntervals);
    272368
    273369    Vector<ExclusionInterval> commonIntervals;
     
    275371
    276372    Vector<ExclusionInterval> edgeIntervals;
    277     computeOverlappingEdgeXProjections(m_polygon, y1, y2, edgeIntervals);
     373    computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals);
    278374
    279375    Vector<ExclusionInterval> includedIntervals;
     
    284380        result.append(LineSegment(interval.x1, interval.x2));
    285381    }
    286 }
    287 
    288 static inline bool isReflexVertex(const FloatPoint& prevVertex, const FloatPoint& vertex, const FloatPoint& nextVertex)
    289 {
    290     return leftSide(prevVertex, nextVertex, vertex) < 0;
    291382}
    292383
     
    317408bool ExclusionPolygon::firstIncludedIntervalLogicalTop(float minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, float& result) const
    318409{
    319     const FloatRect boundingBox = m_polygon.boundingBox();
     410    const FloatPolygon& polygon = shapePaddingBounds();
     411    const FloatRect boundingBox = polygon.boundingBox();
    320412    if (minLogicalIntervalSize.width() > boundingBox.width())
    321413        return false;
     
    328420
    329421    Vector<const FloatPolygonEdge*> edges;
    330     m_polygon.overlappingEdges(minLogicalIntervalTop, boundingBox.maxY(), edges);
     422    polygon.overlappingEdges(minLogicalIntervalTop, boundingBox.maxY(), edges);
    331423
    332424    float dx = minLogicalIntervalSize.width() / 2;
     
    365457    }
    366458
    367     offsetEdges.append(OffsetPolygonEdge(m_polygon, minLogicalIntervalTop, FloatSize(0, dy)));
     459    offsetEdges.append(OffsetPolygonEdge(polygon, minLogicalIntervalTop, FloatSize(0, dy)));
    368460
    369461    FloatPoint offsetEdgesIntersection;
     
    378470                if ((potentialFirstFitLocation.y() >= minLogicalIntervalTop)
    379471                    && (!firstFitFound || aboveOrToTheLeft(potentialFirstFitRect, firstFitRect))
    380                     && m_polygon.contains(offsetEdgesIntersection)
    381                     && firstFitRectInPolygon(m_polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) {
     472                    && polygon.contains(offsetEdgesIntersection)
     473                    && firstFitRectInPolygon(polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) {
    382474                    firstFitFound = true;
    383475                    firstFitRect = potentialFirstFitRect;
  • trunk/Source/WebCore/rendering/ExclusionPolygon.h

    r145411 r147111  
    7676        : ExclusionShape()
    7777        , m_polygon(vertices, fillRule)
     78        , m_marginBounds(nullptr)
     79        , m_paddingBounds(nullptr)
    7880    {
    7981    }
     
    8587    virtual bool firstIncludedIntervalLogicalTop(float minLogicalIntervalTop, const FloatSize& minLogicalIntervalSize, float&) const OVERRIDE;
    8688
     89    const FloatPolygon& shapeMarginBounds() const;
     90    const FloatPolygon& shapePaddingBounds() const;
     91
    8792private:
    8893    FloatPolygon m_polygon;
     94    mutable OwnPtr<FloatPolygon> m_marginBounds;
     95    mutable OwnPtr<FloatPolygon> m_paddingBounds;
    8996};
    9097
Note: See TracChangeset for help on using the changeset viewer.