OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. | 2 * Copyright (C) 2012 Adobe Systems Incorporated. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * | 7 * |
8 * 1. Redistributions of source code must retain the above | 8 * 1. Redistributions of source code must retain the above |
9 * copyright notice, this list of conditions and the following | 9 * copyright notice, this list of conditions and the following |
10 * disclaimer. | 10 * disclaimer. |
(...skipping 12 matching lines...) Expand all Loading... |
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
27 * OF THE POSSIBILITY OF SUCH DAMAGE. | 27 * OF THE POSSIBILITY OF SUCH DAMAGE. |
28 */ | 28 */ |
29 | 29 |
30 #include "config.h" | 30 #include "config.h" |
31 #include "core/rendering/shapes/PolygonShape.h" | 31 #include "core/rendering/shapes/PolygonShape.h" |
32 | 32 |
33 #include "core/rendering/shapes/ShapeInterval.h" | |
34 #include "platform/geometry/LayoutPoint.h" | 33 #include "platform/geometry/LayoutPoint.h" |
35 #include "wtf/MathExtras.h" | 34 #include "wtf/MathExtras.h" |
36 | 35 |
37 namespace WebCore { | 36 namespace WebCore { |
38 | 37 |
39 enum EdgeIntersectionType { | |
40 Normal, | |
41 VertexMinY, | |
42 VertexMaxY, | |
43 VertexYBoth | |
44 }; | |
45 | |
46 struct EdgeIntersection { | |
47 const FloatPolygonEdge* edge; | |
48 FloatPoint point; | |
49 EdgeIntersectionType type; | |
50 }; | |
51 | |
52 static bool computeXIntersection(const FloatPolygonEdge* edgePointer, float y, E
dgeIntersection& result) | |
53 { | |
54 const FloatPolygonEdge& edge = *edgePointer; | |
55 | |
56 if (edge.minY() > y || edge.maxY() < y) | |
57 return false; | |
58 | |
59 const FloatPoint& vertex1 = edge.vertex1(); | |
60 const FloatPoint& vertex2 = edge.vertex2(); | |
61 float dy = vertex2.y() - vertex1.y(); | |
62 | |
63 float intersectionX; | |
64 EdgeIntersectionType intersectionType; | |
65 | |
66 if (!dy) { | |
67 intersectionType = VertexYBoth; | |
68 intersectionX = edge.minX(); | |
69 } else if (y == edge.minY()) { | |
70 intersectionType = VertexMinY; | |
71 intersectionX = (vertex1.y() < vertex2.y()) ? vertex1.x() : vertex2.x(); | |
72 } else if (y == edge.maxY()) { | |
73 intersectionType = VertexMaxY; | |
74 intersectionX = (vertex1.y() > vertex2.y()) ? vertex1.x() : vertex2.x(); | |
75 } else { | |
76 intersectionType = Normal; | |
77 intersectionX = ((y - vertex1.y()) * (vertex2.x() - vertex1.x()) / dy) +
vertex1.x(); | |
78 } | |
79 | |
80 result.edge = edgePointer; | |
81 result.type = intersectionType; | |
82 result.point.set(intersectionX, y); | |
83 | |
84 return true; | |
85 } | |
86 | |
87 static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge) | 38 static inline FloatSize inwardEdgeNormal(const FloatPolygonEdge& edge) |
88 { | 39 { |
89 FloatSize edgeDelta = edge.vertex2() - edge.vertex1(); | 40 FloatSize edgeDelta = edge.vertex2() - edge.vertex1(); |
90 if (!edgeDelta.width()) | 41 if (!edgeDelta.width()) |
91 return FloatSize((edgeDelta.height() > 0 ? -1 : 1), 0); | 42 return FloatSize((edgeDelta.height() > 0 ? -1 : 1), 0); |
92 if (!edgeDelta.height()) | 43 if (!edgeDelta.height()) |
93 return FloatSize(0, (edgeDelta.width() > 0 ? 1 : -1)); | 44 return FloatSize(0, (edgeDelta.width() > 0 ? 1 : -1)); |
94 float edgeLength = edgeDelta.diagonalLength(); | 45 float edgeLength = edgeDelta.diagonalLength(); |
95 return FloatSize(-edgeDelta.height() / edgeLength, edgeDelta.width() / edgeL
ength); | 46 return FloatSize(-edgeDelta.height() / edgeLength, edgeDelta.width() / edgeL
ength); |
96 } | 47 } |
97 | 48 |
98 static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge) | 49 static inline FloatSize outwardEdgeNormal(const FloatPolygonEdge& edge) |
99 { | 50 { |
100 return -inwardEdgeNormal(edge); | 51 return -inwardEdgeNormal(edge); |
101 } | 52 } |
102 | 53 |
103 static inline void appendArc(Vector<FloatPoint>& vertices, const FloatPoint& arc
Center, float arcRadius, const FloatPoint& startArcVertex, const FloatPoint& end
ArcVertex, bool padding) | 54 static inline bool overlapsYRange(const FloatRect& rect, float y1, float y2) { r
eturn !rect.isEmpty() && y2 >= y1 && y2 >= rect.y() && y1 <= rect.maxY(); } |
| 55 |
| 56 float OffsetPolygonEdge::xIntercept(float y) const |
104 { | 57 { |
105 float startAngle = atan2(startArcVertex.y() - arcCenter.y(), startArcVertex.
x() - arcCenter.x()); | 58 ASSERT(y >= minY() && y <= maxY()); |
106 float endAngle = atan2(endArcVertex.y() - arcCenter.y(), endArcVertex.x() -
arcCenter.x()); | |
107 if (startAngle < 0) | |
108 startAngle += twoPiFloat; | |
109 if (endAngle < 0) | |
110 endAngle += twoPiFloat; | |
111 float angle = (startAngle > endAngle) ? (startAngle - endAngle) : (startAngl
e + twoPiFloat - endAngle); | |
112 const float arcSegmentCount = 6; // An even number so that one arc vertex wi
ll be eactly arcRadius from arcCenter. | |
113 float arcSegmentAngle = ((padding) ? -angle : twoPiFloat - angle) / arcSegm
entCount; | |
114 | 59 |
115 vertices.append(startArcVertex); | 60 if (vertex1().y() == vertex2().y() || vertex1().x() == vertex2().x()) |
116 for (unsigned i = 1; i < arcSegmentCount; ++i) { | 61 return minX(); |
117 float angle = startAngle + arcSegmentAngle * i; | 62 if (y == minY()) |
118 vertices.append(arcCenter + FloatPoint(cos(angle) * arcRadius, sin(angle
) * arcRadius)); | 63 return vertex1().y() < vertex2().y() ? vertex1().x() : vertex2().x(); |
119 } | 64 if (y == maxY()) |
120 vertices.append(endArcVertex); | 65 return vertex1().y() > vertex2().y() ? vertex1().x() : vertex2().x(); |
| 66 |
| 67 return vertex1().x() + ((y - vertex1().y()) * (vertex2().x() - vertex1().x()
) / (vertex2().y() - vertex1().y())); |
121 } | 68 } |
122 | 69 |
123 static inline void snapVerticesToLayoutUnitGrid(Vector<FloatPoint>& vertices) | 70 FloatShapeInterval OffsetPolygonEdge::clippedEdgeXRange(float y1, float y2) cons
t |
124 { | 71 { |
125 for (unsigned i = 0; i < vertices.size(); ++i) | 72 if (!overlapsYRange(y1, y2)) |
126 vertices[i] = flooredLayoutPoint(vertices[i]); | 73 return FloatShapeInterval(); |
| 74 |
| 75 if (isWithinYRange(y1, y2)) |
| 76 return FloatShapeInterval(minX(), maxX()); |
| 77 |
| 78 // Clip the edge line segment to the vertical range y1,y2 and then return |
| 79 // the clipped line segment's horizontal range. |
| 80 |
| 81 FloatPoint minYVertex; |
| 82 FloatPoint maxYVertex; |
| 83 if (vertex1().y() < vertex2().y()) { |
| 84 minYVertex = vertex1(); |
| 85 maxYVertex = vertex2(); |
| 86 } else { |
| 87 minYVertex = vertex2(); |
| 88 maxYVertex = vertex1(); |
| 89 } |
| 90 float xForY1 = (minYVertex.y() < y1) ? xIntercept(y1) : minYVertex.x(); |
| 91 float xForY2 = (maxYVertex.y() > y2) ? xIntercept(y2) : maxYVertex.x(); |
| 92 return FloatShapeInterval(std::min(xForY1, xForY2), std::max(xForY1, xForY2)
); |
127 } | 93 } |
128 | 94 |
129 static inline PassOwnPtr<FloatPolygon> computeShapeMarginBounds(const FloatPolyg
on& polygon, float margin, WindRule fillRule) | 95 static float circleXIntercept(float y, float radius) |
130 { | 96 { |
131 OwnPtr<Vector<FloatPoint> > marginVertices = adoptPtr(new Vector<FloatPoint>
()); | 97 ASSERT(radius > 0); |
132 FloatPoint intersection; | 98 return radius * sqrt(1 - (y * y) / (radius * radius)); |
133 | |
134 for (unsigned i = 0; i < polygon.numberOfEdges(); ++i) { | |
135 const FloatPolygonEdge& thisEdge = polygon.edgeAt(i); | |
136 const FloatPolygonEdge& prevEdge = thisEdge.previousEdge(); | |
137 OffsetPolygonEdge thisOffsetEdge(thisEdge, outwardEdgeNormal(thisEdge) *
margin); | |
138 OffsetPolygonEdge prevOffsetEdge(prevEdge, outwardEdgeNormal(prevEdge) *
margin); | |
139 | |
140 if (prevOffsetEdge.intersection(thisOffsetEdge, intersection)) | |
141 marginVertices->append(intersection); | |
142 else | |
143 appendArc(*marginVertices, thisEdge.vertex1(), margin, prevOffsetEdg
e.vertex2(), thisOffsetEdge.vertex1(), false); | |
144 } | |
145 | |
146 snapVerticesToLayoutUnitGrid(*marginVertices); | |
147 return adoptPtr(new FloatPolygon(marginVertices.release(), fillRule)); | |
148 } | 99 } |
149 | 100 |
150 const FloatPolygon& PolygonShape::shapeMarginBounds() const | 101 static FloatShapeInterval clippedCircleXRange(const FloatPoint& center, float ra
dius, float y1, float y2) |
151 { | 102 { |
152 ASSERT(shapeMargin() >= 0); | 103 if (y1 > center.y() + radius || y2 < center.y() - radius) |
153 if (!shapeMargin() || m_polygon.isEmpty()) | 104 return FloatShapeInterval(); |
154 return m_polygon; | |
155 | 105 |
156 if (!m_marginBounds) | 106 if (center.y() >= y1 && center.y() <= y2) |
157 m_marginBounds = computeShapeMarginBounds(m_polygon, shapeMargin(), m_po
lygon.fillRule()); | 107 return FloatShapeInterval(center.x() - radius, center.x() + radius); |
158 | 108 |
159 return *m_marginBounds; | 109 // Clip the circle to the vertical range y1,y2 and return the extent of the
clipped circle's |
| 110 // projection on the X axis |
| 111 |
| 112 float xi = circleXIntercept((y2 < center.y() ? y2 : y1) - center.y(), radiu
s); |
| 113 return FloatShapeInterval(center.x() - xi, center.x() + xi); |
160 } | 114 } |
161 | 115 |
162 static inline bool getVertexIntersectionVertices(const EdgeIntersection& interse
ction, FloatPoint& prevVertex, FloatPoint& thisVertex, FloatPoint& nextVertex) | 116 LayoutRect PolygonShape::shapeMarginLogicalBoundingBox() const |
163 { | 117 { |
164 if (intersection.type != VertexMinY && intersection.type != VertexMaxY) | 118 FloatRect box = m_polygon.boundingBox(); |
165 return false; | 119 box.inflate(shapeMargin()); |
166 | 120 return LayoutRect(box); |
167 ASSERT(intersection.edge && intersection.edge->polygon()); | |
168 const FloatPolygon& polygon = *(intersection.edge->polygon()); | |
169 const FloatPolygonEdge& thisEdge = *(intersection.edge); | |
170 | |
171 if ((intersection.type == VertexMinY && (thisEdge.vertex1().y() < thisEdge.v
ertex2().y())) | |
172 || (intersection.type == VertexMaxY && (thisEdge.vertex1().y() > thisEdg
e.vertex2().y()))) { | |
173 prevVertex = polygon.vertexAt(thisEdge.previousEdge().vertexIndex1()); | |
174 thisVertex = polygon.vertexAt(thisEdge.vertexIndex1()); | |
175 nextVertex = polygon.vertexAt(thisEdge.vertexIndex2()); | |
176 } else { | |
177 prevVertex = polygon.vertexAt(thisEdge.vertexIndex1()); | |
178 thisVertex = polygon.vertexAt(thisEdge.vertexIndex2()); | |
179 nextVertex = polygon.vertexAt(thisEdge.nextEdge().vertexIndex2()); | |
180 } | |
181 | |
182 return true; | |
183 } | |
184 | |
185 static inline bool appendIntervalX(float x, bool inside, FloatShapeIntervals& re
sult) | |
186 { | |
187 if (!inside) | |
188 result.append(FloatShapeInterval(x, x)); | |
189 else | |
190 result.last().setX2(x); | |
191 | |
192 return !inside; | |
193 } | |
194 | |
195 static bool compareEdgeIntersectionX(const EdgeIntersection& intersection1, cons
t EdgeIntersection& intersection2) | |
196 { | |
197 float x1 = intersection1.point.x(); | |
198 float x2 = intersection2.point.x(); | |
199 return (x1 == x2) ? intersection1.type < intersection2.type : x1 < x2; | |
200 } | |
201 | |
202 static void computeXIntersections(const FloatPolygon& polygon, float y, bool isM
inY, FloatShapeIntervals& result) | |
203 { | |
204 Vector<const FloatPolygonEdge*> edges; | |
205 if (!polygon.overlappingEdges(y, y, edges)) | |
206 return; | |
207 | |
208 Vector<EdgeIntersection> intersections; | |
209 EdgeIntersection intersection; | |
210 for (unsigned i = 0; i < edges.size(); ++i) { | |
211 if (computeXIntersection(edges[i], y, intersection) && intersection.type
!= VertexYBoth) | |
212 intersections.append(intersection); | |
213 } | |
214 | |
215 if (intersections.size() < 2) | |
216 return; | |
217 | |
218 std::sort(intersections.begin(), intersections.end(), WebCore::compareEdgeIn
tersectionX); | |
219 | |
220 unsigned index = 0; | |
221 int windCount = 0; | |
222 bool inside = false; | |
223 | |
224 while (index < intersections.size()) { | |
225 const EdgeIntersection& thisIntersection = intersections[index]; | |
226 if (index + 1 < intersections.size()) { | |
227 const EdgeIntersection& nextIntersection = intersections[index + 1]; | |
228 if ((thisIntersection.point.x() == nextIntersection.point.x()) && (t
hisIntersection.type == VertexMinY || thisIntersection.type == VertexMaxY)) { | |
229 if (thisIntersection.type == nextIntersection.type) { | |
230 // Skip pairs of intersections whose types are VertexMaxY,Ve
rtexMaxY and VertexMinY,VertexMinY. | |
231 index += 2; | |
232 } else { | |
233 // Replace pairs of intersections whose types are VertexMinY
,VertexMaxY or VertexMaxY,VertexMinY with one intersection. | |
234 ++index; | |
235 } | |
236 continue; | |
237 } | |
238 } | |
239 | |
240 bool edgeCrossing = thisIntersection.type == Normal; | |
241 if (!edgeCrossing) { | |
242 FloatPoint prevVertex; | |
243 FloatPoint thisVertex; | |
244 FloatPoint nextVertex; | |
245 | |
246 if (getVertexIntersectionVertices(thisIntersection, prevVertex, this
Vertex, nextVertex)) { | |
247 if (nextVertex.y() == y) | |
248 edgeCrossing = (isMinY) ? prevVertex.y() > y : prevVertex.y(
) < y; | |
249 else if (prevVertex.y() == y) | |
250 edgeCrossing = (isMinY) ? nextVertex.y() > y : nextVertex.y(
) < y; | |
251 else | |
252 edgeCrossing = true; | |
253 } | |
254 } | |
255 | |
256 if (edgeCrossing && polygon.fillRule() == RULE_NONZERO) { | |
257 const FloatPolygonEdge& thisEdge = *thisIntersection.edge; | |
258 windCount += (thisEdge.vertex2().y() > thisEdge.vertex1().y()) ? 1 :
-1; | |
259 } | |
260 | |
261 if (edgeCrossing && (!inside || !windCount)) | |
262 inside = appendIntervalX(thisIntersection.point.x(), inside, result)
; | |
263 | |
264 ++index; | |
265 } | |
266 } | |
267 | |
268 static bool compareX1(const FloatShapeInterval a, const FloatShapeInterval& b) {
return a.x1() < b.x1(); } | |
269 | |
270 static void sortAndMergeShapeIntervals(FloatShapeIntervals& intervals) | |
271 { | |
272 std::sort(intervals.begin(), intervals.end(), compareX1); | |
273 | |
274 for (unsigned i = 1; i < intervals.size(); ) { | |
275 const FloatShapeInterval& thisInterval = intervals[i]; | |
276 FloatShapeInterval& previousInterval = intervals[i - 1]; | |
277 if (thisInterval.overlaps(previousInterval)) { | |
278 previousInterval.setX2(std::max<float>(previousInterval.x2(), thisIn
terval.x2())); | |
279 intervals.remove(i); | |
280 } else { | |
281 ++i; | |
282 } | |
283 } | |
284 } | |
285 | |
286 static void computeOverlappingEdgeXProjections(const FloatPolygon& polygon, floa
t y1, float y2, FloatShapeIntervals& result) | |
287 { | |
288 Vector<const FloatPolygonEdge*> edges; | |
289 if (!polygon.overlappingEdges(y1, y2, edges)) | |
290 return; | |
291 | |
292 EdgeIntersection intersection; | |
293 for (unsigned i = 0; i < edges.size(); ++i) { | |
294 const FloatPolygonEdge *edge = edges[i]; | |
295 float x1; | |
296 float x2; | |
297 | |
298 if (edge->minY() < y1) { | |
299 computeXIntersection(edge, y1, intersection); | |
300 x1 = intersection.point.x(); | |
301 } else { | |
302 x1 = (edge->vertex1().y() < edge->vertex2().y()) ? edge->vertex1().x
() : edge->vertex2().x(); | |
303 } | |
304 | |
305 if (edge->maxY() > y2) { | |
306 computeXIntersection(edge, y2, intersection); | |
307 x2 = intersection.point.x(); | |
308 } else { | |
309 x2 = (edge->vertex1().y() > edge->vertex2().y()) ? edge->vertex1().x
() : edge->vertex2().x(); | |
310 } | |
311 | |
312 if (x1 > x2) | |
313 std::swap(x1, x2); | |
314 | |
315 if (x2 > x1) | |
316 result.append(FloatShapeInterval(x1, x2)); | |
317 } | |
318 | |
319 sortAndMergeShapeIntervals(result); | |
320 } | 121 } |
321 | 122 |
322 void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logica
lHeight, SegmentList& result) const | 123 void PolygonShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logica
lHeight, SegmentList& result) const |
323 { | 124 { |
324 const FloatPolygon& polygon = shapeMarginBounds(); | 125 float y1 = logicalTop.toFloat(); |
325 if (polygon.isEmpty()) | 126 float y2 = logicalTop.toFloat() + logicalHeight.toFloat(); |
| 127 |
| 128 if (m_polygon.isEmpty() || !overlapsYRange(m_polygon.boundingBox(), y1 - sha
peMargin(), y2 + shapeMargin())) |
326 return; | 129 return; |
327 | 130 |
328 float y1 = logicalTop.toFloat(); | 131 Vector<const FloatPolygonEdge*> overlappingEdges; |
329 float y2 = (logicalTop + logicalHeight).toFloat(); | 132 if (!m_polygon.overlappingEdges(y1 - shapeMargin(), y2 + shapeMargin(), over
lappingEdges)) |
| 133 return; |
330 | 134 |
331 FloatShapeIntervals y1XIntervals, y2XIntervals; | 135 FloatShapeInterval excludedInterval; |
332 computeXIntersections(polygon, y1, true, y1XIntervals); | 136 for (unsigned i = 0; i < overlappingEdges.size(); i++) { |
333 computeXIntersections(polygon, y2, false, y2XIntervals); | 137 const FloatPolygonEdge& edge = *(overlappingEdges[i]); |
| 138 if (!shapeMargin()) { |
| 139 excludedInterval.unite(OffsetPolygonEdge(edge, FloatSize()).clippedE
dgeXRange(y1, y2)); |
| 140 } else { |
| 141 excludedInterval.unite(OffsetPolygonEdge(edge, outwardEdgeNormal(edg
e) * shapeMargin()).clippedEdgeXRange(y1, y2)); |
| 142 excludedInterval.unite(OffsetPolygonEdge(edge, inwardEdgeNormal(edge
) * shapeMargin()).clippedEdgeXRange(y1, y2)); |
| 143 excludedInterval.unite(clippedCircleXRange(edge.vertex1(), shapeMarg
in(), y1, y2)); |
| 144 } |
| 145 } |
334 | 146 |
335 FloatShapeIntervals mergedIntervals; | 147 if (!excludedInterval.isEmpty()) |
336 FloatShapeInterval::uniteShapeIntervals(y1XIntervals, y2XIntervals, mergedIn
tervals); | 148 result.append(LineSegment(excludedInterval.x1(), excludedInterval.x2()))
; |
337 | |
338 FloatShapeIntervals edgeIntervals; | |
339 computeOverlappingEdgeXProjections(polygon, y1, y2, edgeIntervals); | |
340 | |
341 FloatShapeIntervals excludedIntervals; | |
342 FloatShapeInterval::uniteShapeIntervals(mergedIntervals, edgeIntervals, excl
udedIntervals); | |
343 | |
344 for (unsigned i = 0; i < excludedIntervals.size(); ++i) { | |
345 const FloatShapeInterval& interval = excludedIntervals[i]; | |
346 result.append(LineSegment(interval.x1(), interval.x2())); | |
347 } | |
348 } | 149 } |
349 | 150 |
350 } // namespace WebCore | 151 } // namespace WebCore |
OLD | NEW |