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