• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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