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