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 |