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