| Index: Source/core/rendering/shapes/PolygonShape.cpp
|
| diff --git a/Source/core/rendering/shapes/PolygonShape.cpp b/Source/core/rendering/shapes/PolygonShape.cpp
|
| index 2513ed525b832c33b59261c9571afc57eb7a8134..15fd9bd451b0b58abc4f7dfb467e7225cb9827b2 100644
|
| --- a/Source/core/rendering/shapes/PolygonShape.cpp
|
| +++ b/Source/core/rendering/shapes/PolygonShape.cpp
|
| @@ -30,60 +30,11 @@
|
| #include "config.h"
|
| #include "core/rendering/shapes/PolygonShape.h"
|
|
|
| -#include "core/rendering/shapes/ShapeInterval.h"
|
| #include "platform/geometry/LayoutPoint.h"
|
| #include "wtf/MathExtras.h"
|
|
|
| namespace WebCore {
|
|
|
| -enum EdgeIntersectionType {
|
| - Normal,
|
| - VertexMinY,
|
| - VertexMaxY,
|
| - VertexYBoth
|
| -};
|
| -
|
| -struct EdgeIntersection {
|
| - const FloatPolygonEdge* edge;
|
| - FloatPoint point;
|
| - EdgeIntersectionType type;
|
| -};
|
| -
|
| -static bool computeXIntersection(const FloatPolygonEdge* edgePointer, float y, EdgeIntersection& result)
|
| -{
|
| - const FloatPolygonEdge& edge = *edgePointer;
|
| -
|
| - if (edge.minY() > y || edge.maxY() < y)
|
| - return false;
|
| -
|
| - const FloatPoint& vertex1 = edge.vertex1();
|
| - const FloatPoint& vertex2 = edge.vertex2();
|
| - float dy = vertex2.y() - vertex1.y();
|
| -
|
| - float intersectionX;
|
| - EdgeIntersectionType intersectionType;
|
| -
|
| - if (!dy) {
|
| - intersectionType = VertexYBoth;
|
| - intersectionX = edge.minX();
|
| - } else if (y == edge.minY()) {
|
| - intersectionType = VertexMinY;
|
| - intersectionX = (vertex1.y() < vertex2.y()) ? vertex1.x() : vertex2.x();
|
| - } else if (y == edge.maxY()) {
|
| - intersectionType = VertexMaxY;
|
| - intersectionX = (vertex1.y() > vertex2.y()) ? vertex1.x() : vertex2.x();
|
| - } else {
|
| - intersectionType = Normal;
|
| - intersectionX = ((y - vertex1.y()) * (vertex2.x() - vertex1.x()) / dy) + vertex1.x();
|
| - }
|
| -
|
| - result.edge = edgePointer;
|
| - result.type = intersectionType;
|
| - result.point.set(intersectionX, y);
|
| -
|
| - return true;
|
| -}
|
| -
|
| static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge)
|
| {
|
| FloatSize edgeDelta = edge.vertex2() - edge.vertex1();
|
| @@ -100,251 +51,101 @@ static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge)
|
| return -inwardEdgeNormal(edge);
|
| }
|
|
|
| -static inline void appendArc(Vector<FloatPoint>& vertices, const FloatPoint& arcCenter, float arcRadius, const FloatPoint& startArcVertex, const FloatPoint& endArcVertex, bool padding)
|
| -{
|
| - float startAngle = atan2(startArcVertex.y() - arcCenter.y(), startArcVertex.x() - arcCenter.x());
|
| - float endAngle = atan2(endArcVertex.y() - arcCenter.y(), endArcVertex.x() - arcCenter.x());
|
| - if (startAngle < 0)
|
| - startAngle += twoPiFloat;
|
| - if (endAngle < 0)
|
| - endAngle += twoPiFloat;
|
| - float angle = (startAngle > endAngle) ? (startAngle - endAngle) : (startAngle + twoPiFloat - endAngle);
|
| - const float arcSegmentCount = 6; // An even number so that one arc vertex will be eactly arcRadius from arcCenter.
|
| - float arcSegmentAngle = ((padding) ? -angle : twoPiFloat - angle) / arcSegmentCount;
|
| -
|
| - vertices.append(startArcVertex);
|
| - for (unsigned i = 1; i < arcSegmentCount; ++i) {
|
| - float angle = startAngle + arcSegmentAngle * i;
|
| - vertices.append(arcCenter + FloatPoint(cos(angle) * arcRadius, sin(angle) * arcRadius));
|
| - }
|
| - vertices.append(endArcVertex);
|
| -}
|
| -
|
| -static inline void snapVerticesToLayoutUnitGrid(Vector<FloatPoint>& vertices)
|
| -{
|
| - for (unsigned i = 0; i < vertices.size(); ++i)
|
| - vertices[i] = flooredLayoutPoint(vertices[i]);
|
| -}
|
| +static inline bool overlapsYRange(const FloatRect& rect, float y1, float y2) { return !rect.isEmpty() && y2 >= y1 && y2 >= rect.y() && y1 <= rect.maxY(); }
|
|
|
| -static inline PassOwnPtr<FloatPolygon> computeShapeMarginBounds(const FloatPolygon& polygon, float margin, WindRule fillRule)
|
| +float OffsetPolygonEdge::xIntercept(float y) const
|
| {
|
| - OwnPtr<Vector<FloatPoint> > marginVertices = adoptPtr(new Vector<FloatPoint>());
|
| - FloatPoint intersection;
|
| + ASSERT(y >= minY() && y <= maxY());
|
|
|
| - for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) {
|
| - const FloatPolygonEdge& thisEdge = polygon.edgeAt(i);
|
| - const FloatPolygonEdge& prevEdge = thisEdge.previousEdge();
|
| - OffsetPolygonEdge thisOffsetEdge(thisEdge, outwardEdgeNormal(thisEdge) * margin);
|
| - OffsetPolygonEdge prevOffsetEdge(prevEdge, outwardEdgeNormal(prevEdge) * margin);
|
| + if (vertex1().y() == vertex2().y() || vertex1().x() == vertex2().x())
|
| + return minX();
|
| + if (y == minY())
|
| + return vertex1().y() < vertex2().y() ? vertex1().x() : vertex2().x();
|
| + if (y == maxY())
|
| + return vertex1().y() > vertex2().y() ? vertex1().x() : vertex2().x();
|
|
|
| - if (prevOffsetEdge.intersection(thisOffsetEdge, intersection))
|
| - marginVertices->append(intersection);
|
| - else
|
| - appendArc(*marginVertices, thisEdge.vertex1(), margin, prevOffsetEdge.vertex2(), thisOffsetEdge.vertex1(), false);
|
| - }
|
| -
|
| - snapVerticesToLayoutUnitGrid(*marginVertices);
|
| - return adoptPtr(new FloatPolygon(marginVertices.release(), fillRule));
|
| + return vertex1().x() + ((y - vertex1().y()) * (vertex2().x() - vertex1().x()) / (vertex2().y() - vertex1().y()));
|
| }
|
|
|
| -const FloatPolygon& PolygonShape::shapeMarginBounds() const
|
| +FloatShapeInterval OffsetPolygonEdge::clippedEdgeXRange(float y1, float y2) const
|
| {
|
| - ASSERT(shapeMargin() >= 0);
|
| - if (!shapeMargin() || m_polygon.isEmpty())
|
| - return m_polygon;
|
| + if (!overlapsYRange(y1, y2))
|
| + return FloatShapeInterval();
|
|
|
| - if (!m_marginBounds)
|
| - m_marginBounds = computeShapeMarginBounds(m_polygon, shapeMargin(), m_polygon.fillRule());
|
| + if (isWithinYRange(y1, y2))
|
| + return FloatShapeInterval(minX(), maxX());
|
|
|
| - return *m_marginBounds;
|
| -}
|
| + // Clip the edge line segment to the vertical range y1,y2 and then return
|
| + // the clipped line segment's horizontal range.
|
|
|
| -static inline bool getVertexIntersectionVertices(const EdgeIntersection& intersection, FloatPoint& prevVertex, FloatPoint& thisVertex, FloatPoint& nextVertex)
|
| -{
|
| - if (intersection.type != VertexMinY && intersection.type != VertexMaxY)
|
| - return false;
|
| -
|
| - ASSERT(intersection.edge && intersection.edge->polygon());
|
| - const FloatPolygon& polygon = *(intersection.edge->polygon());
|
| - const FloatPolygonEdge& thisEdge = *(intersection.edge);
|
| -
|
| - if ((intersection.type == VertexMinY && (thisEdge.vertex1().y() < thisEdge.vertex2().y()))
|
| - || (intersection.type == VertexMaxY && (thisEdge.vertex1().y() > thisEdge.vertex2().y()))) {
|
| - prevVertex = polygon.vertexAt(thisEdge.previousEdge().vertexIndex1());
|
| - thisVertex = polygon.vertexAt(thisEdge.vertexIndex1());
|
| - nextVertex = polygon.vertexAt(thisEdge.vertexIndex2());
|
| + FloatPoint minYVertex;
|
| + FloatPoint maxYVertex;
|
| + if (vertex1().y() < vertex2().y()) {
|
| + minYVertex = vertex1();
|
| + maxYVertex = vertex2();
|
| } else {
|
| - prevVertex = polygon.vertexAt(thisEdge.vertexIndex1());
|
| - thisVertex = polygon.vertexAt(thisEdge.vertexIndex2());
|
| - nextVertex = polygon.vertexAt(thisEdge.nextEdge().vertexIndex2());
|
| + minYVertex = vertex2();
|
| + maxYVertex = vertex1();
|
| }
|
| -
|
| - return true;
|
| + float xForY1 = (minYVertex.y() < y1) ? xIntercept(y1) : minYVertex.x();
|
| + float xForY2 = (maxYVertex.y() > y2) ? xIntercept(y2) : maxYVertex.x();
|
| + return FloatShapeInterval(std::min(xForY1, xForY2), std::max(xForY1, xForY2));
|
| }
|
|
|
| -static inline bool appendIntervalX(float x, bool inside, FloatShapeIntervals& result)
|
| +static float circleXIntercept(float y, float radius)
|
| {
|
| - if (!inside)
|
| - result.append(FloatShapeInterval(x, x));
|
| - else
|
| - result.last().setX2(x);
|
| -
|
| - return !inside;
|
| + ASSERT(radius > 0);
|
| + return radius * sqrt(1 - (y * y) / (radius * radius));
|
| }
|
|
|
| -static bool compareEdgeIntersectionX(const EdgeIntersection& intersection1, const EdgeIntersection& intersection2)
|
| +static FloatShapeInterval clippedCircleXRange(const FloatPoint& center, float radius, float y1, float y2)
|
| {
|
| - float x1 = intersection1.point.x();
|
| - float x2 = intersection2.point.x();
|
| - return (x1 == x2) ? intersection1.type < intersection2.type : x1 < x2;
|
| -}
|
| -
|
| -static void computeXIntersections(const FloatPolygon& polygon, float y, bool isMinY, FloatShapeIntervals& result)
|
| -{
|
| - Vector<const FloatPolygonEdge*> edges;
|
| - if (!polygon.overlappingEdges(y, y, edges))
|
| - return;
|
| -
|
| - Vector<EdgeIntersection> intersections;
|
| - EdgeIntersection intersection;
|
| - for (unsigned i = 0; i < edges.size(); ++i) {
|
| - if (computeXIntersection(edges[i], y, intersection) && intersection.type != VertexYBoth)
|
| - intersections.append(intersection);
|
| - }
|
| -
|
| - if (intersections.size() < 2)
|
| - return;
|
| + if (y1 > center.y() + radius || y2 < center.y() - radius)
|
| + return FloatShapeInterval();
|
|
|
| - std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIntersectionX);
|
| + if (center.y() >= y1 && center.y() <= y2)
|
| + return FloatShapeInterval(center.x() - radius, center.x() + radius);
|
|
|
| - unsigned index = 0;
|
| - int windCount = 0;
|
| - bool inside = false;
|
| + // Clip the circle to the vertical range y1,y2 and return the extent of the clipped circle's
|
| + // projection on the X axis
|
|
|
| - while (index < intersections.size()) {
|
| - const EdgeIntersection& thisIntersection = intersections[index];
|
| - if (index + 1 < intersections.size()) {
|
| - const EdgeIntersection& nextIntersection = intersections[index + 1];
|
| - if ((thisIntersection.point.x() == nextIntersection.point.x()) && (thisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) {
|
| - if (thisIntersection.type == nextIntersection.type) {
|
| - // Skip pairs of intersections whose types are VertexMaxY,VertexMaxY and VertexMinY,VertexMinY.
|
| - index += 2;
|
| - } else {
|
| - // Replace pairs of intersections whose types are VertexMinY,VertexMaxY or VertexMaxY,VertexMinY with one intersection.
|
| - ++index;
|
| - }
|
| - continue;
|
| - }
|
| - }
|
| -
|
| - bool edgeCrossing = thisIntersection.type == Normal;
|
| - if (!edgeCrossing) {
|
| - FloatPoint prevVertex;
|
| - FloatPoint thisVertex;
|
| - FloatPoint nextVertex;
|
| -
|
| - if (getVertexIntersectionVertices(thisIntersection, prevVertex, thisVertex, nextVertex)) {
|
| - if (nextVertex.y() == y)
|
| - edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y() < y;
|
| - else if (prevVertex.y() == y)
|
| - edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y() < y;
|
| - else
|
| - edgeCrossing = true;
|
| - }
|
| - }
|
| -
|
| - if (edgeCrossing && polygon.fillRule() == RULE_NONZERO) {
|
| - const FloatPolygonEdge& thisEdge = *thisIntersection.edge;
|
| - windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 : -1;
|
| - }
|
| -
|
| - if (edgeCrossing && (!inside || !windCount))
|
| - inside = appendIntervalX(thisIntersection.point.x(), inside, result);
|
| -
|
| - ++index;
|
| - }
|
| + float xi = circleXIntercept((y2 < center.y() ? y2 : y1) - center.y(), radius);
|
| + return FloatShapeInterval(center.x() - xi, center.x() + xi);
|
| }
|
|
|
| -static bool compareX1(const FloatShapeInterval a, const FloatShapeInterval& b) { return a.x1() < b.x1(); }
|
| -
|
| -static void sortAndMergeShapeIntervals(FloatShapeIntervals& intervals)
|
| +LayoutRect PolygonShape::shapeMarginLogicalBoundingBox() const
|
| {
|
| - std::sort(intervals.begin(), intervals.end(), compareX1);
|
| -
|
| - for (unsigned i = 1; i < intervals.size(); ) {
|
| - const FloatShapeInterval& thisInterval = intervals[i];
|
| - FloatShapeInterval& previousInterval = intervals[i - 1];
|
| - if (thisInterval.overlaps(previousInterval)) {
|
| - previousInterval.setX2(std::max<float>(previousInterval.x2(), thisInterval.x2()));
|
| - intervals.remove(i);
|
| - } else {
|
| - ++i;
|
| - }
|
| - }
|
| + FloatRect box = m_polygon.boundingBox();
|
| + box.inflate(shapeMargin());
|
| + return LayoutRect(box);
|
| }
|
|
|
| -static void computeOverlappingEdgeXProjections(const FloatPolygon& polygon, float y1, float y2, FloatShapeIntervals& result)
|
| +void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
|
| {
|
| - Vector<const FloatPolygonEdge*> edges;
|
| - if (!polygon.overlappingEdges(y1, y2, edges))
|
| - return;
|
| + float y1 = logicalTop.toFloat();
|
| + float y2 = logicalTop.toFloat() + logicalHeight.toFloat();
|
|
|
| - EdgeIntersection intersection;
|
| - for (unsigned i = 0; i < edges.size(); ++i) {
|
| - const FloatPolygonEdge *edge = edges[i];
|
| - float x1;
|
| - float x2;
|
| + if (m_polygon.isEmpty() || !overlapsYRange(m_polygon.boundingBox(), y1 - shapeMargin(), y2 + shapeMargin()))
|
| + return;
|
|
|
| - if (edge->minY() < y1) {
|
| - computeXIntersection(edge, y1, intersection);
|
| - x1 = intersection.point.x();
|
| - } else {
|
| - x1 = (edge->vertex1().y() < edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x();
|
| - }
|
| + Vector<const FloatPolygonEdge*> overlappingEdges;
|
| + if (!m_polygon.overlappingEdges(y1 - shapeMargin(), y2 + shapeMargin(), overlappingEdges))
|
| + return;
|
|
|
| - if (edge->maxY() > y2) {
|
| - computeXIntersection(edge, y2, intersection);
|
| - x2 = intersection.point.x();
|
| + FloatShapeInterval excludedInterval;
|
| + for (unsigned i = 0; i < overlappingEdges.size(); i++) {
|
| + const FloatPolygonEdge& edge = *(overlappingEdges[i]);
|
| + if (!shapeMargin()) {
|
| + excludedInterval.unite(OffsetPolygonEdge(edge, FloatSize()).clippedEdgeXRange(y1, y2));
|
| } else {
|
| - x2 = (edge->vertex1().y() > edge->vertex2().y()) ? edge->vertex1().x() : edge->vertex2().x();
|
| + excludedInterval.unite(OffsetPolygonEdge(edge, outwardEdgeNormal(edge) * shapeMargin()).clippedEdgeXRange(y1, y2));
|
| + excludedInterval.unite(OffsetPolygonEdge(edge, inwardEdgeNormal(edge) * shapeMargin()).clippedEdgeXRange(y1, y2));
|
| + excludedInterval.unite(clippedCircleXRange(edge.vertex1(), shapeMargin(), y1, y2));
|
| }
|
| -
|
| - if (x1 > x2)
|
| - std::swap(x1, x2);
|
| -
|
| - if (x2 > x1)
|
| - result.append(FloatShapeInterval(x1, x2));
|
| }
|
|
|
| - sortAndMergeShapeIntervals(result);
|
| -}
|
| -
|
| -void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
|
| -{
|
| - const FloatPolygon& polygon = shapeMarginBounds();
|
| - if (polygon.isEmpty())
|
| - return;
|
| -
|
| - float y1 = logicalTop.toFloat();
|
| - float y2 = (logicalTop + logicalHeight).toFloat();
|
| -
|
| - FloatShapeIntervals y1XIntervals, y2XIntervals;
|
| - computeXIntersections(polygon, y1, true, y1XIntervals);
|
| - computeXIntersections(polygon, y2, false, y2XIntervals);
|
| -
|
| - FloatShapeIntervals mergedIntervals;
|
| - FloatShapeInterval::uniteShapeIntervals(y1XIntervals, y2XIntervals, mergedIntervals);
|
| -
|
| - FloatShapeIntervals edgeIntervals;
|
| - computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals);
|
| -
|
| - FloatShapeIntervals excludedIntervals;
|
| - FloatShapeInterval::uniteShapeIntervals(mergedIntervals, edgeIntervals, excludedIntervals);
|
| -
|
| - for (unsigned i = 0; i < excludedIntervals.size(); ++i) {
|
| - const FloatShapeInterval& interval = excludedIntervals[i];
|
| - result.append(LineSegment(interval.x1(), interval.x2()));
|
| - }
|
| + if (!excludedInterval.isEmpty())
|
| + result.append(LineSegment(excludedInterval.x1(), excludedInterval.x2()));
|
| }
|
|
|
| } // namespace WebCore
|
|
|