Remove GraphicsContext::drawConvexPolygon() and GraphicsContext::clipConvexPolygon()
[WebKit-https.git] / Source / WebCore / platform / graphics / Path.cpp
1 /*
2  * Copyright (C) 2003, 2006 Apple Inc.  All rights reserved.
3  *                     2006 Rob Buis <buis@kde.org>
4  * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28
29 #include "config.h"
30 #include "Path.h"
31
32 #include "FloatPoint.h"
33 #include "FloatRect.h"
34 #include "FloatRoundedRect.h"
35 #include "PathTraversalState.h"
36 #include "RoundedRect.h"
37 #include "TextStream.h"
38 #include <math.h>
39 #include <wtf/MathExtras.h>
40
41 namespace WebCore {
42
43 float Path::length() const
44 {
45     PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
46
47     apply([&traversalState](const PathElement& element) {
48         traversalState.processPathElement(element);
49     });
50
51     return traversalState.totalLength();
52 }
53
54 PathTraversalState Path::traversalStateAtLength(float length, bool& success) const
55 {
56     PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
57
58     apply([&traversalState](const PathElement& element) {
59         traversalState.processPathElement(element);
60     });
61
62     success = traversalState.success();
63     return traversalState;
64 }
65
66 FloatPoint Path::pointAtLength(float length, bool& success) const
67 {
68     return traversalStateAtLength(length, success).current();
69 }
70
71 float Path::normalAngleAtLength(float length, bool& success) const
72 {
73     return traversalStateAtLength(length, success).normalAngle();
74 }
75
76 void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii, RoundedRectStrategy strategy)
77 {
78     if (rect.isEmpty())
79         return;
80
81     FloatSize radius(roundingRadii);
82     FloatSize halfSize(rect.width() / 2, rect.height() / 2);
83
84     // Apply the SVG corner radius constraints, per the rect section of the SVG shapes spec: if
85     // one of rx,ry is negative, then the other corner radius value is used. If both values are
86     // negative then rx = ry = 0. If rx is greater than half of the width of the rectangle
87     // then set rx to half of the width; ry is handled similarly.
88
89     if (radius.width() < 0)
90         radius.setWidth((radius.height() < 0) ? 0 : radius.height());
91
92     if (radius.height() < 0)
93         radius.setHeight(radius.width());
94
95     if (radius.width() > halfSize.width())
96         radius.setWidth(halfSize.width());
97
98     if (radius.height() > halfSize.height())
99         radius.setHeight(halfSize.height());
100
101     addRoundedRect(FloatRoundedRect(rect, radius, radius, radius, radius), strategy);
102 }
103
104 void Path::addRoundedRect(const FloatRoundedRect& r, RoundedRectStrategy strategy)
105 {
106     if (r.isEmpty())
107         return;
108
109     const FloatRoundedRect::Radii& radii = r.radii();
110     const FloatRect& rect = r.rect();
111
112     if (!r.isRenderable()) {
113         // If all the radii cannot be accommodated, return a rect.
114         addRect(rect);
115         return;
116     }
117
118     if (strategy == PreferNativeRoundedRect) {
119 #if USE(CG)
120         platformAddPathForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
121         return;
122 #endif
123     }
124
125     addBeziersForRoundedRect(rect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight());
126 }
127
128 void Path::addRoundedRect(const RoundedRect& r)
129 {
130     addRoundedRect(FloatRoundedRect(r));
131 }
132
133 // Approximation of control point positions on a bezier to simulate a quarter of a circle.
134 // This is 1-kappa, where kappa = 4 * (sqrt(2) - 1) / 3
135 static const float gCircleControlPoint = 0.447715f;
136
137 void Path::addBeziersForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
138 {
139     moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
140
141     addLineTo(FloatPoint(rect.maxX() - topRightRadius.width(), rect.y()));
142     if (topRightRadius.width() > 0 || topRightRadius.height() > 0)
143         addBezierCurveTo(FloatPoint(rect.maxX() - topRightRadius.width() * gCircleControlPoint, rect.y()),
144             FloatPoint(rect.maxX(), rect.y() + topRightRadius.height() * gCircleControlPoint),
145             FloatPoint(rect.maxX(), rect.y() + topRightRadius.height()));
146     addLineTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height()));
147     if (bottomRightRadius.width() > 0 || bottomRightRadius.height() > 0)
148         addBezierCurveTo(FloatPoint(rect.maxX(), rect.maxY() - bottomRightRadius.height() * gCircleControlPoint),
149             FloatPoint(rect.maxX() - bottomRightRadius.width() * gCircleControlPoint, rect.maxY()),
150             FloatPoint(rect.maxX() - bottomRightRadius.width(), rect.maxY()));
151     addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.maxY()));
152     if (bottomLeftRadius.width() > 0 || bottomLeftRadius.height() > 0)
153         addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.maxY()),
154             FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height() * gCircleControlPoint),
155             FloatPoint(rect.x(), rect.maxY() - bottomLeftRadius.height()));
156     addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height()));
157     if (topLeftRadius.width() > 0 || topLeftRadius.height() > 0)
158         addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint),
159             FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()),
160             FloatPoint(rect.x() + topLeftRadius.width(), rect.y()));
161
162     closeSubpath();
163 }
164
165 #if !USE(CG)
166 Path Path::polygonPathFromPoints(const Vector<FloatPoint>& points)
167 {
168     Path path;
169     if (points.size() < 2)
170         return path;
171
172     path.moveTo(points[0]);
173     for (size_t i = 1; i < points.size(); ++i)
174         path.addLineTo(points[i]);
175
176     path.closeSubpath();
177     return path;
178 }
179
180 FloatRect Path::fastBoundingRect() const
181 {
182     return boundingRect();
183 }
184 #endif
185
186 #ifndef NDEBUG
187 void Path::dump() const
188 {
189     TextStream stream;
190     stream << *this;
191     WTFLogAlways("%s", stream.release().utf8().data());
192 }
193 #endif
194
195 TextStream& operator<<(TextStream& stream, const Path& path)
196 {
197     TextStream::GroupScope group(stream);
198     stream << "path " << &path;
199
200     path.apply([&stream](const PathElement& element) {
201         switch (element.type) {
202         case PathElementMoveToPoint: // The points member will contain 1 value.
203             stream << " move to " << element.points[0];
204             break;
205         case PathElementAddLineToPoint: // The points member will contain 1 value.
206             stream << " add line to " << element.points[0];
207             break;
208         case PathElementAddQuadCurveToPoint: // The points member will contain 2 values.
209             stream << " add quad curve to " << element.points[0] << " " << element.points[1];
210             break;
211         case PathElementAddCurveToPoint: // The points member will contain 3 values.
212             stream << " add curve to " << element.points[0] << " " << element.points[1] << " " << element.points[2];
213             break;
214         case PathElementCloseSubpath: // The points member will contain no values.
215             stream << " close subpath";
216             break;
217         }
218     });
219     
220     return stream;
221 }
222
223 }