Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / svg / SVGPathBlender.cpp
blobc24f3dbb8e772d43cf9e89da1dc01800542fa747
1 /*
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.
20 #include "config.h"
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"
28 namespace blink {
30 enum FloatBlendMode {
31 BlendHorizontal,
32 BlendVertical
35 class SVGPathBlender::BlendState {
36 public:
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&);
48 private:
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;
57 float m_progress;
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().
62 bool m_typesAreEqual;
63 bool m_fromIsAbsolute;
66 // Helper functions
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;
79 if (m_typesAreEqual)
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)
90 return animValue;
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)
109 if (m_typesAreEqual)
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;
116 else
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.
141 if (m_typesAreEqual)
142 return true;
144 // Addition require segments with the same type.
145 if (m_addTypesCount)
146 return false;
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:
159 case PathSegArcRel:
160 case PathSegLineToHorizontalRel:
161 case PathSegLineToVerticalRel:
162 case PathSegCurveToCubicSmoothRel:
163 case PathSegCurveToQuadraticSmoothRel:
164 currentPoint += segment.targetPoint;
165 break;
166 case PathSegMoveToAbs:
167 case PathSegLineToAbs:
168 case PathSegCurveToCubicAbs:
169 case PathSegCurveToQuadraticAbs:
170 case PathSegArcAbs:
171 case PathSegCurveToCubicSmoothAbs:
172 case PathSegCurveToQuadraticSmoothAbs:
173 currentPoint = segment.targetPoint;
174 break;
175 case PathSegLineToHorizontalAbs:
176 currentPoint.setX(segment.targetPoint.x());
177 break;
178 case PathSegLineToVerticalAbs:
179 currentPoint.setY(segment.targetPoint.y());
180 break;
181 case PathSegClosePath:
182 break;
183 default:
184 ASSERT_NOT_REACHED();
188 bool SVGPathBlender::BlendState::blendSegments(const PathSegmentData& fromSeg, const PathSegmentData& toSeg, PathSegmentData& blendedSegment)
190 if (!canBlend(fromSeg, toSeg))
191 return false;
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);
199 /* fall through */
200 case PathSegCurveToCubicSmoothRel:
201 case PathSegCurveToCubicSmoothAbs:
202 blendedSegment.point2 = blendAnimatedFloatPoint(fromSeg.point2, toSeg.point2);
203 /* fall through */
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);
211 break;
212 case PathSegLineToHorizontalRel:
213 case PathSegLineToHorizontalAbs:
214 blendedSegment.targetPoint.setX(blendAnimatedDimensonalFloat(fromSeg.targetPoint.x(), toSeg.targetPoint.x(), BlendHorizontal));
215 break;
216 case PathSegLineToVerticalRel:
217 case PathSegLineToVerticalAbs:
218 blendedSegment.targetPoint.setY(blendAnimatedDimensonalFloat(fromSeg.targetPoint.y(), toSeg.targetPoint.y(), BlendVertical));
219 break;
220 case PathSegClosePath:
221 break;
222 case PathSegCurveToQuadraticRel:
223 case PathSegCurveToQuadraticAbs:
224 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, toSeg.targetPoint);
225 blendedSegment.point1 = blendAnimatedFloatPoint(fromSeg.point1, toSeg.point1);
226 break;
227 case PathSegArcRel:
228 case PathSegArcAbs:
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;
235 } else {
236 blendedSegment.arcLarge = m_isInFirstHalfOfAnimation ? fromSeg.arcLarge : toSeg.arcLarge;
237 blendedSegment.arcSweep = m_isInFirstHalfOfAnimation ? fromSeg.arcSweep : toSeg.arcSweep;
239 break;
240 default:
241 ASSERT_NOT_REACHED();
244 updateCurrentPoint(m_fromCurrentPoint, fromSeg);
245 updateCurrentPoint(m_toCurrentPoint, toSeg);
247 return true;
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);
256 ASSERT(m_toSource);
257 ASSERT(m_consumer);
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)
278 return false;
280 PathSegmentData fromSeg;
281 fromSeg.command = toSeg.command;
283 if (m_fromSource->hasMoreData()) {
284 fromSeg = m_fromSource->parseSegment();
285 if (fromSeg.command == PathSegUnknown)
286 return false;
289 PathSegmentData blendedSeg;
290 if (!blendState.blendSegments(fromSeg, toSeg, blendedSeg))
291 return false;
293 m_consumer->emitSegment(blendedSeg);
295 if (fromSourceIsEmpty)
296 continue;
297 if (m_fromSource->hasMoreData() != m_toSource->hasMoreData())
298 return false;
300 return true;