• 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 
22 #if ENABLE(SVG)
23 #include "SVGPathBlender.h"
24 
25 #include "SVGPathSeg.h"
26 
27 namespace WebCore {
28 
SVGPathBlender()29 SVGPathBlender::SVGPathBlender()
30     : m_fromSource(0)
31     , m_toSource(0)
32     , m_consumer(0)
33     , m_progress(0)
34 {
35 }
36 
37 // Helper functions
blendFloatPoint(const FloatPoint & a,const FloatPoint & b,float progress)38 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint& b, float progress)
39 {
40     return FloatPoint((b.x() - a.x()) * progress + a.x(), (b.y() - a.y()) * progress + a.y());
41 }
42 
blendAnimatedFloat(float from,float to,float progress)43 static inline float blendAnimatedFloat(float from, float to, float progress)
44 {
45     return (to - from) * progress + from;
46 }
47 
blendAnimatedDimensonalFloat(float from,float to,FloatBlendMode blendMode)48 float SVGPathBlender::blendAnimatedDimensonalFloat(float from, float to, FloatBlendMode blendMode)
49 {
50     if (m_fromMode == m_toMode)
51         return blendAnimatedFloat(from, to, m_progress);
52 
53     float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_fromCurrentPoint.y();
54     float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCurrentPoint.y();
55 
56     // Transform toY to the coordinate mode of fromY
57     float animValue = blendAnimatedFloat(from, m_fromMode == AbsoluteCoordinates ? to + toValue : to - toValue, m_progress);
58 
59     if (m_isInFirstHalfOfAnimation)
60         return animValue;
61 
62     // Transform the animated point to the coordinate mode, needed for the current progress.
63     float currentValue = blendAnimatedFloat(fromValue, toValue, m_progress);
64     return m_toMode == AbsoluteCoordinates ? animValue + currentValue : animValue - currentValue;
65 }
66 
blendAnimatedFloatPoint(const FloatPoint & fromPoint,const FloatPoint & toPoint)67 FloatPoint SVGPathBlender::blendAnimatedFloatPoint(const FloatPoint& fromPoint, const FloatPoint& toPoint)
68 {
69     if (m_fromMode == m_toMode)
70         return blendFloatPoint(fromPoint, toPoint, m_progress);
71 
72     // Transform toPoint to the coordinate mode of fromPoint
73     FloatPoint animatedPoint = toPoint;
74     if (m_fromMode == AbsoluteCoordinates)
75         animatedPoint += m_toCurrentPoint;
76     else
77         animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y());
78 
79     animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress);
80 
81     if (m_isInFirstHalfOfAnimation)
82         return animatedPoint;
83 
84     // Transform the animated point to the coordinate mode, needed for the current progress.
85     FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoint, m_progress);
86     if (m_toMode == AbsoluteCoordinates)
87         return animatedPoint + currentPoint;
88 
89     animatedPoint.move(-currentPoint.x(), -currentPoint.y());
90     return animatedPoint;
91 }
92 
blendMoveToSegment()93 bool SVGPathBlender::blendMoveToSegment()
94 {
95     FloatPoint fromTargetPoint;
96     FloatPoint toTargetPoint;
97     if (!m_fromSource->parseMoveToSegment(fromTargetPoint)
98         || !m_toSource->parseMoveToSegment(toTargetPoint))
99         return false;
100 
101     m_consumer->moveTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), false, m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
102     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
103     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
104     return true;
105 }
106 
blendLineToSegment()107 bool SVGPathBlender::blendLineToSegment()
108 {
109     FloatPoint fromTargetPoint;
110     FloatPoint toTargetPoint;
111     if (!m_fromSource->parseLineToSegment(fromTargetPoint)
112         || !m_toSource->parseLineToSegment(toTargetPoint))
113         return false;
114 
115     m_consumer->lineTo(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
116     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
117     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
118     return true;
119 }
120 
blendLineToHorizontalSegment()121 bool SVGPathBlender::blendLineToHorizontalSegment()
122 {
123     float fromX;
124     float toX;
125     if (!m_fromSource->parseLineToHorizontalSegment(fromX)
126         || !m_toSource->parseLineToHorizontalSegment(toX))
127         return false;
128 
129     m_consumer->lineToHorizontal(blendAnimatedDimensonalFloat(fromX, toX, BlendHorizontal), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
130     m_fromCurrentPoint.setX(m_fromMode == AbsoluteCoordinates ? fromX : m_fromCurrentPoint.x() + fromX);
131     m_toCurrentPoint.setX(m_toMode == AbsoluteCoordinates ? toX : m_toCurrentPoint.x() + toX);
132     return true;
133 }
134 
blendLineToVerticalSegment()135 bool SVGPathBlender::blendLineToVerticalSegment()
136 {
137     float fromY;
138     float toY;
139     if (!m_fromSource->parseLineToVerticalSegment(fromY)
140         || !m_toSource->parseLineToVerticalSegment(toY))
141         return false;
142 
143     m_consumer->lineToVertical(blendAnimatedDimensonalFloat(fromY, toY, BlendVertical), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
144     m_fromCurrentPoint.setY(m_fromMode == AbsoluteCoordinates ? fromY : m_fromCurrentPoint.y() + fromY);
145     m_toCurrentPoint.setY(m_toMode == AbsoluteCoordinates ? toY : m_toCurrentPoint.y() + toY);
146     return true;
147 }
148 
blendCurveToCubicSegment()149 bool SVGPathBlender::blendCurveToCubicSegment()
150 {
151     FloatPoint fromTargetPoint;
152     FloatPoint fromPoint1;
153     FloatPoint fromPoint2;
154     FloatPoint toTargetPoint;
155     FloatPoint toPoint1;
156     FloatPoint toPoint2;
157     if (!m_fromSource->parseCurveToCubicSegment(fromPoint1, fromPoint2, fromTargetPoint)
158         || !m_toSource->parseCurveToCubicSegment(toPoint1, toPoint2, toTargetPoint))
159         return false;
160 
161     m_consumer->curveToCubic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
162                              blendAnimatedFloatPoint(fromPoint2, toPoint2),
163                              blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
164                              m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
165     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
166     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
167     return true;
168 }
169 
blendCurveToCubicSmoothSegment()170 bool SVGPathBlender::blendCurveToCubicSmoothSegment()
171 {
172     FloatPoint fromTargetPoint;
173     FloatPoint fromPoint2;
174     FloatPoint toTargetPoint;
175     FloatPoint toPoint2;
176     if (!m_fromSource->parseCurveToCubicSmoothSegment(fromPoint2, fromTargetPoint)
177         || !m_toSource->parseCurveToCubicSmoothSegment(toPoint2, toTargetPoint))
178         return false;
179 
180     m_consumer->curveToCubicSmooth(blendAnimatedFloatPoint(fromPoint2, toPoint2),
181                                    blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
182                                    m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
183     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
184     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
185     return true;
186 }
187 
blendCurveToQuadraticSegment()188 bool SVGPathBlender::blendCurveToQuadraticSegment()
189 {
190     FloatPoint fromTargetPoint;
191     FloatPoint fromPoint1;
192     FloatPoint toTargetPoint;
193     FloatPoint toPoint1;
194     if (!m_fromSource->parseCurveToQuadraticSegment(fromPoint1, fromTargetPoint)
195         || !m_toSource->parseCurveToQuadraticSegment(toPoint1, toTargetPoint))
196         return false;
197 
198     m_consumer->curveToQuadratic(blendAnimatedFloatPoint(fromPoint1, toPoint1),
199                                  blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
200                                  m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
201     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
202     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
203     return true;
204 }
205 
blendCurveToQuadraticSmoothSegment()206 bool SVGPathBlender::blendCurveToQuadraticSmoothSegment()
207 {
208     FloatPoint fromTargetPoint;
209     FloatPoint toTargetPoint;
210     if (!m_fromSource->parseCurveToQuadraticSmoothSegment(fromTargetPoint)
211         || !m_toSource->parseCurveToQuadraticSmoothSegment(toTargetPoint))
212         return false;
213 
214     m_consumer->curveToQuadraticSmooth(blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint), m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
215     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
216     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
217     return true;
218 }
219 
blendArcToSegment()220 bool SVGPathBlender::blendArcToSegment()
221 {
222     float fromRx;
223     float fromRy;
224     float fromAngle;
225     bool fromLargeArc;
226     bool fromSweep;
227     FloatPoint fromTargetPoint;
228     float toRx;
229     float toRy;
230     float toAngle;
231     bool toLargeArc;
232     bool toSweep;
233     FloatPoint toTargetPoint;
234     if (!m_fromSource->parseArcToSegment(fromRx, fromRy, fromAngle, fromLargeArc, fromSweep, fromTargetPoint)
235         || !m_toSource->parseArcToSegment(toRx, toRy, toAngle, toLargeArc, toSweep, toTargetPoint))
236         return false;
237 
238     m_consumer->arcTo(blendAnimatedFloat(fromRx, toRx, m_progress),
239                       blendAnimatedFloat(fromRy, toRy, m_progress),
240                       blendAnimatedFloat(fromAngle, toAngle, m_progress),
241                       m_isInFirstHalfOfAnimation ? fromLargeArc : toLargeArc,
242                       m_isInFirstHalfOfAnimation ? fromSweep : toSweep,
243                       blendAnimatedFloatPoint(fromTargetPoint, toTargetPoint),
244                       m_isInFirstHalfOfAnimation ? m_fromMode : m_toMode);
245     m_fromCurrentPoint = m_fromMode == AbsoluteCoordinates ? fromTargetPoint : m_fromCurrentPoint + fromTargetPoint;
246     m_toCurrentPoint = m_toMode == AbsoluteCoordinates ? toTargetPoint : m_toCurrentPoint + toTargetPoint;
247     return true;
248 }
249 
coordinateModeOfCommand(const SVGPathSegType & type)250 static inline PathCoordinateMode coordinateModeOfCommand(const SVGPathSegType& type)
251 {
252     if (type < PathSegMoveToAbs)
253         return AbsoluteCoordinates;
254 
255     // Odd number = relative command
256     if (type % 2)
257         return RelativeCoordinates;
258 
259     return AbsoluteCoordinates;
260 }
261 
isSegmentEqual(const SVGPathSegType & fromType,const SVGPathSegType & toType,const PathCoordinateMode & fromMode,const PathCoordinateMode & toMode)262 static inline bool isSegmentEqual(const SVGPathSegType& fromType, const SVGPathSegType& toType, const PathCoordinateMode& fromMode, const PathCoordinateMode& toMode)
263 {
264     if (fromType == toType && (fromType == PathSegUnknown || fromType == PathSegClosePath))
265         return true;
266 
267     unsigned short from = fromType;
268     unsigned short to = toType;
269     if (fromMode == toMode)
270         return from == to;
271     if (fromMode == AbsoluteCoordinates)
272         return from == to - 1;
273     return to == from - 1;
274 }
275 
blendAnimatedPath(float progress,SVGPathSource * fromSource,SVGPathSource * toSource,SVGPathConsumer * consumer)276 bool SVGPathBlender::blendAnimatedPath(float progress, SVGPathSource* fromSource, SVGPathSource* toSource, SVGPathConsumer* consumer)
277 {
278     ASSERT(fromSource);
279     ASSERT(toSource);
280     ASSERT(consumer);
281     m_fromSource = fromSource;
282     m_toSource = toSource;
283     m_consumer = consumer;
284     m_isInFirstHalfOfAnimation = progress < 0.5f;
285 
286     m_progress = progress;
287     while (true) {
288         SVGPathSegType fromCommand;
289         SVGPathSegType toCommand;
290         if (!m_fromSource->parseSVGSegmentType(fromCommand) || !m_toSource->parseSVGSegmentType(toCommand))
291             return false;
292 
293         m_fromMode = coordinateModeOfCommand(fromCommand);
294         m_toMode = coordinateModeOfCommand(toCommand);
295         if (!isSegmentEqual(fromCommand, toCommand, m_fromMode, m_toMode))
296             return false;
297 
298         switch (fromCommand) {
299         case PathSegMoveToRel:
300         case PathSegMoveToAbs:
301             if (!blendMoveToSegment())
302                 return false;
303             break;
304         case PathSegLineToRel:
305         case PathSegLineToAbs:
306             if (!blendLineToSegment())
307                 return false;
308             break;
309         case PathSegLineToHorizontalRel:
310         case PathSegLineToHorizontalAbs:
311             if (!blendLineToHorizontalSegment())
312                 return false;
313             break;
314         case PathSegLineToVerticalRel:
315         case PathSegLineToVerticalAbs:
316             if (!blendLineToVerticalSegment())
317                 return false;
318             break;
319         case PathSegClosePath:
320             m_consumer->closePath();
321             break;
322         case PathSegCurveToCubicRel:
323         case PathSegCurveToCubicAbs:
324             if (!blendCurveToCubicSegment())
325                 return false;
326             break;
327         case PathSegCurveToCubicSmoothRel:
328         case PathSegCurveToCubicSmoothAbs:
329             if (!blendCurveToCubicSmoothSegment())
330                 return false;
331             break;
332         case PathSegCurveToQuadraticRel:
333         case PathSegCurveToQuadraticAbs:
334             if (!blendCurveToQuadraticSegment())
335                 return false;
336             break;
337         case PathSegCurveToQuadraticSmoothRel:
338         case PathSegCurveToQuadraticSmoothAbs:
339             if (!blendCurveToQuadraticSmoothSegment())
340                 return false;
341             break;
342         case PathSegArcRel:
343         case PathSegArcAbs:
344             if (!blendArcToSegment())
345                 return false;
346             break;
347         default:
348             return false;
349         }
350         if (m_fromSource->hasMoreData() != m_toSource->hasMoreData())
351             return false;
352         if (!m_fromSource->hasMoreData() || !m_toSource->hasMoreData())
353             break;
354     }
355     return true;
356 }
357 
cleanup()358 void SVGPathBlender::cleanup()
359 {
360     ASSERT(m_toSource);
361     ASSERT(m_fromSource);
362     ASSERT(m_consumer);
363 
364     m_consumer->cleanup();
365     m_toSource = 0;
366     m_fromSource = 0;
367     m_consumer = 0;
368     m_fromCurrentPoint = FloatPoint();
369     m_toCurrentPoint = FloatPoint();
370 }
371 
372 }
373 
374 #endif // ENABLE(SVG)
375