1 /*
2 * Copyright (C) Research In Motion Limited 2010, 2011. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "core/svg/SVGPathBlender.h"
22
23 #include "core/svg/SVGPathSeg.h"
24 #include "core/svg/SVGPathSource.h"
25 #include "platform/animation/AnimationUtilities.h"
26 #include "wtf/TemporaryChange.h"
27
28 namespace WebCore {
29
SVGPathBlender()30 SVGPathBlender::SVGPathBlender()
31 : m_fromSource(0)
32 , m_toSource(0)
33 , m_consumer(0)
34 , m_progress(0)
35 , m_addTypesCount(0)
36 , m_isInFirstHalfOfAnimation(false)
37 {
38 }
39
40 // Helper functions
blendFloatPoint(const FloatPoint & a,const FloatPoint & b,float progress)41 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint& b, float progress)
42 {
43 return FloatPoint(blend(a.x(), b.x(), progress), blend(a.y(), b.y(), progress));
44 }
45
blendAnimatedDimensonalFloat(float from,float to,FloatBlendMode blendMode)46 float SVGPathBlender::blendAnimatedDimensonalFloat(float from, float to, FloatBlendMode blendMode)
47 {
48 if (m_addTypesCount) {
49 ASSERT(m_fromMode == m_toMode);
50 return from + to * m_addTypesCount;
51 }
52
53 if (m_fromMode == m_toMode)
54 return blend(from, to, m_progress);
55
56 float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_fromCurrentPoint.y();
57 float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCurrentPoint.y();
58
59 // Transform toY to the coordinate mode of fromY
60 float animValue = blend(from, m_fromMode == AbsoluteCoordinates ? to + toValue : to - toValue, m_progress);
61
62 if (m_isInFirstHalfOfAnimation)
63 return animValue;
64
65 // Transform the animated point to the coordinate mode, needed for the current progress.
66 float currentValue = blend(fromValue, toValue, m_progress);
67 return m_toMode == AbsoluteCoordinates ? animValue + currentValue : animValue - currentValue;
68 }
69
blendAnimatedFloatPoint(const FloatPoint & fromPoint,const FloatPoint & toPoint)70 FloatPoint SVGPathBlender::blendAnimatedFloatPoint(const FloatPoint& fromPoint, const FloatPoint& toPoint)
71 {
72 if (m_addTypesCount) {
73 ASSERT(m_fromMode == m_toMode);
74 FloatPoint repeatedToPoint = toPoint;
75 repeatedToPoint.scale(m_addTypesCount, m_addTypesCount);
76 return fromPoint + repeatedToPoint;
77 }
78
79 if (m_fromMode == m_toMode)
80 return blendFloatPoint(fromPoint, toPoint, m_progress);
81
82 // Transform toPoint to the coordinate mode of fromPoint
83 FloatPoint animatedPoint = toPoint;
84 if (m_fromMode == AbsoluteCoordinates)
85 animatedPoint += m_toCurrentPoint;
86 else
87 animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y());
88
89 animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress);
90
91 if (m_isInFirstHalfOfAnimation)
92 return animatedPoint;
93
94 // Transform the animated point to the coordinate mode, needed for the current progress.
95 FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoint, m_progress);
96 if (m_toMode == AbsoluteCoordinates)
97 return animatedPoint + currentPoint;
98
99 animatedPoint.move(-currentPoint.x(), -currentPoint.y());
100 return animatedPoint;
101 }
102
blendMoveToSegment()103 bool SVGPathBlender::blendMoveToSegment()
104 {
105 FloatPoint fromTargetPoint;
106 FloatPoint toTargetPoint;
107 if ((m_fromSource->hasMoreData() && !m_fromSource->parseMoveToSegment(fromTargetPoint))
108 || !m_toSource->parseMoveToSegment(toTargetPoint))
109 return false;
110
111 m_consumer->moveTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), false, m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
112 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
113 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
114 return true;
115 }
116
blendLineToSegment()117 bool SVGPathBlender::blendLineToSegment()
118 {
119 FloatPoint fromTargetPoint;
120 FloatPoint toTargetPoint;
121 if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToSegment(fromTargetPoint))
122 || !m_toSource->parseLineToSegment(toTargetPoint))
123 return false;
124
125 m_consumer->lineTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
126 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
127 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
128 return true;
129 }
130
blendLineToHorizontalSegment()131 bool SVGPathBlender::blendLineToHorizontalSegment()
132 {
133 float fromX = 0;
134 float toX = 0;
135 if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToHorizontalSegment(fromX))
136 || !m_toSource->parseLineToHorizontalSegment(toX))
137 return false;
138
139 m_consumer->lineToHorizontal(blendAnimatedDimensonalFloat(fromX, toX, BlendHorizontal), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
140 m_fromCurrentPoint.setX(m_fromMode == AbsoluteCoordinates ? fromX : m_fromCurrentPoint.x() + fromX);
141 m_toCurrentPoint.setX(m_toMode == AbsoluteCoordinates ? toX : m_toCurrentPoint.x() + toX);
142 return true;
143 }
144
blendLineToVerticalSegment()145 bool SVGPathBlender::blendLineToVerticalSegment()
146 {
147 float fromY = 0;
148 float toY = 0;
149 if ((m_fromSource->hasMoreData() && !m_fromSource->parseLineToVerticalSegment(fromY))
150 || !m_toSource->parseLineToVerticalSegment(toY))
151 return false;
152
153 m_consumer->lineToVertical(blendAnimatedDimensonalFloat(fromY, toY, BlendVertical), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
154 m_fromCurrentPoint.setY(m_fromMode == AbsoluteCoordinates ? fromY : m_fromCurrentPoint.y() + fromY);
155 m_toCurrentPoint.setY(m_toMode == AbsoluteCoordinates ? toY : m_toCurrentPoint.y() + toY);
156 return true;
157 }
158
blendCurveToCubicSegment()159 bool SVGPathBlender::blendCurveToCubicSegment()
160 {
161 FloatPoint fromTargetPoint;
162 FloatPoint fromPoint1;
163 FloatPoint fromPoint2;
164 FloatPoint toTargetPoint;
165 FloatPoint toPoint1;
166 FloatPoint toPoint2;
167 if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToCubicSegment(fromPoint1, fromPoint2, fromTargetPoint))
168 || !m_toSource->parseCurveToCubicSegment(toPoint1, toPoint2, toTargetPoint))
169 return false;
170
171 m_consumer->curveToCubic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
172 blendAnimatedFloatPoint(fromPoint2, toPoint2),
173 blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
174 m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
175 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
176 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
177 return true;
178 }
179
blendCurveToCubicSmoothSegment()180 bool SVGPathBlender::blendCurveToCubicSmoothSegment()
181 {
182 FloatPoint fromTargetPoint;
183 FloatPoint fromPoint2;
184 FloatPoint toTargetPoint;
185 FloatPoint toPoint2;
186 if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToCubicSmoothSegment(fromPoint2, fromTargetPoint))
187 || !m_toSource->parseCurveToCubicSmoothSegment(toPoint2, toTargetPoint))
188 return false;
189
190 m_consumer->curveToCubicSmooth(blendAnimatedFloatPoint(fromPoint2, toPoint2),
191 blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
192 m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
193 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
194 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
195 return true;
196 }
197
blendCurveToQuadraticSegment()198 bool SVGPathBlender::blendCurveToQuadraticSegment()
199 {
200 FloatPoint fromTargetPoint;
201 FloatPoint fromPoint1;
202 FloatPoint toTargetPoint;
203 FloatPoint toPoint1;
204 if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToQuadraticSegment(fromPoint1, fromTargetPoint))
205 || !m_toSource->parseCurveToQuadraticSegment(toPoint1, toTargetPoint))
206 return false;
207
208 m_consumer->curveToQuadratic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
209 blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
210 m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
211 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
212 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
213 return true;
214 }
215
blendCurveToQuadraticSmoothSegment()216 bool SVGPathBlender::blendCurveToQuadraticSmoothSegment()
217 {
218 FloatPoint fromTargetPoint;
219 FloatPoint toTargetPoint;
220 if ((m_fromSource->hasMoreData() && !m_fromSource->parseCurveToQuadraticSmoothSegment(fromTargetPoint))
221 || !m_toSource->parseCurveToQuadraticSmoothSegment(toTargetPoint))
222 return false;
223
224 m_consumer->curveToQuadraticSmooth(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
225 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
226 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
227 return true;
228 }
229
blendArcToSegment()230 bool SVGPathBlender::blendArcToSegment()
231 {
232 float fromRx = 0;
233 float fromRy = 0;
234 float fromAngle = 0;
235 bool fromLargeArc = false;
236 bool fromSweep = false;
237 FloatPoint fromTargetPoint;
238 float toRx = 0;
239 float toRy = 0;
240 float toAngle = 0;
241 bool toLargeArc = false;
242 bool toSweep = false;
243 FloatPoint toTargetPoint;
244 if ((m_fromSource->hasMoreData() && !m_fromSource->parseArcToSegment(fromRx, fromRy, fromAngle, fromLargeArc, fromSweep, fromTargetPoint))
245 || !m_toSource->parseArcToSegment(toRx, toRy, toAngle, toLargeArc, toSweep, toTargetPoint))
246 return false;
247
248 if (m_addTypesCount) {
249 ASSERT(m_fromMode == m_toMode);
250 FloatPoint scaledToTargetPoint = toTargetPoint;
251 scaledToTargetPoint.scale(m_addTypesCount, m_addTypesCount);
252 m_consumer->arcTo(fromRx + toRx * m_addTypesCount,
253 fromRy + toRy * m_addTypesCount,
254 fromAngle + toAngle * m_addTypesCount,
255 fromLargeArc || toLargeArc,
256 fromSweep || toSweep,
257 fromTargetPoint + scaledToTargetPoint,
258 m_fromMode);
259 } else {
260 m_consumer->arcTo(blend(fromRx, toRx, m_progress),
261 blend(fromRy, toRy, m_progress),
262 blend(fromAngle, toAngle, m_progress),
263 m_isInFirstHalfOfAnimation ? fromLargeArc : toLargeArc,
264 m_isInFirstHalfOfAnimation ? fromSweep : toSweep,
265 blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
266 m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
267 }
268 m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
269 m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
270 return true;
271 }
272
coordinateModeOfCommand(const SVGPathSegType & type)273 static inline PathCoordinateMode coordinateModeOfCommand(const SVGPathSegType& type)
274 {
275 if (type < PathSegMoveToAbs)
276 return AbsoluteCoordinates;
277
278 // Odd number = relative command
279 if (type % 2)
280 return RelativeCoordinates;
281
282 return AbsoluteCoordinates;
283 }
284
isSegmentEqual(const SVGPathSegType & fromType,const SVGPathSegType & toType,const PathCoordinateMode & fromMode,const PathCoordinateMode & toMode)285 static inline bool isSegmentEqual(const SVGPathSegType& fromType, const SVGPathSegType& toType, const PathCoordinateMode& fromMode, const PathCoordinateMode& toMode)
286 {
287 if (fromType == toType && (fromType == PathSegUnknown || fromType == PathSegClosePath))
288 return true;
289
290 unsigned short from = fromType;
291 unsigned short to = toType;
292 if (fromMode == toMode)
293 return from == to;
294 if (fromMode == AbsoluteCoordinates)
295 return from == to - 1;
296 return to == from - 1;
297 }
298
addAnimatedPath(SVGPathSource * fromSource,SVGPathSource * toSource,SVGPathConsumer * consumer,unsigned repeatCount)299 bool SVGPathBlender::addAnimatedPath(SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer, unsigned repeatCount)
300 {
301 TemporaryChange<unsigned> change(m_addTypesCount, repeatCount);
302 return blendAnimatedPath(0, fromSource, toSource, consumer);
303 }
304
blendAnimatedPath(float progress,SVGPathSource * fromSource,SVGPathSource * toSource,SVGPathConsumer * consumer)305 bool SVGPathBlender::blendAnimatedPath(float progress, SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer)
306 {
307 ASSERT(fromSource);
308 ASSERT(toSource);
309 ASSERT(consumer);
310 m_fromSource = fromSource;
311 m_toSource = toSource;
312 m_consumer = consumer;
313 m_isInFirstHalfOfAnimation = progress < 0.5f;
314 m_progress = progress;
315
316 bool fromSourceHadData = m_fromSource->hasMoreData();
317 while (m_toSource->hasMoreData()) {
318 SVGPathSegType fromCommand;
319 SVGPathSegType toCommand;
320 if ((fromSourceHadData && !m_fromSource->parseSVGSegmentType(fromCommand)) || !m_toSource->parseSVGSegmentType(toCommand))
321 return false;
322
323 m_toMode = coordinateModeOfCommand(toCommand);
324 m_fromMode = fromSourceHadData ? coordinateModeOfCommand(fromCommand) : m_toMode;
325 if (m_fromMode != m_toMode && m_addTypesCount)
326 return false;
327
328 if (fromSourceHadData && !isSegmentEqual(fromCommand, toCommand, m_fromMode, m_toMode))
329 return false;
330
331 switch (toCommand) {
332 case PathSegMoveToRel:
333 case PathSegMoveToAbs:
334 if (!blendMoveToSegment())
335 return false;
336 break;
337 case PathSegLineToRel:
338 case PathSegLineToAbs:
339 if (!blendLineToSegment())
340 return false;
341 break;
342 case PathSegLineToHorizontalRel:
343 case PathSegLineToHorizontalAbs:
344 if (!blendLineToHorizontalSegment())
345 return false;
346 break;
347 case PathSegLineToVerticalRel:
348 case PathSegLineToVerticalAbs:
349 if (!blendLineToVerticalSegment())
350 return false;
351 break;
352 case PathSegClosePath:
353 m_consumer->closePath();
354 break;
355 case PathSegCurveToCubicRel:
356 case PathSegCurveToCubicAbs:
357 if (!blendCurveToCubicSegment())
358 return false;
359 break;
360 case PathSegCurveToCubicSmoothRel:
361 case PathSegCurveToCubicSmoothAbs:
362 if (!blendCurveToCubicSmoothSegment())
363 return false;
364 break;
365 case PathSegCurveToQuadraticRel:
366 case PathSegCurveToQuadraticAbs:
367 if (!blendCurveToQuadraticSegment())
368 return false;
369 break;
370 case PathSegCurveToQuadraticSmoothRel:
371 case PathSegCurveToQuadraticSmoothAbs:
372 if (!blendCurveToQuadraticSmoothSegment())
373 return false;
374 break;
375 case PathSegArcRel:
376 case PathSegArcAbs:
377 if (!blendArcToSegment())
378 return false;
379 break;
380 case PathSegUnknown:
381 return false;
382 }
383
384 if (!fromSourceHadData)
385 continue;
386 if (m_fromSource->hasMoreData() != m_toSource->hasMoreData())
387 return false;
388 if (!m_fromSource->hasMoreData() || !m_toSource->hasMoreData())
389 return true;
390 }
391
392 return true;
393 }
394
cleanup()395 void SVGPathBlender::cleanup()
396 {
397 ASSERT(m_toSource);
398 ASSERT(m_fromSource);
399 ASSERT(m_consumer);
400
401 m_consumer->cleanup();
402 m_toSource = 0;
403 m_fromSource = 0;
404 m_consumer = 0;
405 m_fromCurrentPoint = FloatPoint();
406 m_toCurrentPoint = FloatPoint();
407 }
408
409 }
410