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 "FloatPoint.h" |
25 | #include "Path.h" |
26 | #include "PathTraversalState.h" |
27 | #include "SVGPathBlender.h" |
28 | #include "SVGPathBuilder.h" |
29 | #include "SVGPathByteStreamBuilder.h" |
30 | #include "SVGPathByteStreamSource.h" |
31 | #include "SVGPathConsumer.h" |
32 | #include "SVGPathElement.h" |
33 | #include "SVGPathParser.h" |
34 | #include "SVGPathSegListBuilder.h" |
35 | #include "SVGPathSegListSource.h" |
36 | #include "SVGPathStringBuilder.h" |
37 | #include "SVGPathStringSource.h" |
38 | #include "SVGPathTraversalStateBuilder.h" |
39 | |
40 | namespace WebCore { |
41 | |
42 | Path buildPathFromString(const String& d) |
43 | { |
44 | if (d.isEmpty()) |
45 | return { }; |
46 | |
47 | Path path; |
48 | SVGPathBuilder builder(path); |
49 | SVGPathStringSource source(d); |
50 | SVGPathParser::parse(source, builder); |
51 | return path; |
52 | } |
53 | |
54 | String buildStringFromPath(const Path& path) |
55 | { |
56 | StringBuilder builder; |
57 | |
58 | if (!path.isNull() && !path.isEmpty()) { |
59 | path.apply([&builder] (const PathElement& element) { |
60 | switch (element.type) { |
61 | case PathElementMoveToPoint: |
62 | builder.append('M'); |
63 | builder.appendShortestFormNumber(element.points[0].x()); |
64 | builder.append(' '); |
65 | builder.appendShortestFormNumber(element.points[0].y()); |
66 | break; |
67 | case PathElementAddLineToPoint: |
68 | builder.append('L'); |
69 | builder.appendShortestFormNumber(element.points[0].x()); |
70 | builder.append(' '); |
71 | builder.appendShortestFormNumber(element.points[0].y()); |
72 | break; |
73 | case PathElementAddQuadCurveToPoint: |
74 | builder.append('Q'); |
75 | builder.appendShortestFormNumber(element.points[0].x()); |
76 | builder.append(' '); |
77 | builder.appendShortestFormNumber(element.points[0].y()); |
78 | builder.append(','); |
79 | builder.appendShortestFormNumber(element.points[1].x()); |
80 | builder.append(' '); |
81 | builder.appendShortestFormNumber(element.points[1].y()); |
82 | break; |
83 | case PathElementAddCurveToPoint: |
84 | builder.append('C'); |
85 | builder.appendShortestFormNumber(element.points[0].x()); |
86 | builder.append(' '); |
87 | builder.appendShortestFormNumber(element.points[0].y()); |
88 | builder.append(','); |
89 | builder.appendShortestFormNumber(element.points[1].x()); |
90 | builder.append(' '); |
91 | builder.appendShortestFormNumber(element.points[1].y()); |
92 | builder.append(','); |
93 | builder.appendShortestFormNumber(element.points[2].x()); |
94 | builder.append(' '); |
95 | builder.appendShortestFormNumber(element.points[2].y()); |
96 | break; |
97 | case PathElementCloseSubpath: |
98 | builder.append('Z'); |
99 | break; |
100 | } |
101 | }); |
102 | } |
103 | |
104 | return builder.toString(); |
105 | } |
106 | |
107 | bool buildSVGPathByteStreamFromSVGPathSegList(const SVGPathSegList& list, SVGPathByteStream& stream, PathParsingMode parsingMode, bool checkForInitialMoveTo) |
108 | { |
109 | stream.clear(); |
110 | if (list.isEmpty()) |
111 | return true; |
112 | |
113 | SVGPathSegListSource source(list); |
114 | return SVGPathParser::parseToByteStream(source, stream, parsingMode, checkForInitialMoveTo); |
115 | } |
116 | |
117 | Path buildPathFromByteStream(const SVGPathByteStream& stream) |
118 | { |
119 | if (stream.isEmpty()) |
120 | return { }; |
121 | |
122 | Path path; |
123 | SVGPathBuilder builder(path); |
124 | SVGPathByteStreamSource source(stream); |
125 | SVGPathParser::parse(source, builder); |
126 | return path; |
127 | } |
128 | |
129 | bool buildSVGPathSegListFromByteStream(const SVGPathByteStream& stream, SVGPathSegList& list, PathParsingMode mode) |
130 | { |
131 | if (stream.isEmpty()) |
132 | return true; |
133 | |
134 | SVGPathSegListBuilder builder(list); |
135 | SVGPathByteStreamSource source(stream); |
136 | return SVGPathParser::parse(source, builder, mode); |
137 | } |
138 | |
139 | bool buildStringFromByteStream(const SVGPathByteStream& stream, String& result, PathParsingMode parsingMode, bool checkForInitialMoveTo) |
140 | { |
141 | if (stream.isEmpty()) |
142 | return true; |
143 | |
144 | SVGPathByteStreamSource source(stream); |
145 | return SVGPathParser::parseToString(source, result, parsingMode, checkForInitialMoveTo); |
146 | } |
147 | |
148 | bool buildSVGPathByteStreamFromString(const String& d, SVGPathByteStream& result, PathParsingMode parsingMode) |
149 | { |
150 | result.clear(); |
151 | if (d.isEmpty()) |
152 | return true; |
153 | |
154 | SVGPathStringSource source(d); |
155 | return SVGPathParser::parseToByteStream(source, result, parsingMode); |
156 | } |
157 | |
158 | bool canBlendSVGPathByteStreams(const SVGPathByteStream& fromStream, const SVGPathByteStream& toStream) |
159 | { |
160 | SVGPathByteStreamSource fromSource(fromStream); |
161 | SVGPathByteStreamSource toSource(toStream); |
162 | return SVGPathBlender::canBlendPaths(fromSource, toSource); |
163 | } |
164 | |
165 | bool buildAnimatedSVGPathByteStream(const SVGPathByteStream& fromStream, const SVGPathByteStream& toStream, SVGPathByteStream& result, float progress) |
166 | { |
167 | ASSERT(&toStream != &result); |
168 | result.clear(); |
169 | if (toStream.isEmpty()) |
170 | return true; |
171 | |
172 | SVGPathByteStreamBuilder builder(result); |
173 | |
174 | SVGPathByteStreamSource fromSource(fromStream); |
175 | SVGPathByteStreamSource toSource(toStream); |
176 | return SVGPathBlender::blendAnimatedPath(fromSource, toSource, builder, progress); |
177 | } |
178 | |
179 | bool addToSVGPathByteStream(SVGPathByteStream& streamToAppendTo, const SVGPathByteStream& byStream, unsigned repeatCount) |
180 | { |
181 | // The byStream will be blended with streamToAppendTo. So streamToAppendTo has to have elements. |
182 | if (streamToAppendTo.isEmpty() || byStream.isEmpty()) |
183 | return true; |
184 | |
185 | // builder is the destination of blending fromSource and bySource. The stream of builder |
186 | // (i.e. streamToAppendTo) has to be cleared before calling addAnimatedPath. |
187 | SVGPathByteStreamBuilder builder(streamToAppendTo); |
188 | |
189 | SVGPathByteStream fromStreamCopy = WTFMove(streamToAppendTo); |
190 | |
191 | SVGPathByteStreamSource fromSource(fromStreamCopy); |
192 | SVGPathByteStreamSource bySource(byStream); |
193 | return SVGPathBlender::addAnimatedPath(fromSource, bySource, builder, repeatCount); |
194 | } |
195 | |
196 | bool getSVGPathSegAtLengthFromSVGPathByteStream(const SVGPathByteStream& stream, float length, unsigned& pathSeg) |
197 | { |
198 | if (stream.isEmpty()) |
199 | return false; |
200 | |
201 | PathTraversalState traversalState(PathTraversalState::Action::SegmentAtLength); |
202 | SVGPathTraversalStateBuilder builder(traversalState, length); |
203 | |
204 | SVGPathByteStreamSource source(stream); |
205 | bool ok = SVGPathParser::parse(source, builder); |
206 | pathSeg = builder.pathSegmentIndex(); |
207 | return ok; |
208 | } |
209 | |
210 | bool getTotalLengthOfSVGPathByteStream(const SVGPathByteStream& stream, float& totalLength) |
211 | { |
212 | if (stream.isEmpty()) |
213 | return false; |
214 | |
215 | PathTraversalState traversalState(PathTraversalState::Action::TotalLength); |
216 | |
217 | SVGPathTraversalStateBuilder builder(traversalState); |
218 | |
219 | SVGPathByteStreamSource source(stream); |
220 | bool ok = SVGPathParser::parse(source, builder); |
221 | totalLength = builder.totalLength(); |
222 | return ok; |
223 | } |
224 | |
225 | bool getPointAtLengthOfSVGPathByteStream(const SVGPathByteStream& stream, float length, FloatPoint& point) |
226 | { |
227 | if (stream.isEmpty()) |
228 | return false; |
229 | |
230 | PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength); |
231 | |
232 | SVGPathTraversalStateBuilder builder(traversalState, length); |
233 | |
234 | SVGPathByteStreamSource source(stream); |
235 | bool ok = SVGPathParser::parse(source, builder); |
236 | point = builder.currentPoint(); |
237 | return ok; |
238 | } |
239 | |
240 | } |
241 | |