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