1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef NS_SMILANIMATIONFUNCTION_H_ 8 #define NS_SMILANIMATIONFUNCTION_H_ 9 10 #include "nsISMILAttr.h" 11 #include "nsGkAtoms.h" 12 #include "nsString.h" 13 #include "nsSMILTargetIdentifier.h" 14 #include "nsSMILTimeValue.h" 15 #include "nsSMILKeySpline.h" 16 #include "nsSMILValue.h" 17 #include "nsTArray.h" 18 #include "nsAttrValue.h" 19 #include "nsSMILTypes.h" 20 21 namespace mozilla { 22 namespace dom { 23 class SVGAnimationElement; 24 } // namespace dom 25 } // namespace mozilla 26 27 //---------------------------------------------------------------------- 28 // nsSMILAnimationFunction 29 // 30 // The animation function calculates animation values. It it is provided with 31 // time parameters (sample time, repeat iteration etc.) and it uses this to 32 // build an appropriate animation value by performing interpolation and 33 // addition operations. 34 // 35 // It is responsible for implementing the animation parameters of an animation 36 // element (e.g. from, by, to, values, calcMode, additive, accumulate, keyTimes, 37 // keySplines) 38 // 39 class nsSMILAnimationFunction 40 { 41 public: 42 nsSMILAnimationFunction(); 43 44 /* 45 * Sets the owning animation element which this class uses to query attribute 46 * values and compare document positions. 47 */ 48 void SetAnimationElement(mozilla::dom::SVGAnimationElement* aAnimationElement); 49 50 /* 51 * Sets animation-specific attributes (or marks them dirty, in the case 52 * of from/to/by/values). 53 * 54 * @param aAttribute The attribute being set 55 * @param aValue The updated value of the attribute. 56 * @param aResult The nsAttrValue object that may be used for storing the 57 * parsed result. 58 * @param aParseResult Outparam used for reporting parse errors. Will be set 59 * to NS_OK if everything succeeds. 60 * @return true if aAttribute is a recognized animation-related 61 * attribute; false otherwise. 62 */ 63 virtual bool SetAttr(nsIAtom* aAttribute, const nsAString& aValue, 64 nsAttrValue& aResult, nsresult* aParseResult = nullptr); 65 66 /* 67 * Unsets the given attribute. 68 * 69 * @returns true if aAttribute is a recognized animation-related 70 * attribute; false otherwise. 71 */ 72 virtual bool UnsetAttr(nsIAtom* aAttribute); 73 74 /** 75 * Indicate a new sample has occurred. 76 * 77 * @param aSampleTime The sample time for this timed element expressed in 78 * simple time. 79 * @param aSimpleDuration The simple duration for this timed element. 80 * @param aRepeatIteration The repeat iteration for this sample. The first 81 * iteration has a value of 0. 82 */ 83 void SampleAt(nsSMILTime aSampleTime, 84 const nsSMILTimeValue& aSimpleDuration, 85 uint32_t aRepeatIteration); 86 87 /** 88 * Indicate to sample using the last value defined for the animation function. 89 * This value is not normally sampled due to the end-point exclusive timing 90 * model but only occurs when the fill mode is "freeze" and the active 91 * duration is an even multiple of the simple duration. 92 * 93 * @param aRepeatIteration The repeat iteration for this sample. The first 94 * iteration has a value of 0. 95 */ 96 void SampleLastValue(uint32_t aRepeatIteration); 97 98 /** 99 * Indicate that this animation is now active. This is used to instruct the 100 * animation function that it should now add its result to the animation 101 * sandwich. The begin time is also provided for proper prioritization of 102 * animation functions, and for this reason, this method must be called 103 * before either of the Sample methods. 104 * 105 * @param aBeginTime The begin time for the newly active interval. 106 */ 107 void Activate(nsSMILTime aBeginTime); 108 109 /** 110 * Indicate that this animation is no longer active. This is used to instruct 111 * the animation function that it should no longer add its result to the 112 * animation sandwich. 113 * 114 * @param aIsFrozen true if this animation should continue to contribute 115 * to the animation sandwich using the most recent sample 116 * parameters. 117 */ 118 void Inactivate(bool aIsFrozen); 119 120 /** 121 * Combines the result of this animation function for the last sample with the 122 * specified value. 123 * 124 * @param aSMILAttr This animation's target attribute. Used here for 125 * doing attribute-specific parsing of from/to/by/values. 126 * 127 * @param aResult The value to compose with. 128 */ 129 void ComposeResult(const nsISMILAttr& aSMILAttr, nsSMILValue& aResult); 130 131 /** 132 * Returns the relative priority of this animation to another. The priority is 133 * used for determining the position of the animation in the animation 134 * sandwich -- higher priority animations are applied on top of lower 135 * priority animations. 136 * 137 * @return -1 if this animation has lower priority or 1 if this animation has 138 * higher priority 139 * 140 * This method should never return any other value, including 0. 141 */ 142 int8_t CompareTo(const nsSMILAnimationFunction* aOther) const; 143 144 /* 145 * The following methods are provided so that the compositor can optimize its 146 * operations by only composing those animation that will affect the final 147 * result. 148 */ 149 150 /** 151 * Indicates if the animation is currently active or frozen. Inactive 152 * animations will not contribute to the composed result. 153 * 154 * @return true if the animation is active or frozen, false otherwise. 155 */ IsActiveOrFrozen()156 bool IsActiveOrFrozen() const 157 { 158 /* 159 * - Frozen animations should be considered active for the purposes of 160 * compositing. 161 * - This function does not assume that our nsSMILValues (by/from/to/values) 162 * have already been parsed. 163 */ 164 return (mIsActive || mIsFrozen); 165 } 166 167 /** 168 * Indicates if the animation is active. 169 * 170 * @return true if the animation is active, false otherwise. 171 */ IsActive()172 bool IsActive() const { 173 return mIsActive; 174 } 175 176 /** 177 * Indicates if this animation will replace the passed in result rather than 178 * adding to it. Animations that replace the underlying value may be called 179 * without first calling lower priority animations. 180 * 181 * @return True if the animation will replace, false if it will add or 182 * otherwise build on the passed in value. 183 */ 184 virtual bool WillReplace() const; 185 186 /** 187 * Indicates if the parameters for this animation have changed since the last 188 * time it was composited. This allows rendering to be performed only when 189 * necessary, particularly when no animations are active. 190 * 191 * Note that the caller is responsible for determining if the animation 192 * target has changed (with help from my UpdateCachedTarget() method). 193 * 194 * @return true if the animation parameters have changed, false 195 * otherwise. 196 */ 197 bool HasChanged() const; 198 199 /** 200 * This method lets us clear the 'HasChanged' flag for inactive animations 201 * after we've reacted to their change to the 'inactive' state, so that we 202 * won't needlessly recompose their targets in every sample. 203 * 204 * This should only be called on an animation function that is inactive and 205 * that returns true from HasChanged(). 206 */ ClearHasChanged()207 void ClearHasChanged() 208 { 209 MOZ_ASSERT(HasChanged(), 210 "clearing mHasChanged flag, when it's already false"); 211 MOZ_ASSERT(!IsActiveOrFrozen(), 212 "clearing mHasChanged flag for active animation"); 213 mHasChanged = false; 214 } 215 216 /** 217 * Updates the cached record of our animation target, and returns a boolean 218 * that indicates whether the target has changed since the last call to this 219 * function. (This lets nsSMILCompositor check whether its animation 220 * functions have changed value or target since the last sample. If none of 221 * them have, then the compositor doesn't need to do anything.) 222 * 223 * @param aNewTarget A nsSMILTargetIdentifier representing the animation 224 * target of this function for this sample. 225 * @return true if |aNewTarget| is different from the old cached value; 226 * otherwise, false. 227 */ 228 bool UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget); 229 230 /** 231 * Returns true if this function was skipped in the previous sample (because 232 * there was a higher-priority non-additive animation). If a skipped animation 233 * function is later used, then the animation sandwich must be recomposited. 234 */ WasSkippedInPrevSample()235 bool WasSkippedInPrevSample() const { 236 return mWasSkippedInPrevSample; 237 } 238 239 /** 240 * Mark this animation function as having been skipped. By marking the 241 * function as skipped, if it is used in a subsequent sample we'll know to 242 * recomposite the sandwich. 243 */ SetWasSkipped()244 void SetWasSkipped() { 245 mWasSkippedInPrevSample = true; 246 } 247 248 /** 249 * Returns true if we need to recalculate the animation value on every sample. 250 * (e.g. because it depends on context like the font-size) 251 */ ValueNeedsReparsingEverySample()252 bool ValueNeedsReparsingEverySample() const { 253 return mValueNeedsReparsingEverySample; 254 } 255 256 // Comparator utility class, used for sorting nsSMILAnimationFunctions 257 class Comparator { 258 public: Equals(const nsSMILAnimationFunction * aElem1,const nsSMILAnimationFunction * aElem2)259 bool Equals(const nsSMILAnimationFunction* aElem1, 260 const nsSMILAnimationFunction* aElem2) const { 261 return (aElem1->CompareTo(aElem2) == 0); 262 } LessThan(const nsSMILAnimationFunction * aElem1,const nsSMILAnimationFunction * aElem2)263 bool LessThan(const nsSMILAnimationFunction* aElem1, 264 const nsSMILAnimationFunction* aElem2) const { 265 return (aElem1->CompareTo(aElem2) < 0); 266 } 267 }; 268 269 protected: 270 // Typedefs 271 typedef FallibleTArray<nsSMILValue> nsSMILValueArray; 272 273 // Types 274 enum nsSMILCalcMode : uint8_t 275 { 276 CALC_LINEAR, 277 CALC_DISCRETE, 278 CALC_PACED, 279 CALC_SPLINE 280 }; 281 282 // Used for sorting nsSMILAnimationFunctions GetBeginTime()283 nsSMILTime GetBeginTime() const { return mBeginTime; } 284 285 // Property getters 286 bool GetAccumulate() const; 287 bool GetAdditive() const; 288 virtual nsSMILCalcMode GetCalcMode() const; 289 290 // Property setters 291 nsresult SetAccumulate(const nsAString& aAccumulate, nsAttrValue& aResult); 292 nsresult SetAdditive(const nsAString& aAdditive, nsAttrValue& aResult); 293 nsresult SetCalcMode(const nsAString& aCalcMode, nsAttrValue& aResult); 294 nsresult SetKeyTimes(const nsAString& aKeyTimes, nsAttrValue& aResult); 295 nsresult SetKeySplines(const nsAString& aKeySplines, nsAttrValue& aResult); 296 297 // Property un-setters 298 void UnsetAccumulate(); 299 void UnsetAdditive(); 300 void UnsetCalcMode(); 301 void UnsetKeyTimes(); 302 void UnsetKeySplines(); 303 304 // Helpers 305 virtual nsresult InterpolateResult(const nsSMILValueArray& aValues, 306 nsSMILValue& aResult, 307 nsSMILValue& aBaseValue); 308 nsresult AccumulateResult(const nsSMILValueArray& aValues, 309 nsSMILValue& aResult); 310 311 nsresult ComputePacedPosition(const nsSMILValueArray& aValues, 312 double aSimpleProgress, 313 double& aIntervalProgress, 314 const nsSMILValue*& aFrom, 315 const nsSMILValue*& aTo); 316 double ComputePacedTotalDistance(const nsSMILValueArray& aValues) const; 317 318 /** 319 * Adjust the simple progress, that is, the point within the simple duration, 320 * by applying any keyTimes. 321 */ 322 double ScaleSimpleProgress(double aProgress, nsSMILCalcMode aCalcMode); 323 /** 324 * Adjust the progress within an interval, that is, between two animation 325 * values, by applying any keySplines. 326 */ 327 double ScaleIntervalProgress(double aProgress, uint32_t aIntervalIndex); 328 329 // Convenience attribute getters -- use these instead of querying 330 // mAnimationElement as these may need to be overridden by subclasses 331 virtual bool HasAttr(nsIAtom* aAttName) const; 332 virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const; 333 virtual bool GetAttr(nsIAtom* aAttName, 334 nsAString& aResult) const; 335 336 bool ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr, 337 nsSMILValue& aResult, 338 bool& aPreventCachingOfSandwich) const; 339 340 virtual nsresult GetValues(const nsISMILAttr& aSMILAttr, 341 nsSMILValueArray& aResult); 342 343 virtual void CheckValueListDependentAttrs(uint32_t aNumValues); 344 void CheckKeyTimes(uint32_t aNumValues); 345 void CheckKeySplines(uint32_t aNumValues); 346 IsToAnimation()347 virtual bool IsToAnimation() const { 348 return !HasAttr(nsGkAtoms::values) && 349 HasAttr(nsGkAtoms::to) && 350 !HasAttr(nsGkAtoms::from); 351 } 352 353 // Returns true if we know our composited value won't change over the 354 // simple duration of this animation (for a fixed base value). 355 virtual bool IsValueFixedForSimpleDuration() const; 356 IsAdditive()357 inline bool IsAdditive() const { 358 /* 359 * Animation is additive if: 360 * 361 * (1) additive = "sum" (GetAdditive() == true), or 362 * (2) it is 'by animation' (by is set, from and values are not) 363 * 364 * Although animation is not additive if it is 'to animation' 365 */ 366 bool isByAnimation = (!HasAttr(nsGkAtoms::values) && 367 HasAttr(nsGkAtoms::by) && 368 !HasAttr(nsGkAtoms::from)); 369 return !IsToAnimation() && (GetAdditive() || isByAnimation); 370 } 371 372 // Setters for error flags 373 // These correspond to bit-indices in mErrorFlags, for tracking parse errors 374 // in these attributes, when those parse errors should block us from doing 375 // animation. 376 enum AnimationAttributeIdx { 377 BF_ACCUMULATE = 0, 378 BF_ADDITIVE = 1, 379 BF_CALC_MODE = 2, 380 BF_KEY_TIMES = 3, 381 BF_KEY_SPLINES = 4, 382 BF_KEY_POINTS = 5 // <animateMotion> only 383 }; 384 SetAccumulateErrorFlag(bool aNewValue)385 inline void SetAccumulateErrorFlag(bool aNewValue) { 386 SetErrorFlag(BF_ACCUMULATE, aNewValue); 387 } SetAdditiveErrorFlag(bool aNewValue)388 inline void SetAdditiveErrorFlag(bool aNewValue) { 389 SetErrorFlag(BF_ADDITIVE, aNewValue); 390 } SetCalcModeErrorFlag(bool aNewValue)391 inline void SetCalcModeErrorFlag(bool aNewValue) { 392 SetErrorFlag(BF_CALC_MODE, aNewValue); 393 } SetKeyTimesErrorFlag(bool aNewValue)394 inline void SetKeyTimesErrorFlag(bool aNewValue) { 395 SetErrorFlag(BF_KEY_TIMES, aNewValue); 396 } SetKeySplinesErrorFlag(bool aNewValue)397 inline void SetKeySplinesErrorFlag(bool aNewValue) { 398 SetErrorFlag(BF_KEY_SPLINES, aNewValue); 399 } SetKeyPointsErrorFlag(bool aNewValue)400 inline void SetKeyPointsErrorFlag(bool aNewValue) { 401 SetErrorFlag(BF_KEY_POINTS, aNewValue); 402 } SetErrorFlag(AnimationAttributeIdx aField,bool aValue)403 inline void SetErrorFlag(AnimationAttributeIdx aField, bool aValue) { 404 if (aValue) { 405 mErrorFlags |= (0x01 << aField); 406 } else { 407 mErrorFlags &= ~(0x01 << aField); 408 } 409 } 410 411 // Members 412 // ------- 413 414 static nsAttrValue::EnumTable sAdditiveTable[]; 415 static nsAttrValue::EnumTable sCalcModeTable[]; 416 static nsAttrValue::EnumTable sAccumulateTable[]; 417 418 FallibleTArray<double> mKeyTimes; 419 FallibleTArray<nsSMILKeySpline> mKeySplines; 420 421 // These are the parameters provided by the previous sample. Currently we 422 // perform lazy calculation. That is, we only calculate the result if and when 423 // instructed by the compositor. This allows us to apply the result directly 424 // to the animation value and allows the compositor to filter out functions 425 // that it determines will not contribute to the final result. 426 nsSMILTime mSampleTime; // sample time within simple dur 427 nsSMILTimeValue mSimpleDuration; 428 uint32_t mRepeatIteration; 429 430 nsSMILTime mBeginTime; // document time 431 432 // The owning animation element. This is used for sorting based on document 433 // position and for fetching attribute values stored in the element. 434 // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive 435 // its owning animation element. 436 mozilla::dom::SVGAnimationElement* mAnimationElement; 437 438 // Which attributes have been set but have had errors. This is not used for 439 // all attributes but only those which have specified error behaviour 440 // associated with them. 441 uint16_t mErrorFlags; 442 443 // Allows us to check whether an animation function has changed target from 444 // sample to sample (because if neither target nor animated value have 445 // changed, we don't have to do anything). 446 nsSMILWeakTargetIdentifier mLastTarget; 447 448 // Boolean flags 449 bool mIsActive:1; 450 bool mIsFrozen:1; 451 bool mLastValue:1; 452 bool mHasChanged:1; 453 bool mValueNeedsReparsingEverySample:1; 454 bool mPrevSampleWasSingleValueAnimation:1; 455 bool mWasSkippedInPrevSample:1; 456 }; 457 458 #endif // NS_SMILANIMATIONFUNCTION_H_ 459