| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) Research In Motion Limited 2010, 2011. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010, 2011. All rights reserved. |
| 3 * | 3 * |
| 4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
| 5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
| 6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
| 7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
| 8 * | 8 * |
| 9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
| 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
| 13 * | 13 * |
| 14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
| 18 */ | 18 */ |
| 19 | 19 |
| 20 #include "config.h" | 20 #include "config.h" |
| 21 #include "core/svg/SVGPathBlender.h" | 21 #include "core/svg/SVGPathBlender.h" |
| 22 | 22 |
| 23 #include "core/svg/SVGPathConsumer.h" | 23 #include "core/svg/SVGPathConsumer.h" |
| 24 #include "core/svg/SVGPathSeg.h" | 24 #include "core/svg/SVGPathSeg.h" |
| 25 #include "core/svg/SVGPathSource.h" | 25 #include "core/svg/SVGPathSource.h" |
| 26 #include "platform/animation/AnimationUtilities.h" | 26 #include "platform/animation/AnimationUtilities.h" |
| 27 #include "wtf/TemporaryChange.h" | |
| 28 | 27 |
| 29 namespace blink { | 28 namespace blink { |
| 30 | 29 |
| 31 SVGPathBlender::SVGPathBlender(SVGPathSource* fromSource, SVGPathSource* toSourc
e, SVGPathConsumer* consumer) | 30 enum FloatBlendMode { |
| 32 : m_fromSource(fromSource) | 31 BlendHorizontal, |
| 33 , m_toSource(toSource) | 32 BlendVertical |
| 34 , m_consumer(consumer) | 33 }; |
| 35 , m_progress(0) | |
| 36 , m_addTypesCount(0) | |
| 37 , m_isInFirstHalfOfAnimation(false) | |
| 38 , m_typesAreEqual(false) | |
| 39 , m_fromIsAbsolute(false) | |
| 40 , m_toIsAbsolute(false) | |
| 41 { | |
| 42 ASSERT(m_fromSource); | |
| 43 ASSERT(m_toSource); | |
| 44 ASSERT(m_consumer); | |
| 45 } | |
| 46 | 34 |
| 47 DEFINE_TRACE(SVGPathBlender) | 35 class SVGPathBlender::BlendState { |
| 48 { | 36 public: |
| 49 visitor->trace(m_fromSource); | 37 BlendState(float progress, unsigned addTypesCount = 0) |
| 50 visitor->trace(m_toSource); | 38 : m_progress(progress) |
| 51 visitor->trace(m_consumer); | 39 , m_addTypesCount(addTypesCount) |
| 52 } | 40 , m_isInFirstHalfOfAnimation(progress < 0.5f) |
| 41 , m_typesAreEqual(false) |
| 42 , m_fromIsAbsolute(false) |
| 43 { |
| 44 } |
| 45 |
| 46 bool blendSegments(const PathSegmentData& fromSeg, const PathSegmentData& to
Seg, PathSegmentData&); |
| 47 |
| 48 private: |
| 49 float blendAnimatedDimensonalFloat(float, float, FloatBlendMode); |
| 50 FloatPoint blendAnimatedFloatPointSameCoordinates(const FloatPoint& from, co
nst FloatPoint& to); |
| 51 FloatPoint blendAnimatedFloatPoint(const FloatPoint& from, const FloatPoint&
to); |
| 52 bool canBlend(const PathSegmentData& fromSeg, const PathSegmentData& toSeg); |
| 53 |
| 54 FloatPoint m_fromCurrentPoint; |
| 55 FloatPoint m_toCurrentPoint; |
| 56 |
| 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; |
| 64 }; |
| 53 | 65 |
| 54 // Helper functions | 66 // Helper functions |
| 55 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint&
b, float progress) | 67 static inline FloatPoint blendFloatPoint(const FloatPoint& a, const FloatPoint&
b, float progress) |
| 56 { | 68 { |
| 57 return FloatPoint(blend(a.x(), b.x(), progress), blend(a.y(), b.y(), progres
s)); | 69 return FloatPoint(blend(a.x(), b.x(), progress), blend(a.y(), b.y(), progres
s)); |
| 58 } | 70 } |
| 59 | 71 |
| 60 float SVGPathBlender::blendAnimatedDimensonalFloat(float from, float to, FloatBl
endMode blendMode) | 72 float SVGPathBlender::BlendState::blendAnimatedDimensonalFloat(float from, float
to, FloatBlendMode blendMode) |
| 61 { | 73 { |
| 62 if (m_addTypesCount) { | 74 if (m_addTypesCount) { |
| 63 ASSERT(m_typesAreEqual); | 75 ASSERT(m_typesAreEqual); |
| 64 return from + to * m_addTypesCount; | 76 return from + to * m_addTypesCount; |
| 65 } | 77 } |
| 66 | 78 |
| 67 if (m_typesAreEqual) | 79 if (m_typesAreEqual) |
| 68 return blend(from, to, m_progress); | 80 return blend(from, to, m_progress); |
| 69 | 81 |
| 70 float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_
fromCurrentPoint.y(); | 82 float fromValue = blendMode == BlendHorizontal ? m_fromCurrentPoint.x() : m_
fromCurrentPoint.y(); |
| 71 float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCu
rrentPoint.y(); | 83 float toValue = blendMode == BlendHorizontal ? m_toCurrentPoint.x() : m_toCu
rrentPoint.y(); |
| 72 | 84 |
| 73 // Transform toY to the coordinate mode of fromY | 85 // Transform toY to the coordinate mode of fromY |
| 74 float animValue = blend(from, m_fromIsAbsolute ? to + toValue : to - toValue
, m_progress); | 86 float animValue = blend(from, m_fromIsAbsolute ? to + toValue : to - toValue
, m_progress); |
| 75 | 87 |
| 76 // If we're in the first half of the animation, we should use the type of th
e from segment. | 88 // If we're in the first half of the animation, we should use the type of th
e from segment. |
| 77 if (m_isInFirstHalfOfAnimation) | 89 if (m_isInFirstHalfOfAnimation) |
| 78 return animValue; | 90 return animValue; |
| 79 | 91 |
| 80 // Transform the animated point to the coordinate mode, needed for the curre
nt progress. | 92 // Transform the animated point to the coordinate mode, needed for the curre
nt progress. |
| 81 float currentValue = blend(fromValue, toValue, m_progress); | 93 float currentValue = blend(fromValue, toValue, m_progress); |
| 82 return !m_fromIsAbsolute ? animValue + currentValue : animValue - currentVal
ue; | 94 return !m_fromIsAbsolute ? animValue + currentValue : animValue - currentVal
ue; |
| 83 } | 95 } |
| 84 | 96 |
| 85 FloatPoint SVGPathBlender::blendAnimatedFloatPointSameCoordinates(const FloatPoi
nt& fromPoint, const FloatPoint& toPoint) | 97 FloatPoint SVGPathBlender::BlendState::blendAnimatedFloatPointSameCoordinates(co
nst FloatPoint& fromPoint, const FloatPoint& toPoint) |
| 86 { | 98 { |
| 87 if (m_addTypesCount) { | 99 if (m_addTypesCount) { |
| 88 FloatPoint repeatedToPoint = toPoint; | 100 FloatPoint repeatedToPoint = toPoint; |
| 89 repeatedToPoint.scale(m_addTypesCount, m_addTypesCount); | 101 repeatedToPoint.scale(m_addTypesCount, m_addTypesCount); |
| 90 return fromPoint + repeatedToPoint; | 102 return fromPoint + repeatedToPoint; |
| 91 } | 103 } |
| 92 return blendFloatPoint(fromPoint, toPoint, m_progress); | 104 return blendFloatPoint(fromPoint, toPoint, m_progress); |
| 93 } | 105 } |
| 94 | 106 |
| 95 FloatPoint SVGPathBlender::blendAnimatedFloatPoint(const FloatPoint& fromPoint,
const FloatPoint& toPoint) | 107 FloatPoint SVGPathBlender::BlendState::blendAnimatedFloatPoint(const FloatPoint&
fromPoint, const FloatPoint& toPoint) |
| 96 { | 108 { |
| 97 if (m_typesAreEqual) | 109 if (m_typesAreEqual) |
| 98 return blendAnimatedFloatPointSameCoordinates(fromPoint, toPoint); | 110 return blendAnimatedFloatPointSameCoordinates(fromPoint, toPoint); |
| 99 | 111 |
| 100 // Transform toPoint to the coordinate mode of fromPoint | 112 // Transform toPoint to the coordinate mode of fromPoint |
| 101 FloatPoint animatedPoint = toPoint; | 113 FloatPoint animatedPoint = toPoint; |
| 102 if (m_fromIsAbsolute) | 114 if (m_fromIsAbsolute) |
| 103 animatedPoint += m_toCurrentPoint; | 115 animatedPoint += m_toCurrentPoint; |
| 104 else | 116 else |
| 105 animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y()); | 117 animatedPoint.move(-m_toCurrentPoint.x(), -m_toCurrentPoint.y()); |
| 106 | 118 |
| 107 animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress); | 119 animatedPoint = blendFloatPoint(fromPoint, animatedPoint, m_progress); |
| 108 | 120 |
| 109 // If we're in the first half of the animation, we should use the type of th
e from segment. | 121 // If we're in the first half of the animation, we should use the type of th
e from segment. |
| 110 if (m_isInFirstHalfOfAnimation) | 122 if (m_isInFirstHalfOfAnimation) |
| 111 return animatedPoint; | 123 return animatedPoint; |
| 112 | 124 |
| 113 // Transform the animated point to the coordinate mode, needed for the curre
nt progress. | 125 // Transform the animated point to the coordinate mode, needed for the curre
nt progress. |
| 114 FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoi
nt, m_progress); | 126 FloatPoint currentPoint = blendFloatPoint(m_fromCurrentPoint, m_toCurrentPoi
nt, m_progress); |
| 115 if (!m_fromIsAbsolute) | 127 if (!m_fromIsAbsolute) |
| 116 return animatedPoint + currentPoint; | 128 return animatedPoint + currentPoint; |
| 117 | 129 |
| 118 animatedPoint.move(-currentPoint.x(), -currentPoint.y()); | 130 animatedPoint.move(-currentPoint.x(), -currentPoint.y()); |
| 119 return animatedPoint; | 131 return animatedPoint; |
| 120 } | 132 } |
| 121 | 133 |
| 122 PathSegmentData SVGPathBlender::blendMoveToSegment(const PathSegmentData& fromSe
g, const PathSegmentData& toSeg) | 134 bool SVGPathBlender::BlendState::canBlend(const PathSegmentData& fromSeg, const
PathSegmentData& toSeg) |
| 123 { | 135 { |
| 124 PathSegmentData blendedSegment; | 136 // Update state first because we'll need it if we return true below. |
| 125 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | 137 m_typesAreEqual = fromSeg.command == toSeg.command; |
| 126 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | 138 m_fromIsAbsolute = isAbsolutePathSegType(fromSeg.command); |
| 127 | 139 |
| 128 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | 140 // If the types are equal, they'll blend regardless of parameters. |
| 129 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | 141 if (m_typesAreEqual) |
| 130 return blendedSegment; | 142 return true; |
| 143 |
| 144 // Addition require segments with the same type. |
| 145 if (m_addTypesCount) |
| 146 return false; |
| 147 |
| 148 // Allow the segments to differ in "relativeness". |
| 149 return toAbsolutePathSegType(fromSeg.command) == toAbsolutePathSegType(toSeg
.command); |
| 131 } | 150 } |
| 132 | 151 |
| 133 PathSegmentData SVGPathBlender::blendLineToSegment(const PathSegmentData& fromSe
g, const PathSegmentData& toSeg) | 152 static void updateCurrentPoint(FloatPoint& currentPoint, const PathSegmentData&
segment) |
| 134 { | 153 { |
| 135 PathSegmentData blendedSegment; | 154 switch (segment.command) { |
| 136 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | 155 case PathSegMoveToRel: |
| 137 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | 156 case PathSegLineToRel: |
| 138 | 157 case PathSegCurveToCubicRel: |
| 139 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | 158 case PathSegCurveToQuadraticRel: |
| 140 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | 159 case PathSegArcRel: |
| 141 return blendedSegment; | 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(); |
| 185 } |
| 142 } | 186 } |
| 143 | 187 |
| 144 PathSegmentData SVGPathBlender::blendLineToHorizontalSegment(const PathSegmentDa
ta& fromSeg, const PathSegmentData& toSeg) | 188 bool SVGPathBlender::BlendState::blendSegments(const PathSegmentData& fromSeg, c
onst PathSegmentData& toSeg, PathSegmentData& blendedSegment) |
| 145 { | 189 { |
| 146 PathSegmentData blendedSegment; | 190 if (!canBlend(fromSeg, toSeg)) |
| 191 return false; |
| 192 |
| 147 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | 193 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; |
| 148 blendedSegment.targetPoint.setX(blendAnimatedDimensonalFloat(fromSeg.targetP
oint.x(), toSeg.targetPoint.x(), BlendHorizontal)); | |
| 149 | 194 |
| 150 m_fromCurrentPoint.setX(m_fromIsAbsolute ? fromSeg.targetPoint.x() : m_fromC
urrentPoint.x() + fromSeg.targetPoint.x()); | |
| 151 m_toCurrentPoint.setX(m_toIsAbsolute ? toSeg.targetPoint.x() : m_toCurrentPo
int.x() + toSeg.targetPoint.x()); | |
| 152 return blendedSegment; | |
| 153 } | |
| 154 | |
| 155 PathSegmentData SVGPathBlender::blendLineToVerticalSegment(const PathSegmentData
& fromSeg, const PathSegmentData& toSeg) | |
| 156 { | |
| 157 PathSegmentData blendedSegment; | |
| 158 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 159 blendedSegment.targetPoint.setY(blendAnimatedDimensonalFloat(fromSeg.targetP
oint.y(), toSeg.targetPoint.y(), BlendVertical)); | |
| 160 | |
| 161 m_fromCurrentPoint.setY(m_fromIsAbsolute ? fromSeg.targetPoint.y() : m_fromC
urrentPoint.y() + fromSeg.targetPoint.y()); | |
| 162 m_toCurrentPoint.setY(m_toIsAbsolute ? toSeg.targetPoint.y() : m_toCurrentPo
int.y() + toSeg.targetPoint.y()); | |
| 163 return blendedSegment; | |
| 164 } | |
| 165 | |
| 166 PathSegmentData SVGPathBlender::blendCurveToCubicSegment(const PathSegmentData&
fromSeg, const PathSegmentData& toSeg) | |
| 167 { | |
| 168 PathSegmentData blendedSegment; | |
| 169 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 170 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | |
| 171 blendedSegment.point1 = blendAnimatedFloatPoint(fromSeg.point1, toSeg.point1
); | |
| 172 blendedSegment.point2 = blendAnimatedFloatPoint(fromSeg.point2, toSeg.point2
); | |
| 173 | |
| 174 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | |
| 175 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | |
| 176 return blendedSegment; | |
| 177 } | |
| 178 | |
| 179 PathSegmentData SVGPathBlender::blendCurveToCubicSmoothSegment(const PathSegment
Data& fromSeg, const PathSegmentData& toSeg) | |
| 180 { | |
| 181 PathSegmentData blendedSegment; | |
| 182 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 183 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | |
| 184 blendedSegment.point2 = blendAnimatedFloatPoint(fromSeg.point2, toSeg.point2
); | |
| 185 | |
| 186 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | |
| 187 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | |
| 188 return blendedSegment; | |
| 189 } | |
| 190 | |
| 191 PathSegmentData SVGPathBlender::blendCurveToQuadraticSegment(const PathSegmentDa
ta& fromSeg, const PathSegmentData& toSeg) | |
| 192 { | |
| 193 PathSegmentData blendedSegment; | |
| 194 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 195 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | |
| 196 blendedSegment.point1 = blendAnimatedFloatPoint(fromSeg.point1, toSeg.point1
); | |
| 197 | |
| 198 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | |
| 199 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | |
| 200 return blendedSegment; | |
| 201 } | |
| 202 | |
| 203 PathSegmentData SVGPathBlender::blendCurveToQuadraticSmoothSegment(const PathSeg
mentData& fromSeg, const PathSegmentData& toSeg) | |
| 204 { | |
| 205 PathSegmentData blendedSegment; | |
| 206 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 207 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | |
| 208 | |
| 209 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | |
| 210 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | |
| 211 return blendedSegment; | |
| 212 } | |
| 213 | |
| 214 PathSegmentData SVGPathBlender::blendArcToSegment(const PathSegmentData& fromSeg
, const PathSegmentData& toSeg) | |
| 215 { | |
| 216 ASSERT(!m_addTypesCount || fromSeg.command == toSeg.command); | |
| 217 | |
| 218 PathSegmentData blendedSegment; | |
| 219 blendedSegment.command = m_isInFirstHalfOfAnimation ? fromSeg.command : toSe
g.command; | |
| 220 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint, to
Seg.targetPoint); | |
| 221 blendedSegment.point1 = blendAnimatedFloatPointSameCoordinates(fromSeg.arcRa
dii(), toSeg.arcRadii()); | |
| 222 blendedSegment.point2 = blendAnimatedFloatPointSameCoordinates(fromSeg.point
2, toSeg.point2); | |
| 223 if (m_addTypesCount) { | |
| 224 blendedSegment.arcLarge = fromSeg.arcLarge || toSeg.arcLarge; | |
| 225 blendedSegment.arcSweep = fromSeg.arcSweep || toSeg.arcSweep; | |
| 226 } else { | |
| 227 blendedSegment.arcLarge = m_isInFirstHalfOfAnimation ? fromSeg.arcLarge
: toSeg.arcLarge; | |
| 228 blendedSegment.arcSweep = m_isInFirstHalfOfAnimation ? fromSeg.arcSweep
: toSeg.arcSweep; | |
| 229 } | |
| 230 | |
| 231 m_fromCurrentPoint = m_fromIsAbsolute ? fromSeg.targetPoint : m_fromCurrentP
oint + fromSeg.targetPoint; | |
| 232 m_toCurrentPoint = m_toIsAbsolute ? toSeg.targetPoint : m_toCurrentPoint + t
oSeg.targetPoint; | |
| 233 return blendedSegment; | |
| 234 } | |
| 235 | |
| 236 void SVGPathBlender::blendSegments(const PathSegmentData& fromSeg, const PathSeg
mentData& toSeg) | |
| 237 { | |
| 238 PathSegmentData blendedSegment; | |
| 239 switch (toSeg.command) { | 195 switch (toSeg.command) { |
| 196 case PathSegCurveToCubicRel: |
| 197 case PathSegCurveToCubicAbs: |
| 198 blendedSegment.point1 = blendAnimatedFloatPoint(fromSeg.point1, toSeg.po
int1); |
| 199 /* fall through */ |
| 200 case PathSegCurveToCubicSmoothRel: |
| 201 case PathSegCurveToCubicSmoothAbs: |
| 202 blendedSegment.point2 = blendAnimatedFloatPoint(fromSeg.point2, toSeg.po
int2); |
| 203 /* fall through */ |
| 240 case PathSegMoveToRel: | 204 case PathSegMoveToRel: |
| 241 case PathSegMoveToAbs: | 205 case PathSegMoveToAbs: |
| 242 blendedSegment = blendMoveToSegment(fromSeg, toSeg); | |
| 243 break; | |
| 244 case PathSegLineToRel: | 206 case PathSegLineToRel: |
| 245 case PathSegLineToAbs: | 207 case PathSegLineToAbs: |
| 246 blendedSegment = blendLineToSegment(fromSeg, toSeg); | 208 case PathSegCurveToQuadraticSmoothRel: |
| 209 case PathSegCurveToQuadraticSmoothAbs: |
| 210 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint
, toSeg.targetPoint); |
| 247 break; | 211 break; |
| 248 case PathSegLineToHorizontalRel: | 212 case PathSegLineToHorizontalRel: |
| 249 case PathSegLineToHorizontalAbs: | 213 case PathSegLineToHorizontalAbs: |
| 250 blendedSegment = blendLineToHorizontalSegment(fromSeg, toSeg); | 214 blendedSegment.targetPoint.setX(blendAnimatedDimensonalFloat(fromSeg.tar
getPoint.x(), toSeg.targetPoint.x(), BlendHorizontal)); |
| 251 break; | 215 break; |
| 252 case PathSegLineToVerticalRel: | 216 case PathSegLineToVerticalRel: |
| 253 case PathSegLineToVerticalAbs: | 217 case PathSegLineToVerticalAbs: |
| 254 blendedSegment = blendLineToVerticalSegment(fromSeg, toSeg); | 218 blendedSegment.targetPoint.setY(blendAnimatedDimensonalFloat(fromSeg.tar
getPoint.y(), toSeg.targetPoint.y(), BlendVertical)); |
| 255 break; | 219 break; |
| 256 case PathSegClosePath: | 220 case PathSegClosePath: |
| 257 blendedSegment = toSeg; | |
| 258 break; | |
| 259 case PathSegCurveToCubicRel: | |
| 260 case PathSegCurveToCubicAbs: | |
| 261 blendedSegment = blendCurveToCubicSegment(fromSeg, toSeg); | |
| 262 break; | |
| 263 case PathSegCurveToCubicSmoothRel: | |
| 264 case PathSegCurveToCubicSmoothAbs: | |
| 265 blendedSegment = blendCurveToCubicSmoothSegment(fromSeg, toSeg); | |
| 266 break; | 221 break; |
| 267 case PathSegCurveToQuadraticRel: | 222 case PathSegCurveToQuadraticRel: |
| 268 case PathSegCurveToQuadraticAbs: | 223 case PathSegCurveToQuadraticAbs: |
| 269 blendedSegment = blendCurveToQuadraticSegment(fromSeg, toSeg); | 224 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint
, toSeg.targetPoint); |
| 270 break; | 225 blendedSegment.point1 = blendAnimatedFloatPoint(fromSeg.point1, toSeg.po
int1); |
| 271 case PathSegCurveToQuadraticSmoothRel: | |
| 272 case PathSegCurveToQuadraticSmoothAbs: | |
| 273 blendedSegment = blendCurveToQuadraticSmoothSegment(fromSeg, toSeg); | |
| 274 break; | 226 break; |
| 275 case PathSegArcRel: | 227 case PathSegArcRel: |
| 276 case PathSegArcAbs: | 228 case PathSegArcAbs: |
| 277 blendedSegment = blendArcToSegment(fromSeg, toSeg); | 229 blendedSegment.targetPoint = blendAnimatedFloatPoint(fromSeg.targetPoint
, toSeg.targetPoint); |
| 230 blendedSegment.point1 = blendAnimatedFloatPointSameCoordinates(fromSeg.a
rcRadii(), toSeg.arcRadii()); |
| 231 blendedSegment.point2 = blendAnimatedFloatPointSameCoordinates(fromSeg.p
oint2, 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.arcLa
rge : toSeg.arcLarge; |
| 237 blendedSegment.arcSweep = m_isInFirstHalfOfAnimation ? fromSeg.arcSw
eep : toSeg.arcSweep; |
| 238 } |
| 278 break; | 239 break; |
| 279 default: | 240 default: |
| 280 ASSERT_NOT_REACHED(); | 241 ASSERT_NOT_REACHED(); |
| 281 } | 242 } |
| 282 | 243 |
| 283 m_consumer->emitSegment(blendedSegment); | 244 updateCurrentPoint(m_fromCurrentPoint, fromSeg); |
| 245 updateCurrentPoint(m_toCurrentPoint, toSeg); |
| 246 |
| 247 return true; |
| 248 } |
| 249 |
| 250 SVGPathBlender::SVGPathBlender(SVGPathSource* fromSource, SVGPathSource* toSourc
e, SVGPathConsumer* consumer) |
| 251 : m_fromSource(fromSource) |
| 252 , m_toSource(toSource) |
| 253 , m_consumer(consumer) |
| 254 { |
| 255 ASSERT(m_fromSource); |
| 256 ASSERT(m_toSource); |
| 257 ASSERT(m_consumer); |
| 258 } |
| 259 |
| 260 DEFINE_TRACE(SVGPathBlender) |
| 261 { |
| 262 visitor->trace(m_fromSource); |
| 263 visitor->trace(m_toSource); |
| 264 visitor->trace(m_consumer); |
| 284 } | 265 } |
| 285 | 266 |
| 286 bool SVGPathBlender::addAnimatedPath(unsigned repeatCount) | 267 bool SVGPathBlender::addAnimatedPath(unsigned repeatCount) |
| 287 { | 268 { |
| 288 TemporaryChange<unsigned> change(m_addTypesCount, repeatCount); | 269 BlendState blendState(0, repeatCount); |
| 289 return blendAnimatedPath(0); | 270 return blendAnimatedPath(blendState); |
| 290 } | 271 } |
| 291 | 272 |
| 292 bool SVGPathBlender::blendAnimatedPath(float progress) | 273 bool SVGPathBlender::blendAnimatedPath(float progress) |
| 293 { | 274 { |
| 294 m_isInFirstHalfOfAnimation = progress < 0.5f; | 275 BlendState blendState(progress); |
| 295 m_progress = progress; | 276 return blendAnimatedPath(blendState); |
| 277 } |
| 296 | 278 |
| 297 bool fromSourceHadData = m_fromSource->hasMoreData(); | 279 bool SVGPathBlender::blendAnimatedPath(BlendState& blendState) |
| 280 { |
| 281 bool fromSourceIsEmpty = !m_fromSource->hasMoreData(); |
| 298 while (m_toSource->hasMoreData()) { | 282 while (m_toSource->hasMoreData()) { |
| 299 PathSegmentData toSeg = m_toSource->parseSegment(); | 283 PathSegmentData toSeg = m_toSource->parseSegment(); |
| 300 if (toSeg.command == PathSegUnknown) | 284 if (toSeg.command == PathSegUnknown) |
| 301 return false; | 285 return false; |
| 302 | 286 |
| 303 PathSegmentData fromSeg; | 287 PathSegmentData fromSeg; |
| 304 fromSeg.command = toSeg.command; | 288 fromSeg.command = toSeg.command; |
| 305 | 289 |
| 306 if (m_fromSource->hasMoreData()) { | 290 if (m_fromSource->hasMoreData()) { |
| 307 fromSeg = m_fromSource->parseSegment(); | 291 fromSeg = m_fromSource->parseSegment(); |
| 308 if (fromSeg.command == PathSegUnknown) | 292 if (fromSeg.command == PathSegUnknown) |
| 309 return false; | 293 return false; |
| 310 } | 294 } |
| 311 | 295 |
| 312 m_typesAreEqual = fromSeg.command == toSeg.command; | 296 PathSegmentData blendedSeg; |
| 297 if (!blendState.blendSegments(fromSeg, toSeg, blendedSeg)) |
| 298 return false; |
| 313 | 299 |
| 314 // If the types are equal, they'll blend regardless of parameters. | 300 m_consumer->emitSegment(blendedSeg); |
| 315 if (!m_typesAreEqual) { | |
| 316 // Addition require segments with the same type. | |
| 317 if (m_addTypesCount) | |
| 318 return false; | |
| 319 // Allow the segments to differ in "relativeness". | |
| 320 if (toAbsolutePathSegType(fromSeg.command) != toAbsolutePathSegType(
toSeg.command)) | |
| 321 return false; | |
| 322 } | |
| 323 | 301 |
| 324 m_fromIsAbsolute = isAbsolutePathSegType(fromSeg.command); | 302 if (fromSourceIsEmpty) |
| 325 m_toIsAbsolute = isAbsolutePathSegType(toSeg.command); | |
| 326 | |
| 327 blendSegments(fromSeg, toSeg); | |
| 328 | |
| 329 if (!fromSourceHadData) | |
| 330 continue; | 303 continue; |
| 331 if (m_fromSource->hasMoreData() != m_toSource->hasMoreData()) | 304 if (m_fromSource->hasMoreData() != m_toSource->hasMoreData()) |
| 332 return false; | 305 return false; |
| 333 } | 306 } |
| 334 return true; | 307 return true; |
| 335 } | 308 } |
| 336 | 309 |
| 337 } | 310 } |
| OLD | NEW |