OLD | NEW |
| (Empty) |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "config.h" | |
6 #include "core/animation/PathSVGInterpolation.h" | |
7 | |
8 #include "core/svg/SVGPathByteStreamBuilder.h" | |
9 #include "core/svg/SVGPathByteStreamSource.h" | |
10 #include "core/svg/SVGPathElement.h" | |
11 #include "core/svg/SVGPathParser.h" | |
12 | |
13 namespace blink { | |
14 | |
15 namespace { | |
16 | |
17 struct SubPathCoordinates { | |
18 double initialX = 0; | |
19 double initialY = 0; | |
20 double currentX = 0; | |
21 double currentY = 0; | |
22 }; | |
23 | |
24 PassOwnPtr<InterpolableNumber> controlToInterpolableValue(double value, bool isA
bsolute, double currentValue) | |
25 { | |
26 if (isAbsolute) | |
27 return InterpolableNumber::create(value); | |
28 return InterpolableNumber::create(currentValue + value); | |
29 } | |
30 | |
31 double controlFromInterpolableValue(const InterpolableValue* number, bool isAbso
lute, double currentValue) | |
32 { | |
33 double value = toInterpolableNumber(number)->value(); | |
34 | |
35 if (isAbsolute) | |
36 return value; | |
37 return value - currentValue; | |
38 } | |
39 | |
40 PassOwnPtr<InterpolableNumber> specifiedToInterpolableValue(double value, bool i
sAbsolute, double& currentValue) | |
41 { | |
42 if (isAbsolute) | |
43 currentValue = value; | |
44 else | |
45 currentValue += value; | |
46 return InterpolableNumber::create(currentValue); | |
47 } | |
48 | |
49 double specifiedFromInterpolableValue(const InterpolableValue* number, bool isAb
solute, double& currentValue) | |
50 { | |
51 double previousValue = currentValue; | |
52 currentValue = toInterpolableNumber(number)->value(); | |
53 | |
54 if (isAbsolute) | |
55 return currentValue; | |
56 return currentValue - previousValue; | |
57 } | |
58 | |
59 PassOwnPtr<InterpolableValue> pathSegClosePathToInterpolableValue(const PathSegm
entData&, SubPathCoordinates& coordinates) | |
60 { | |
61 coordinates.currentX = coordinates.initialX; | |
62 coordinates.currentY = coordinates.initialY; | |
63 | |
64 // arbitrary | |
65 return InterpolableBool::create(false); | |
66 } | |
67 | |
68 PathSegmentData pathSegClosePathFromInterpolableValue(const InterpolableValue&,
SVGPathSegType segType, SubPathCoordinates& coordinates) | |
69 { | |
70 coordinates.currentX = coordinates.initialX; | |
71 coordinates.currentY = coordinates.initialY; | |
72 | |
73 PathSegmentData segment; | |
74 segment.command = segType; | |
75 return segment; | |
76 } | |
77 | |
78 PassOwnPtr<InterpolableValue> pathSegSingleCoordinateToInterpolableValue(const P
athSegmentData& segment, SubPathCoordinates& coordinates) | |
79 { | |
80 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
81 OwnPtr<InterpolableList> result = InterpolableList::create(2); | |
82 result->set(0, specifiedToInterpolableValue(segment.x(), isAbsolute, coordin
ates.currentX)); | |
83 result->set(1, specifiedToInterpolableValue(segment.y(), isAbsolute, coordin
ates.currentY)); | |
84 | |
85 if (toAbsolutePathSegType(segment.command) == PathSegMoveToAbs) { | |
86 // Any upcoming 'closepath' commands bring us back to the location we ha
ve just moved to. | |
87 coordinates.initialX = coordinates.currentX; | |
88 coordinates.initialY = coordinates.currentY; | |
89 } | |
90 | |
91 return result.release(); | |
92 } | |
93 | |
94 PathSegmentData pathSegSingleCoordinateFromInterpolableValue(const InterpolableV
alue& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
95 { | |
96 const InterpolableList& list = toInterpolableList(value); | |
97 bool isAbsolute = isAbsolutePathSegType(segType); | |
98 PathSegmentData segment; | |
99 segment.command = segType; | |
100 segment.targetPoint.setX(specifiedFromInterpolableValue(list.get(0), isAbsol
ute, coordinates.currentX)); | |
101 segment.targetPoint.setY(specifiedFromInterpolableValue(list.get(1), isAbsol
ute, coordinates.currentY)); | |
102 | |
103 if (toAbsolutePathSegType(segType) == PathSegMoveToAbs) { | |
104 // Any upcoming 'closepath' commands bring us back to the location we ha
ve just moved to. | |
105 coordinates.initialX = coordinates.currentX; | |
106 coordinates.initialY = coordinates.currentY; | |
107 } | |
108 | |
109 return segment; | |
110 } | |
111 | |
112 PassOwnPtr<InterpolableValue> pathSegCurvetoCubicToInterpolableValue(const PathS
egmentData& segment, SubPathCoordinates& coordinates) | |
113 { | |
114 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
115 OwnPtr<InterpolableList> result = InterpolableList::create(6); | |
116 result->set(0, controlToInterpolableValue(segment.x1(), isAbsolute, coordina
tes.currentX)); | |
117 result->set(1, controlToInterpolableValue(segment.y1(), isAbsolute, coordina
tes.currentY)); | |
118 result->set(2, controlToInterpolableValue(segment.x2(), isAbsolute, coordina
tes.currentX)); | |
119 result->set(3, controlToInterpolableValue(segment.y2(), isAbsolute, coordina
tes.currentY)); | |
120 result->set(4, specifiedToInterpolableValue(segment.x(), isAbsolute, coordin
ates.currentX)); | |
121 result->set(5, specifiedToInterpolableValue(segment.y(), isAbsolute, coordin
ates.currentY)); | |
122 return result.release(); | |
123 } | |
124 | |
125 PathSegmentData pathSegCurvetoCubicFromInterpolableValue(const InterpolableValue
& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
126 { | |
127 const InterpolableList& list = toInterpolableList(value); | |
128 bool isAbsolute = isAbsolutePathSegType(segType); | |
129 PathSegmentData segment; | |
130 segment.command = segType; | |
131 segment.point1.setX(controlFromInterpolableValue(list.get(0), isAbsolute, co
ordinates.currentX)); | |
132 segment.point1.setY(controlFromInterpolableValue(list.get(1), isAbsolute, co
ordinates.currentY)); | |
133 segment.point2.setX(controlFromInterpolableValue(list.get(2), isAbsolute, co
ordinates.currentX)); | |
134 segment.point2.setY(controlFromInterpolableValue(list.get(3), isAbsolute, co
ordinates.currentY)); | |
135 segment.targetPoint.setX(specifiedFromInterpolableValue(list.get(4), isAbsol
ute, coordinates.currentX)); | |
136 segment.targetPoint.setY(specifiedFromInterpolableValue(list.get(5), isAbsol
ute, coordinates.currentY)); | |
137 return segment; | |
138 } | |
139 | |
140 PassOwnPtr<InterpolableValue> pathSegCurvetoQuadraticToInterpolableValue(const P
athSegmentData& segment, SubPathCoordinates& coordinates) | |
141 { | |
142 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
143 OwnPtr<InterpolableList> result = InterpolableList::create(4); | |
144 result->set(0, controlToInterpolableValue(segment.x1(), isAbsolute, coordina
tes.currentX)); | |
145 result->set(1, controlToInterpolableValue(segment.y1(), isAbsolute, coordina
tes.currentY)); | |
146 result->set(2, specifiedToInterpolableValue(segment.x(), isAbsolute, coordin
ates.currentX)); | |
147 result->set(3, specifiedToInterpolableValue(segment.y(), isAbsolute, coordin
ates.currentY)); | |
148 return result.release(); | |
149 } | |
150 | |
151 PathSegmentData pathSegCurvetoQuadraticFromInterpolableValue(const InterpolableV
alue& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
152 { | |
153 const InterpolableList& list = toInterpolableList(value); | |
154 bool isAbsolute = isAbsolutePathSegType(segType); | |
155 PathSegmentData segment; | |
156 segment.command = segType; | |
157 segment.point1.setX(controlFromInterpolableValue(list.get(0), isAbsolute, co
ordinates.currentX)); | |
158 segment.point1.setY(controlFromInterpolableValue(list.get(1), isAbsolute, co
ordinates.currentY)); | |
159 segment.targetPoint.setX(specifiedFromInterpolableValue(list.get(2), isAbsol
ute, coordinates.currentX)); | |
160 segment.targetPoint.setY(specifiedFromInterpolableValue(list.get(3), isAbsol
ute, coordinates.currentY)); | |
161 return segment; | |
162 } | |
163 | |
164 PassOwnPtr<InterpolableValue> pathSegArcToInterpolableValue(const PathSegmentDat
a& segment, SubPathCoordinates& coordinates) | |
165 { | |
166 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
167 OwnPtr<InterpolableList> result = InterpolableList::create(7); | |
168 result->set(0, specifiedToInterpolableValue(segment.x(), isAbsolute, coordin
ates.currentX)); | |
169 result->set(1, specifiedToInterpolableValue(segment.y(), isAbsolute, coordin
ates.currentY)); | |
170 result->set(2, InterpolableNumber::create(segment.r1())); | |
171 result->set(3, InterpolableNumber::create(segment.r2())); | |
172 result->set(4, InterpolableNumber::create(segment.arcAngle())); | |
173 result->set(5, InterpolableBool::create(segment.largeArcFlag())); | |
174 result->set(6, InterpolableBool::create(segment.sweepFlag())); | |
175 return result.release(); | |
176 } | |
177 | |
178 PathSegmentData pathSegArcFromInterpolableValue(const InterpolableValue& value,
SVGPathSegType segType, SubPathCoordinates& coordinates) | |
179 { | |
180 const InterpolableList& list = toInterpolableList(value); | |
181 bool isAbsolute = isAbsolutePathSegType(segType); | |
182 PathSegmentData segment; | |
183 segment.command = segType; | |
184 segment.targetPoint.setX(specifiedFromInterpolableValue(list.get(0), isAbsol
ute, coordinates.currentX)); | |
185 segment.targetPoint.setY(specifiedFromInterpolableValue(list.get(1), isAbsol
ute, coordinates.currentY)); | |
186 segment.arcRadii().setX(toInterpolableNumber(list.get(2))->value()); | |
187 segment.arcRadii().setY(toInterpolableNumber(list.get(3))->value()); | |
188 segment.setArcAngle(toInterpolableNumber(list.get(4))->value()); | |
189 segment.arcLarge = toInterpolableBool(list.get(5))->value(); | |
190 segment.arcSweep = toInterpolableBool(list.get(6))->value(); | |
191 return segment; | |
192 } | |
193 | |
194 PassOwnPtr<InterpolableValue> pathSegLinetoHorizontalToInterpolableValue(const P
athSegmentData& segment, SubPathCoordinates& coordinates) | |
195 { | |
196 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
197 return specifiedToInterpolableValue(segment.x(), isAbsolute, coordinates.cur
rentX); | |
198 } | |
199 | |
200 PathSegmentData pathSegLinetoHorizontalFromInterpolableValue(const InterpolableV
alue& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
201 { | |
202 bool isAbsolute = isAbsolutePathSegType(segType); | |
203 PathSegmentData segment; | |
204 segment.command = segType; | |
205 segment.targetPoint.setX(specifiedFromInterpolableValue(&value, isAbsolute,
coordinates.currentX)); | |
206 return segment; | |
207 } | |
208 | |
209 PassOwnPtr<InterpolableValue> pathSegLinetoVerticalToInterpolableValue(const Pat
hSegmentData& segment, SubPathCoordinates& coordinates) | |
210 { | |
211 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
212 return specifiedToInterpolableValue(segment.y(), isAbsolute, coordinates.cur
rentY); | |
213 } | |
214 | |
215 PathSegmentData pathSegLinetoVerticalFromInterpolableValue(const InterpolableVal
ue& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
216 { | |
217 bool isAbsolute = isAbsolutePathSegType(segType); | |
218 PathSegmentData segment; | |
219 segment.command = segType; | |
220 segment.targetPoint.setY(specifiedFromInterpolableValue(&value, isAbsolute,
coordinates.currentY)); | |
221 return segment; | |
222 } | |
223 | |
224 PassOwnPtr<InterpolableValue> pathSegCurvetoCubicSmoothToInterpolableValue(const
PathSegmentData& segment, SubPathCoordinates& coordinates) | |
225 { | |
226 bool isAbsolute = isAbsolutePathSegType(segment.command); | |
227 OwnPtr<InterpolableList> result = InterpolableList::create(4); | |
228 result->set(0, controlToInterpolableValue(segment.x2(), isAbsolute, coordina
tes.currentX)); | |
229 result->set(1, controlToInterpolableValue(segment.y2(), isAbsolute, coordina
tes.currentY)); | |
230 result->set(2, specifiedToInterpolableValue(segment.x(), isAbsolute, coordin
ates.currentX)); | |
231 result->set(3, specifiedToInterpolableValue(segment.y(), isAbsolute, coordin
ates.currentY)); | |
232 return result.release(); | |
233 } | |
234 | |
235 PathSegmentData pathSegCurvetoCubicSmoothFromInterpolableValue(const Interpolabl
eValue& value, SVGPathSegType segType, SubPathCoordinates& coordinates) | |
236 { | |
237 const InterpolableList& list = toInterpolableList(value); | |
238 bool isAbsolute = isAbsolutePathSegType(segType); | |
239 PathSegmentData segment; | |
240 segment.command = segType; | |
241 segment.point2.setX(controlFromInterpolableValue(list.get(0), isAbsolute, co
ordinates.currentX)); | |
242 segment.point2.setY(controlFromInterpolableValue(list.get(1), isAbsolute, co
ordinates.currentY)); | |
243 segment.targetPoint.setX(specifiedFromInterpolableValue(list.get(2), isAbsol
ute, coordinates.currentX)); | |
244 segment.targetPoint.setY(specifiedFromInterpolableValue(list.get(3), isAbsol
ute, coordinates.currentY)); | |
245 return segment; | |
246 } | |
247 | |
248 PassOwnPtr<InterpolableValue> pathSegToInterpolableValue(const PathSegmentData&
segment, SubPathCoordinates& coordinates, SVGPathSegType* ptrSegType) | |
249 { | |
250 if (ptrSegType) | |
251 *ptrSegType = segment.command; | |
252 | |
253 switch (segment.command) { | |
254 case PathSegClosePath: | |
255 return pathSegClosePathToInterpolableValue(segment, coordinates); | |
256 | |
257 case PathSegMoveToAbs: | |
258 case PathSegMoveToRel: | |
259 case PathSegLineToAbs: | |
260 case PathSegLineToRel: | |
261 case PathSegCurveToQuadraticSmoothAbs: | |
262 case PathSegCurveToQuadraticSmoothRel: | |
263 return pathSegSingleCoordinateToInterpolableValue(segment, coordinates); | |
264 | |
265 case PathSegCurveToCubicAbs: | |
266 case PathSegCurveToCubicRel: | |
267 return pathSegCurvetoCubicToInterpolableValue(segment, coordinates); | |
268 | |
269 case PathSegCurveToQuadraticAbs: | |
270 case PathSegCurveToQuadraticRel: | |
271 return pathSegCurvetoQuadraticToInterpolableValue(segment, coordinates); | |
272 | |
273 case PathSegArcAbs: | |
274 case PathSegArcRel: | |
275 return pathSegArcToInterpolableValue(segment, coordinates); | |
276 | |
277 case PathSegLineToHorizontalAbs: | |
278 case PathSegLineToHorizontalRel: | |
279 return pathSegLinetoHorizontalToInterpolableValue(segment, coordinates); | |
280 | |
281 case PathSegLineToVerticalAbs: | |
282 case PathSegLineToVerticalRel: | |
283 return pathSegLinetoVerticalToInterpolableValue(segment, coordinates); | |
284 | |
285 case PathSegCurveToCubicSmoothAbs: | |
286 case PathSegCurveToCubicSmoothRel: | |
287 return pathSegCurvetoCubicSmoothToInterpolableValue(segment, coordinates
); | |
288 | |
289 case PathSegUnknown: | |
290 ASSERT_NOT_REACHED(); | |
291 } | |
292 ASSERT_NOT_REACHED(); | |
293 return nullptr; | |
294 } | |
295 | |
296 PathSegmentData pathSegFromInterpolableValue(const InterpolableValue& value, SVG
PathSegType segType, SubPathCoordinates& coordinates) | |
297 { | |
298 switch (segType) { | |
299 case PathSegClosePath: | |
300 return pathSegClosePathFromInterpolableValue(value, segType, coordinates
); | |
301 | |
302 case PathSegMoveToAbs: | |
303 case PathSegMoveToRel: | |
304 case PathSegLineToAbs: | |
305 case PathSegLineToRel: | |
306 case PathSegCurveToQuadraticSmoothAbs: | |
307 case PathSegCurveToQuadraticSmoothRel: | |
308 return pathSegSingleCoordinateFromInterpolableValue(value, segType, coor
dinates); | |
309 | |
310 case PathSegCurveToCubicAbs: | |
311 case PathSegCurveToCubicRel: | |
312 return pathSegCurvetoCubicFromInterpolableValue(value, segType, coordina
tes); | |
313 | |
314 case PathSegCurveToQuadraticAbs: | |
315 case PathSegCurveToQuadraticRel: | |
316 return pathSegCurvetoQuadraticFromInterpolableValue(value, segType, coor
dinates); | |
317 | |
318 case PathSegArcAbs: | |
319 case PathSegArcRel: | |
320 return pathSegArcFromInterpolableValue(value, segType, coordinates); | |
321 | |
322 case PathSegLineToHorizontalAbs: | |
323 case PathSegLineToHorizontalRel: | |
324 return pathSegLinetoHorizontalFromInterpolableValue(value, segType, coor
dinates); | |
325 | |
326 case PathSegLineToVerticalAbs: | |
327 case PathSegLineToVerticalRel: | |
328 return pathSegLinetoVerticalFromInterpolableValue(value, segType, coordi
nates); | |
329 | |
330 case PathSegCurveToCubicSmoothAbs: | |
331 case PathSegCurveToCubicSmoothRel: | |
332 return pathSegCurvetoCubicSmoothFromInterpolableValue(value, segType, co
ordinates); | |
333 | |
334 case PathSegUnknown: | |
335 ASSERT_NOT_REACHED(); | |
336 } | |
337 ASSERT_NOT_REACHED(); | |
338 return PathSegmentData(); | |
339 } | |
340 | |
341 class InterpolatedPathSource : public SVGPathSource { | |
342 public: | |
343 InterpolatedPathSource(const InterpolableList& listValue, const Vector<SVGPa
thSegType>& pathSegTypes) | |
344 : m_currentIndex(0) | |
345 , m_list(listValue) | |
346 , m_segmentTypes(pathSegTypes) | |
347 { | |
348 ASSERT(m_list.length() == m_segmentTypes.size()); | |
349 } | |
350 | |
351 private: | |
352 bool hasMoreData() const override; | |
353 SVGPathSegType peekSegmentType() override; | |
354 PathSegmentData parseSegment() override; | |
355 | |
356 SubPathCoordinates m_normalizationState; | |
357 size_t m_currentIndex; | |
358 const InterpolableList& m_list; | |
359 const Vector<SVGPathSegType>& m_segmentTypes; | |
360 }; | |
361 | |
362 bool InterpolatedPathSource::hasMoreData() const | |
363 { | |
364 return m_currentIndex < m_list.length(); | |
365 } | |
366 | |
367 SVGPathSegType InterpolatedPathSource::peekSegmentType() | |
368 { | |
369 ASSERT(hasMoreData()); | |
370 return m_segmentTypes.at(m_currentIndex); | |
371 } | |
372 | |
373 PathSegmentData InterpolatedPathSource::parseSegment() | |
374 { | |
375 PathSegmentData segment = pathSegFromInterpolableValue(*m_list.get(m_current
Index), m_segmentTypes.at(m_currentIndex), m_normalizationState); | |
376 ++m_currentIndex; | |
377 return segment; | |
378 } | |
379 | |
380 size_t countPathCommands(const SVGPathByteStream& path) | |
381 { | |
382 size_t count = 0; | |
383 SVGPathByteStreamSource pathSource(path); | |
384 while (pathSource.hasMoreData()) { | |
385 pathSource.parseSegment(); | |
386 ++count; | |
387 } | |
388 return count; | |
389 } | |
390 | |
391 } // namespace | |
392 | |
393 PassRefPtr<PathSVGInterpolation> PathSVGInterpolation::maybeCreate(SVGPropertyBa
se* start, SVGPropertyBase* end, PassRefPtrWillBeRawPtr<SVGAnimatedPropertyBase>
attribute) | |
394 { | |
395 ASSERT(start->type() == SVGPath::classType()); | |
396 ASSERT(end->type() == SVGPath::classType()); | |
397 | |
398 const SVGPathByteStream& startPath = static_cast<SVGPath*>(start)->byteStrea
m(); | |
399 const SVGPathByteStream& endPath = static_cast<SVGPath*>(end)->byteStream(); | |
400 | |
401 if (startPath.size() != endPath.size()) | |
402 return nullptr; | |
403 | |
404 size_t length = countPathCommands(startPath); | |
405 | |
406 SVGPathByteStreamSource startPathSource(startPath); | |
407 SVGPathByteStreamSource endPathSource(endPath); | |
408 | |
409 Vector<SVGPathSegType> pathSegTypes(length); | |
410 OwnPtr<InterpolableList> startValue = InterpolableList::create(length); | |
411 OwnPtr<InterpolableList> endValue = InterpolableList::create(length); | |
412 SubPathCoordinates startCoordinates; | |
413 SubPathCoordinates endCoordinates; | |
414 size_t i = 0; | |
415 while (startPathSource.hasMoreData()) { | |
416 if (toAbsolutePathSegType(startPathSource.peekSegmentType()) != toAbsolu
tePathSegType(endPathSource.peekSegmentType())) | |
417 return nullptr; | |
418 | |
419 // Like Firefox SMIL, we use the final path seg type. | |
420 const PathSegmentData startSeg = startPathSource.parseSegment(); | |
421 startValue->set(i, pathSegToInterpolableValue(startSeg, startCoordinates
, nullptr)); | |
422 | |
423 const PathSegmentData endSeg = endPathSource.parseSegment(); | |
424 endValue->set(i, pathSegToInterpolableValue(endSeg, endCoordinates, &pat
hSegTypes.at(i))); | |
425 | |
426 ++i; | |
427 } | |
428 ASSERT(!endPathSource.hasMoreData()); | |
429 ASSERT(i == length); | |
430 | |
431 return adoptRef(new PathSVGInterpolation(startValue.release(), endValue.rele
ase(), attribute, pathSegTypes)); | |
432 } | |
433 | |
434 PassRefPtrWillBeRawPtr<SVGPropertyBase> PathSVGInterpolation::fromInterpolableVa
lue(const InterpolableValue& value, const Vector<SVGPathSegType>& pathSegTypes) | |
435 { | |
436 RefPtrWillBeRawPtr<SVGPath> result = SVGPath::create(); | |
437 InterpolatedPathSource source(toInterpolableList(value), pathSegTypes); | |
438 SVGPathByteStreamBuilder builder(result->mutableByteStream()); | |
439 SVGPathParser parser(&source, &builder); | |
440 parser.parsePathDataFromSource(UnalteredParsing, false); | |
441 return result.release(); | |
442 } | |
443 | |
444 PassRefPtrWillBeRawPtr<SVGPropertyBase> PathSVGInterpolation::interpolatedValue(
SVGElement&) const | |
445 { | |
446 return fromInterpolableValue(*m_cachedValue, m_pathSegTypes); | |
447 } | |
448 | |
449 } | |
OLD | NEW |