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