260ed516721888e61b6263aaf2c1b82ef7ff30b1
[WebKit.git] / Source / WebCore / svg / SVGPathUtilities.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010, 2012. All rights reserved.
3  * Copyright (C) 2015 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "SVGPathUtilities.h"
23
24 #include "Path.h"
25 #include "PathTraversalState.h"
26 #include "SVGPathBlender.h"
27 #include "SVGPathBuilder.h"
28 #include "SVGPathByteStreamBuilder.h"
29 #include "SVGPathByteStreamSource.h"
30 #include "SVGPathConsumer.h"
31 #include "SVGPathElement.h"
32 #include "SVGPathParser.h"
33 #include "SVGPathSegListBuilder.h"
34 #include "SVGPathSegListSource.h"
35 #include "SVGPathStringBuilder.h"
36 #include "SVGPathStringSource.h"
37 #include "SVGPathTraversalStateBuilder.h"
38
39 namespace WebCore {
40
41 bool buildPathFromString(const String& d, Path& result)
42 {
43     if (d.isEmpty())
44         return true;
45
46     SVGPathBuilder builder(result);
47     SVGPathStringSource source(d);
48     return SVGPathParser::parse(source, builder);
49 }
50
51 bool buildSVGPathByteStreamFromSVGPathSegList(const SVGPathSegList& list, SVGPathByteStream& result, PathParsingMode parsingMode)
52 {
53     result.clear();
54     if (list.isEmpty())
55         return true;
56
57     SVGPathSegListSource source(list);
58     return SVGPathParser::parseToByteStream(source, result, parsingMode);
59 }
60
61 bool appendSVGPathByteStreamFromSVGPathSeg(RefPtr<SVGPathSeg>&& pathSeg, SVGPathByteStream& result, PathParsingMode parsingMode)
62 {
63     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=15412 - Implement normalized path segment lists!
64     ASSERT(parsingMode == UnalteredParsing);
65
66     SVGPathSegList appendedItemList(PathSegUnalteredRole);
67     appendedItemList.append(WTFMove(pathSeg));
68
69     SVGPathByteStream appendedByteStream;
70     SVGPathSegListSource source(appendedItemList);
71     bool ok = SVGPathParser::parseToByteStream(source, result, parsingMode, false);
72
73     if (ok)
74         result.append(appendedByteStream);
75
76     return ok;
77 }
78
79 bool buildPathFromByteStream(const SVGPathByteStream& stream, Path& result)
80 {
81     if (stream.isEmpty())
82         return true;
83
84     SVGPathBuilder builder(result);
85     SVGPathByteStreamSource source(stream);
86     return SVGPathParser::parse(source, builder);
87 }
88
89 bool buildSVGPathSegListFromByteStream(const SVGPathByteStream& stream, SVGPathElement& element, SVGPathSegList& result, PathParsingMode parsingMode)
90 {
91     if (stream.isEmpty())
92         return true;
93
94     SVGPathSegListBuilder builder(element, result, parsingMode == NormalizedParsing ? PathSegNormalizedRole : PathSegUnalteredRole);
95     SVGPathByteStreamSource source(stream);
96     return SVGPathParser::parse(source, builder, parsingMode);
97 }
98
99 bool buildStringFromByteStream(const SVGPathByteStream& stream, String& result, PathParsingMode parsingMode)
100 {
101     if (stream.isEmpty())
102         return true;
103
104     SVGPathByteStreamSource source(stream);
105     return SVGPathParser::parseToString(source, result, parsingMode);
106 }
107
108 bool buildStringFromSVGPathSegList(const SVGPathSegList& list, String& result, PathParsingMode parsingMode)
109 {
110     result = String();
111     if (list.isEmpty())
112         return true;
113
114     SVGPathSegListSource source(list);
115     return SVGPathParser::parseToString(source, result, parsingMode);
116 }
117
118 bool buildSVGPathByteStreamFromString(const String& d, SVGPathByteStream& result, PathParsingMode parsingMode)
119 {
120     result.clear();
121     if (d.isEmpty())
122         return true;
123
124     SVGPathStringSource source(d);
125     return SVGPathParser::parseToByteStream(source, result, parsingMode);
126 }
127
128 bool canBlendSVGPathByteStreams(const SVGPathByteStream& fromStream, const SVGPathByteStream& toStream)
129 {
130     SVGPathByteStreamSource fromSource(fromStream);
131     SVGPathByteStreamSource toSource(toStream);
132     return SVGPathBlender::canBlendPaths(fromSource, toSource);
133 }
134
135 bool buildAnimatedSVGPathByteStream(const SVGPathByteStream& fromStream, const SVGPathByteStream& toStream, SVGPathByteStream& result, float progress)
136 {
137     ASSERT(&toStream != &result);
138     result.clear();
139     if (toStream.isEmpty())
140         return true;
141
142     SVGPathByteStreamBuilder builder(result);
143
144     SVGPathByteStreamSource fromSource(fromStream);
145     SVGPathByteStreamSource toSource(toStream);
146     return SVGPathBlender::blendAnimatedPath(fromSource, toSource, builder, progress);
147 }
148
149 bool addToSVGPathByteStream(SVGPathByteStream& streamToAppendTo, const SVGPathByteStream& byStream, unsigned repeatCount)
150 {
151     // Why return when streamToAppendTo is empty? Don't we still need to append?
152     if (streamToAppendTo.isEmpty() || byStream.isEmpty())
153         return true;
154
155     // Is it OK to make the SVGPathByteStreamBuilder from a stream, and then clear that stream?
156     SVGPathByteStreamBuilder builder(streamToAppendTo);
157
158     SVGPathByteStream fromStreamCopy = streamToAppendTo;
159     streamToAppendTo.clear();
160
161     SVGPathByteStreamSource fromSource(fromStreamCopy);
162     SVGPathByteStreamSource bySource(byStream);
163     return SVGPathBlender::addAnimatedPath(fromSource, bySource, builder, repeatCount);
164 }
165
166 bool getSVGPathSegAtLengthFromSVGPathByteStream(const SVGPathByteStream& stream, float length, unsigned& pathSeg)
167 {
168     if (stream.isEmpty())
169         return false;
170
171     PathTraversalState traversalState(PathTraversalState::Action::SegmentAtLength);
172     SVGPathTraversalStateBuilder builder(traversalState, length);
173
174     SVGPathByteStreamSource source(stream);
175     bool ok = SVGPathParser::parse(source, builder);
176     pathSeg = builder.pathSegmentIndex();
177     return ok;
178 }
179
180 bool getTotalLengthOfSVGPathByteStream(const SVGPathByteStream& stream, float& totalLength)
181 {
182     if (stream.isEmpty())
183         return false;
184
185     PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
186
187     SVGPathTraversalStateBuilder builder(traversalState);
188
189     SVGPathByteStreamSource source(stream);
190     bool ok = SVGPathParser::parse(source, builder);
191     totalLength = builder.totalLength();
192     return ok;
193 }
194
195 bool getPointAtLengthOfSVGPathByteStream(const SVGPathByteStream& stream, float length, SVGPoint& point)
196 {
197     if (stream.isEmpty())
198         return false;
199
200     PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength);
201
202     SVGPathTraversalStateBuilder builder(traversalState, length);
203
204     SVGPathByteStreamSource source(stream);
205     bool ok = SVGPathParser::parse(source, builder);
206     point = builder.currentPoint();
207     return ok;
208 }
209
210 static void pathIteratorForBuildingString(SVGPathConsumer& consumer, const PathElement& pathElement)
211 {
212     switch (pathElement.type) {
213     case PathElementMoveToPoint:
214         consumer.moveTo(pathElement.points[0], false, AbsoluteCoordinates);
215         break;
216     case PathElementAddLineToPoint:
217         consumer.lineTo(pathElement.points[0], AbsoluteCoordinates);
218         break;
219     case PathElementAddQuadCurveToPoint:
220         consumer.curveToQuadratic(pathElement.points[0], pathElement.points[1], AbsoluteCoordinates);
221         break;
222     case PathElementAddCurveToPoint:
223         consumer.curveToCubic(pathElement.points[0], pathElement.points[1], pathElement.points[2], AbsoluteCoordinates);
224         break;
225     case PathElementCloseSubpath:
226         consumer.closePath();
227         break;
228
229     default:
230         ASSERT_NOT_REACHED();
231         break;
232     }
233 }
234
235 bool buildStringFromPath(const Path& path, String& string)
236 {
237     // Ideally we would have a SVGPathPlatformPathSource, but it's not possible to manually iterate
238     // a path, only apply a function to all path elements at once.
239
240     SVGPathStringBuilder builder;
241     path.apply([&builder](const PathElement& pathElement) {
242         pathIteratorForBuildingString(builder, pathElement);
243     });
244     string = builder.result();
245
246     return true;
247 }
248
249 }