2 * Copyright (C) Research In Motion Limited 2010, 2011. All rights reserved.
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.
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.
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.
21 #include "core/svg/SVGPathBlender.h"
23 #include "core/svg/SVGPathConsumer.h"
24 #include "core/svg/SVGPathSeg.h"
25 #include "core/svg/SVGPathSource.h"
26 #include "platform/animation/AnimationUtilities.h"
35 class SVGPathBlender::BlendState
{
37 BlendState(float progress
, unsigned addTypesCount
= 0)
38 : m_progress(progress
)
39 , m_addTypesCount(addTypesCount
)
40 , m_isInFirstHalfOfAnimation(progress
< 0.5f
)
41 , m_typesAreEqual(false)
42 , m_fromIsAbsolute(false)
46 bool blendSegments(const PathSegmentData
& fromSeg
, const PathSegmentData
& toSeg
, PathSegmentData
&);
49 float blendAnimatedDimensonalFloat(float, float, FloatBlendMode
);
50 FloatPoint
blendAnimatedFloatPointSameCoordinates(const FloatPoint
& from
, const FloatPoint
& to
);
51 FloatPoint
blendAnimatedFloatPoint(const FloatPoint
& from
, const FloatPoint
& to
);
52 bool canBlend(const PathSegmentData
& fromSeg
, const PathSegmentData
& toSeg
);
54 FloatPoint m_fromCurrentPoint
;
55 FloatPoint m_toCurrentPoint
;
58 unsigned m_addTypesCount
;
59 bool m_isInFirstHalfOfAnimation
;
60 // This is per-segment blend state corresponding to the 'from' and 'to'
61 // segments currently being blended, and only used within blendSegments().
63 bool m_fromIsAbsolute
;
67 static inline FloatPoint
blendFloatPoint(const FloatPoint
& a
, const FloatPoint
& b
, float progress
)
69 return FloatPoint(blend(a
.x(), b
.x(), progress
), blend(a
.y(), b
.y(), progress
));
72 float SVGPathBlender::BlendState::blendAnimatedDimensonalFloat(float from
, float to
, FloatBlendMode blendMode
)
74 if (m_addTypesCount
) {
75 ASSERT(m_typesAreEqual
);
76 return from
+ to
* m_addTypesCount
;
80 return blend(from
, to
, m_progress
);
82 float fromValue
= blendMode
== BlendHorizontal
? m_fromCurrentPoint
.x() : m_fromCurrentPoint
.y();
83 float toValue
= blendMode
== BlendHorizontal
? m_toCurrentPoint
.x() : m_toCurrentPoint
.y();
85 // Transform toY to the coordinate mode of fromY
86 float animValue
= blend(from
, m_fromIsAbsolute
? to
+ toValue
: to
- toValue
, m_progress
);
88 // If we're in the first half of the animation, we should use the type of the from segment.
89 if (m_isInFirstHalfOfAnimation
)
92 // Transform the animated point to the coordinate mode, needed for the current progress.
93 float currentValue
= blend(fromValue
, toValue
, m_progress
);
94 return !m_fromIsAbsolute
? animValue
+ currentValue
: animValue
- currentValue
;
97 FloatPoint
SVGPathBlender::BlendState::blendAnimatedFloatPointSameCoordinates(const FloatPoint
& fromPoint
, const FloatPoint
& toPoint
)
99 if (m_addTypesCount
) {
100 FloatPoint repeatedToPoint
= toPoint
;
101 repeatedToPoint
.scale(m_addTypesCount
, m_addTypesCount
);
102 return fromPoint
+ repeatedToPoint
;
104 return blendFloatPoint(fromPoint
, toPoint
, m_progress
);
107 FloatPoint
SVGPathBlender::BlendState::blendAnimatedFloatPoint(const FloatPoint
& fromPoint
, const FloatPoint
& toPoint
)
110 return blendAnimatedFloatPointSameCoordinates(fromPoint
, toPoint
);
112 // Transform toPoint to the coordinate mode of fromPoint
113 FloatPoint animatedPoint
= toPoint
;
114 if (m_fromIsAbsolute
)
115 animatedPoint
+= m_toCurrentPoint
;
117 animatedPoint
.move(-m_toCurrentPoint
.x(), -m_toCurrentPoint
.y());
119 animatedPoint
= blendFloatPoint(fromPoint
, animatedPoint
, m_progress
);
121 // If we're in the first half of the animation, we should use the type of the from segment.
122 if (m_isInFirstHalfOfAnimation
)
123 return animatedPoint
;
125 // Transform the animated point to the coordinate mode, needed for the current progress.
126 FloatPoint currentPoint
= blendFloatPoint(m_fromCurrentPoint
, m_toCurrentPoint
, m_progress
);
127 if (!m_fromIsAbsolute
)
128 return animatedPoint
+ currentPoint
;
130 animatedPoint
.move(-currentPoint
.x(), -currentPoint
.y());
131 return animatedPoint
;
134 bool SVGPathBlender::BlendState::canBlend(const PathSegmentData
& fromSeg
, const PathSegmentData
& toSeg
)
136 // Update state first because we'll need it if we return true below.
137 m_typesAreEqual
= fromSeg
.command
== toSeg
.command
;
138 m_fromIsAbsolute
= isAbsolutePathSegType(fromSeg
.command
);
140 // If the types are equal, they'll blend regardless of parameters.
144 // Addition require segments with the same type.
148 // Allow the segments to differ in "relativeness".
149 return toAbsolutePathSegType(fromSeg
.command
) == toAbsolutePathSegType(toSeg
.command
);
152 static void updateCurrentPoint(FloatPoint
& currentPoint
, const PathSegmentData
& segment
)
154 switch (segment
.command
) {
155 case PathSegMoveToRel
:
156 case PathSegLineToRel
:
157 case PathSegCurveToCubicRel
:
158 case PathSegCurveToQuadraticRel
:
160 case PathSegLineToHorizontalRel
:
161 case PathSegLineToVerticalRel
:
162 case PathSegCurveToCubicSmoothRel
:
163 case PathSegCurveToQuadraticSmoothRel
:
164 currentPoint
+= segment
.targetPoint
;
166 case PathSegMoveToAbs
:
167 case PathSegLineToAbs
:
168 case PathSegCurveToCubicAbs
:
169 case PathSegCurveToQuadraticAbs
:
171 case PathSegCurveToCubicSmoothAbs
:
172 case PathSegCurveToQuadraticSmoothAbs
:
173 currentPoint
= segment
.targetPoint
;
175 case PathSegLineToHorizontalAbs
:
176 currentPoint
.setX(segment
.targetPoint
.x());
178 case PathSegLineToVerticalAbs
:
179 currentPoint
.setY(segment
.targetPoint
.y());
181 case PathSegClosePath
:
184 ASSERT_NOT_REACHED();
188 bool SVGPathBlender::BlendState::blendSegments(const PathSegmentData
& fromSeg
, const PathSegmentData
& toSeg
, PathSegmentData
& blendedSegment
)
190 if (!canBlend(fromSeg
, toSeg
))
193 blendedSegment
.command
= m_isInFirstHalfOfAnimation
? fromSeg
.command
: toSeg
.command
;
195 switch (toSeg
.command
) {
196 case PathSegCurveToCubicRel
:
197 case PathSegCurveToCubicAbs
:
198 blendedSegment
.point1
= blendAnimatedFloatPoint(fromSeg
.point1
, toSeg
.point1
);
200 case PathSegCurveToCubicSmoothRel
:
201 case PathSegCurveToCubicSmoothAbs
:
202 blendedSegment
.point2
= blendAnimatedFloatPoint(fromSeg
.point2
, toSeg
.point2
);
204 case PathSegMoveToRel
:
205 case PathSegMoveToAbs
:
206 case PathSegLineToRel
:
207 case PathSegLineToAbs
:
208 case PathSegCurveToQuadraticSmoothRel
:
209 case PathSegCurveToQuadraticSmoothAbs
:
210 blendedSegment
.targetPoint
= blendAnimatedFloatPoint(fromSeg
.targetPoint
, toSeg
.targetPoint
);
212 case PathSegLineToHorizontalRel
:
213 case PathSegLineToHorizontalAbs
:
214 blendedSegment
.targetPoint
.setX(blendAnimatedDimensonalFloat(fromSeg
.targetPoint
.x(), toSeg
.targetPoint
.x(), BlendHorizontal
));
216 case PathSegLineToVerticalRel
:
217 case PathSegLineToVerticalAbs
:
218 blendedSegment
.targetPoint
.setY(blendAnimatedDimensonalFloat(fromSeg
.targetPoint
.y(), toSeg
.targetPoint
.y(), BlendVertical
));
220 case PathSegClosePath
:
222 case PathSegCurveToQuadraticRel
:
223 case PathSegCurveToQuadraticAbs
:
224 blendedSegment
.targetPoint
= blendAnimatedFloatPoint(fromSeg
.targetPoint
, toSeg
.targetPoint
);
225 blendedSegment
.point1
= blendAnimatedFloatPoint(fromSeg
.point1
, toSeg
.point1
);
229 blendedSegment
.targetPoint
= blendAnimatedFloatPoint(fromSeg
.targetPoint
, toSeg
.targetPoint
);
230 blendedSegment
.point1
= blendAnimatedFloatPointSameCoordinates(fromSeg
.arcRadii(), toSeg
.arcRadii());
231 blendedSegment
.point2
= blendAnimatedFloatPointSameCoordinates(fromSeg
.point2
, toSeg
.point2
);
232 if (m_addTypesCount
) {
233 blendedSegment
.arcLarge
= fromSeg
.arcLarge
|| toSeg
.arcLarge
;
234 blendedSegment
.arcSweep
= fromSeg
.arcSweep
|| toSeg
.arcSweep
;
236 blendedSegment
.arcLarge
= m_isInFirstHalfOfAnimation
? fromSeg
.arcLarge
: toSeg
.arcLarge
;
237 blendedSegment
.arcSweep
= m_isInFirstHalfOfAnimation
? fromSeg
.arcSweep
: toSeg
.arcSweep
;
241 ASSERT_NOT_REACHED();
244 updateCurrentPoint(m_fromCurrentPoint
, fromSeg
);
245 updateCurrentPoint(m_toCurrentPoint
, toSeg
);
250 SVGPathBlender::SVGPathBlender(SVGPathSource
* fromSource
, SVGPathSource
* toSource
, SVGPathConsumer
* consumer
)
251 : m_fromSource(fromSource
)
252 , m_toSource(toSource
)
253 , m_consumer(consumer
)
255 ASSERT(m_fromSource
);
260 bool SVGPathBlender::addAnimatedPath(unsigned repeatCount
)
262 BlendState
blendState(0, repeatCount
);
263 return blendAnimatedPath(blendState
);
266 bool SVGPathBlender::blendAnimatedPath(float progress
)
268 BlendState
blendState(progress
);
269 return blendAnimatedPath(blendState
);
272 bool SVGPathBlender::blendAnimatedPath(BlendState
& blendState
)
274 bool fromSourceIsEmpty
= !m_fromSource
->hasMoreData();
275 while (m_toSource
->hasMoreData()) {
276 PathSegmentData toSeg
= m_toSource
->parseSegment();
277 if (toSeg
.command
== PathSegUnknown
)
280 PathSegmentData fromSeg
;
281 fromSeg
.command
= toSeg
.command
;
283 if (m_fromSource
->hasMoreData()) {
284 fromSeg
= m_fromSource
->parseSegment();
285 if (fromSeg
.command
== PathSegUnknown
)
289 PathSegmentData blendedSeg
;
290 if (!blendState
.blendSegments(fromSeg
, toSeg
, blendedSeg
))
293 m_consumer
->emitSegment(blendedSeg
);
295 if (fromSourceIsEmpty
)
297 if (m_fromSource
->hasMoreData() != m_toSource
->hasMoreData())