michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef NS_SMILANIMATIONFUNCTION_H_ michael@0: #define NS_SMILANIMATIONFUNCTION_H_ michael@0: michael@0: #include "nsISMILAttr.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsString.h" michael@0: #include "nsSMILTargetIdentifier.h" michael@0: #include "nsSMILTimeValue.h" michael@0: #include "nsSMILKeySpline.h" michael@0: #include "nsSMILValue.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsTArray.h" michael@0: #include "nsAttrValue.h" michael@0: #include "nsSMILTypes.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class SVGAnimationElement; michael@0: } michael@0: } michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // nsSMILAnimationFunction michael@0: // michael@0: // The animation function calculates animation values. It it is provided with michael@0: // time parameters (sample time, repeat iteration etc.) and it uses this to michael@0: // build an appropriate animation value by performing interpolation and michael@0: // addition operations. michael@0: // michael@0: // It is responsible for implementing the animation parameters of an animation michael@0: // element (e.g. from, by, to, values, calcMode, additive, accumulate, keyTimes, michael@0: // keySplines) michael@0: // michael@0: class nsSMILAnimationFunction michael@0: { michael@0: public: michael@0: nsSMILAnimationFunction(); michael@0: michael@0: /* michael@0: * Sets the owning animation element which this class uses to query attribute michael@0: * values and compare document positions. michael@0: */ michael@0: void SetAnimationElement(mozilla::dom::SVGAnimationElement* aAnimationElement); michael@0: michael@0: /* michael@0: * Sets animation-specific attributes (or marks them dirty, in the case michael@0: * of from/to/by/values). michael@0: * michael@0: * @param aAttribute The attribute being set michael@0: * @param aValue The updated value of the attribute. michael@0: * @param aResult The nsAttrValue object that may be used for storing the michael@0: * parsed result. michael@0: * @param aParseResult Outparam used for reporting parse errors. Will be set michael@0: * to NS_OK if everything succeeds. michael@0: * @return true if aAttribute is a recognized animation-related michael@0: * attribute; false otherwise. michael@0: */ michael@0: virtual bool SetAttr(nsIAtom* aAttribute, const nsAString& aValue, michael@0: nsAttrValue& aResult, nsresult* aParseResult = nullptr); michael@0: michael@0: /* michael@0: * Unsets the given attribute. michael@0: * michael@0: * @returns true if aAttribute is a recognized animation-related michael@0: * attribute; false otherwise. michael@0: */ michael@0: virtual bool UnsetAttr(nsIAtom* aAttribute); michael@0: michael@0: /** michael@0: * Indicate a new sample has occurred. michael@0: * michael@0: * @param aSampleTime The sample time for this timed element expressed in michael@0: * simple time. michael@0: * @param aSimpleDuration The simple duration for this timed element. michael@0: * @param aRepeatIteration The repeat iteration for this sample. The first michael@0: * iteration has a value of 0. michael@0: */ michael@0: void SampleAt(nsSMILTime aSampleTime, michael@0: const nsSMILTimeValue& aSimpleDuration, michael@0: uint32_t aRepeatIteration); michael@0: michael@0: /** michael@0: * Indicate to sample using the last value defined for the animation function. michael@0: * This value is not normally sampled due to the end-point exclusive timing michael@0: * model but only occurs when the fill mode is "freeze" and the active michael@0: * duration is an even multiple of the simple duration. michael@0: * michael@0: * @param aRepeatIteration The repeat iteration for this sample. The first michael@0: * iteration has a value of 0. michael@0: */ michael@0: void SampleLastValue(uint32_t aRepeatIteration); michael@0: michael@0: /** michael@0: * Indicate that this animation is now active. This is used to instruct the michael@0: * animation function that it should now add its result to the animation michael@0: * sandwich. The begin time is also provided for proper prioritization of michael@0: * animation functions, and for this reason, this method must be called michael@0: * before either of the Sample methods. michael@0: * michael@0: * @param aBeginTime The begin time for the newly active interval. michael@0: */ michael@0: void Activate(nsSMILTime aBeginTime); michael@0: michael@0: /** michael@0: * Indicate that this animation is no longer active. This is used to instruct michael@0: * the animation function that it should no longer add its result to the michael@0: * animation sandwich. michael@0: * michael@0: * @param aIsFrozen true if this animation should continue to contribute michael@0: * to the animation sandwich using the most recent sample michael@0: * parameters. michael@0: */ michael@0: void Inactivate(bool aIsFrozen); michael@0: michael@0: /** michael@0: * Combines the result of this animation function for the last sample with the michael@0: * specified value. michael@0: * michael@0: * @param aSMILAttr This animation's target attribute. Used here for michael@0: * doing attribute-specific parsing of from/to/by/values. michael@0: * michael@0: * @param aResult The value to compose with. michael@0: */ michael@0: void ComposeResult(const nsISMILAttr& aSMILAttr, nsSMILValue& aResult); michael@0: michael@0: /** michael@0: * Returns the relative priority of this animation to another. The priority is michael@0: * used for determining the position of the animation in the animation michael@0: * sandwich -- higher priority animations are applied on top of lower michael@0: * priority animations. michael@0: * michael@0: * @return -1 if this animation has lower priority or 1 if this animation has michael@0: * higher priority michael@0: * michael@0: * This method should never return any other value, including 0. michael@0: */ michael@0: int8_t CompareTo(const nsSMILAnimationFunction* aOther) const; michael@0: michael@0: /* michael@0: * The following methods are provided so that the compositor can optimize its michael@0: * operations by only composing those animation that will affect the final michael@0: * result. michael@0: */ michael@0: michael@0: /** michael@0: * Indicates if the animation is currently active or frozen. Inactive michael@0: * animations will not contribute to the composed result. michael@0: * michael@0: * @return true if the animation is active or frozen, false otherwise. michael@0: */ michael@0: bool IsActiveOrFrozen() const michael@0: { michael@0: /* michael@0: * - Frozen animations should be considered active for the purposes of michael@0: * compositing. michael@0: * - This function does not assume that our nsSMILValues (by/from/to/values) michael@0: * have already been parsed. michael@0: */ michael@0: return (mIsActive || mIsFrozen); michael@0: } michael@0: michael@0: /** michael@0: * Indicates if the animation is active. michael@0: * michael@0: * @return true if the animation is active, false otherwise. michael@0: */ michael@0: bool IsActive() const { michael@0: return mIsActive; michael@0: } michael@0: michael@0: /** michael@0: * Indicates if this animation will replace the passed in result rather than michael@0: * adding to it. Animations that replace the underlying value may be called michael@0: * without first calling lower priority animations. michael@0: * michael@0: * @return True if the animation will replace, false if it will add or michael@0: * otherwise build on the passed in value. michael@0: */ michael@0: virtual bool WillReplace() const; michael@0: michael@0: /** michael@0: * Indicates if the parameters for this animation have changed since the last michael@0: * time it was composited. This allows rendering to be performed only when michael@0: * necessary, particularly when no animations are active. michael@0: * michael@0: * Note that the caller is responsible for determining if the animation michael@0: * target has changed (with help from my UpdateCachedTarget() method). michael@0: * michael@0: * @return true if the animation parameters have changed, false michael@0: * otherwise. michael@0: */ michael@0: bool HasChanged() const; michael@0: michael@0: /** michael@0: * This method lets us clear the 'HasChanged' flag for inactive animations michael@0: * after we've reacted to their change to the 'inactive' state, so that we michael@0: * won't needlessly recompose their targets in every sample. michael@0: * michael@0: * This should only be called on an animation function that is inactive and michael@0: * that returns true from HasChanged(). michael@0: */ michael@0: void ClearHasChanged() michael@0: { michael@0: NS_ABORT_IF_FALSE(HasChanged(), michael@0: "clearing mHasChanged flag, when it's already false"); michael@0: NS_ABORT_IF_FALSE(!IsActiveOrFrozen(), michael@0: "clearing mHasChanged flag for active animation"); michael@0: mHasChanged = false; michael@0: } michael@0: michael@0: /** michael@0: * Updates the cached record of our animation target, and returns a boolean michael@0: * that indicates whether the target has changed since the last call to this michael@0: * function. (This lets nsSMILCompositor check whether its animation michael@0: * functions have changed value or target since the last sample. If none of michael@0: * them have, then the compositor doesn't need to do anything.) michael@0: * michael@0: * @param aNewTarget A nsSMILTargetIdentifier representing the animation michael@0: * target of this function for this sample. michael@0: * @return true if |aNewTarget| is different from the old cached value; michael@0: * otherwise, false. michael@0: */ michael@0: bool UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget); michael@0: michael@0: /** michael@0: * Returns true if this function was skipped in the previous sample (because michael@0: * there was a higher-priority non-additive animation). If a skipped animation michael@0: * function is later used, then the animation sandwich must be recomposited. michael@0: */ michael@0: bool WasSkippedInPrevSample() const { michael@0: return mWasSkippedInPrevSample; michael@0: } michael@0: michael@0: /** michael@0: * Mark this animation function as having been skipped. By marking the michael@0: * function as skipped, if it is used in a subsequent sample we'll know to michael@0: * recomposite the sandwich. michael@0: */ michael@0: void SetWasSkipped() { michael@0: mWasSkippedInPrevSample = true; michael@0: } michael@0: michael@0: // Comparator utility class, used for sorting nsSMILAnimationFunctions michael@0: class Comparator { michael@0: public: michael@0: bool Equals(const nsSMILAnimationFunction* aElem1, michael@0: const nsSMILAnimationFunction* aElem2) const { michael@0: return (aElem1->CompareTo(aElem2) == 0); michael@0: } michael@0: bool LessThan(const nsSMILAnimationFunction* aElem1, michael@0: const nsSMILAnimationFunction* aElem2) const { michael@0: return (aElem1->CompareTo(aElem2) < 0); michael@0: } michael@0: }; michael@0: michael@0: protected: michael@0: // Typedefs michael@0: typedef FallibleTArray nsSMILValueArray; michael@0: michael@0: // Types michael@0: enum nsSMILCalcMode michael@0: { michael@0: CALC_LINEAR, michael@0: CALC_DISCRETE, michael@0: CALC_PACED, michael@0: CALC_SPLINE michael@0: }; michael@0: michael@0: // Used for sorting nsSMILAnimationFunctions michael@0: nsSMILTime GetBeginTime() const { return mBeginTime; } michael@0: michael@0: // Property getters michael@0: bool GetAccumulate() const; michael@0: bool GetAdditive() const; michael@0: virtual nsSMILCalcMode GetCalcMode() const; michael@0: michael@0: // Property setters michael@0: nsresult SetAccumulate(const nsAString& aAccumulate, nsAttrValue& aResult); michael@0: nsresult SetAdditive(const nsAString& aAdditive, nsAttrValue& aResult); michael@0: nsresult SetCalcMode(const nsAString& aCalcMode, nsAttrValue& aResult); michael@0: nsresult SetKeyTimes(const nsAString& aKeyTimes, nsAttrValue& aResult); michael@0: nsresult SetKeySplines(const nsAString& aKeySplines, nsAttrValue& aResult); michael@0: michael@0: // Property un-setters michael@0: void UnsetAccumulate(); michael@0: void UnsetAdditive(); michael@0: void UnsetCalcMode(); michael@0: void UnsetKeyTimes(); michael@0: void UnsetKeySplines(); michael@0: michael@0: // Helpers michael@0: virtual nsresult InterpolateResult(const nsSMILValueArray& aValues, michael@0: nsSMILValue& aResult, michael@0: nsSMILValue& aBaseValue); michael@0: nsresult AccumulateResult(const nsSMILValueArray& aValues, michael@0: nsSMILValue& aResult); michael@0: michael@0: nsresult ComputePacedPosition(const nsSMILValueArray& aValues, michael@0: double aSimpleProgress, michael@0: double& aIntervalProgress, michael@0: const nsSMILValue*& aFrom, michael@0: const nsSMILValue*& aTo); michael@0: double ComputePacedTotalDistance(const nsSMILValueArray& aValues) const; michael@0: michael@0: /** michael@0: * Adjust the simple progress, that is, the point within the simple duration, michael@0: * by applying any keyTimes. michael@0: */ michael@0: double ScaleSimpleProgress(double aProgress, nsSMILCalcMode aCalcMode); michael@0: /** michael@0: * Adjust the progress within an interval, that is, between two animation michael@0: * values, by applying any keySplines. michael@0: */ michael@0: double ScaleIntervalProgress(double aProgress, uint32_t aIntervalIndex); michael@0: michael@0: // Convenience attribute getters -- use these instead of querying michael@0: // mAnimationElement as these may need to be overridden by subclasses michael@0: virtual bool HasAttr(nsIAtom* aAttName) const; michael@0: virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const; michael@0: virtual bool GetAttr(nsIAtom* aAttName, michael@0: nsAString& aResult) const; michael@0: michael@0: bool ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr, michael@0: nsSMILValue& aResult, michael@0: bool& aPreventCachingOfSandwich) const; michael@0: michael@0: virtual nsresult GetValues(const nsISMILAttr& aSMILAttr, michael@0: nsSMILValueArray& aResult); michael@0: michael@0: virtual void CheckValueListDependentAttrs(uint32_t aNumValues); michael@0: void CheckKeyTimes(uint32_t aNumValues); michael@0: void CheckKeySplines(uint32_t aNumValues); michael@0: michael@0: virtual bool IsToAnimation() const { michael@0: return !HasAttr(nsGkAtoms::values) && michael@0: HasAttr(nsGkAtoms::to) && michael@0: !HasAttr(nsGkAtoms::from); michael@0: } michael@0: michael@0: // Returns true if we know our composited value won't change over the michael@0: // simple duration of this animation (for a fixed base value). michael@0: virtual bool IsValueFixedForSimpleDuration() const; michael@0: michael@0: inline bool IsAdditive() const { michael@0: /* michael@0: * Animation is additive if: michael@0: * michael@0: * (1) additive = "sum" (GetAdditive() == true), or michael@0: * (2) it is 'by animation' (by is set, from and values are not) michael@0: * michael@0: * Although animation is not additive if it is 'to animation' michael@0: */ michael@0: bool isByAnimation = (!HasAttr(nsGkAtoms::values) && michael@0: HasAttr(nsGkAtoms::by) && michael@0: !HasAttr(nsGkAtoms::from)); michael@0: return !IsToAnimation() && (GetAdditive() || isByAnimation); michael@0: } michael@0: michael@0: // Setters for error flags michael@0: // These correspond to bit-indices in mErrorFlags, for tracking parse errors michael@0: // in these attributes, when those parse errors should block us from doing michael@0: // animation. michael@0: enum AnimationAttributeIdx { michael@0: BF_ACCUMULATE = 0, michael@0: BF_ADDITIVE = 1, michael@0: BF_CALC_MODE = 2, michael@0: BF_KEY_TIMES = 3, michael@0: BF_KEY_SPLINES = 4, michael@0: BF_KEY_POINTS = 5 // only michael@0: }; michael@0: michael@0: inline void SetAccumulateErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_ACCUMULATE, aNewValue); michael@0: } michael@0: inline void SetAdditiveErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_ADDITIVE, aNewValue); michael@0: } michael@0: inline void SetCalcModeErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_CALC_MODE, aNewValue); michael@0: } michael@0: inline void SetKeyTimesErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_KEY_TIMES, aNewValue); michael@0: } michael@0: inline void SetKeySplinesErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_KEY_SPLINES, aNewValue); michael@0: } michael@0: inline void SetKeyPointsErrorFlag(bool aNewValue) { michael@0: SetErrorFlag(BF_KEY_POINTS, aNewValue); michael@0: } michael@0: inline void SetErrorFlag(AnimationAttributeIdx aField, bool aValue) { michael@0: if (aValue) { michael@0: mErrorFlags |= (0x01 << aField); michael@0: } else { michael@0: mErrorFlags &= ~(0x01 << aField); michael@0: } michael@0: } michael@0: michael@0: // Members michael@0: // ------- michael@0: michael@0: static nsAttrValue::EnumTable sAdditiveTable[]; michael@0: static nsAttrValue::EnumTable sCalcModeTable[]; michael@0: static nsAttrValue::EnumTable sAccumulateTable[]; michael@0: michael@0: FallibleTArray mKeyTimes; michael@0: FallibleTArray mKeySplines; michael@0: michael@0: // These are the parameters provided by the previous sample. Currently we michael@0: // perform lazy calculation. That is, we only calculate the result if and when michael@0: // instructed by the compositor. This allows us to apply the result directly michael@0: // to the animation value and allows the compositor to filter out functions michael@0: // that it determines will not contribute to the final result. michael@0: nsSMILTime mSampleTime; // sample time within simple dur michael@0: nsSMILTimeValue mSimpleDuration; michael@0: uint32_t mRepeatIteration; michael@0: michael@0: nsSMILTime mBeginTime; // document time michael@0: michael@0: // The owning animation element. This is used for sorting based on document michael@0: // position and for fetching attribute values stored in the element. michael@0: // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive michael@0: // its owning animation element. michael@0: mozilla::dom::SVGAnimationElement* mAnimationElement; michael@0: michael@0: // Which attributes have been set but have had errors. This is not used for michael@0: // all attributes but only those which have specified error behaviour michael@0: // associated with them. michael@0: uint16_t mErrorFlags; michael@0: michael@0: // Allows us to check whether an animation function has changed target from michael@0: // sample to sample (because if neither target nor animated value have michael@0: // changed, we don't have to do anything). michael@0: nsSMILWeakTargetIdentifier mLastTarget; michael@0: michael@0: // Boolean flags michael@0: bool mIsActive:1; michael@0: bool mIsFrozen:1; michael@0: bool mLastValue:1; michael@0: bool mHasChanged:1; michael@0: bool mValueNeedsReparsingEverySample:1; michael@0: bool mPrevSampleWasSingleValueAnimation:1; michael@0: bool mWasSkippedInPrevSample:1; michael@0: }; michael@0: michael@0: #endif // NS_SMILANIMATIONFUNCTION_H_