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