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