summaryrefslogtreecommitdiffstats
path: root/dom/smil/SMILAnimationFunction.h
blob: 8e85da76f6170be599b03018f027d5ff38db614e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef DOM_SMIL_SMILANIMATIONFUNCTION_H_
#define DOM_SMIL_SMILANIMATIONFUNCTION_H_

#include "mozilla/SMILAttr.h"
#include "mozilla/SMILKeySpline.h"
#include "mozilla/SMILTargetIdentifier.h"
#include "mozilla/SMILTimeValue.h"
#include "mozilla/SMILTypes.h"
#include "mozilla/SMILValue.h"
#include "nsAttrValue.h"
#include "nsGkAtoms.h"
#include "nsString.h"
#include "nsTArray.h"

namespace mozilla {
namespace dom {
class SVGAnimationElement;
}  // namespace dom

//----------------------------------------------------------------------
// SMILAnimationFunction
//
// The animation function calculates animation values. It it is provided with
// time parameters (sample time, repeat iteration etc.) and it uses this to
// build an appropriate animation value by performing interpolation and
// addition operations.
//
// It is responsible for implementing the animation parameters of an animation
// element (e.g. from, by, to, values, calcMode, additive, accumulate, keyTimes,
// keySplines)
//
class SMILAnimationFunction {
 public:
  SMILAnimationFunction();

  /*
   * Sets the owning animation element which this class uses to query attribute
   * values and compare document positions.
   */
  void SetAnimationElement(
      mozilla::dom::SVGAnimationElement* aAnimationElement);

  /*
   * Sets animation-specific attributes (or marks them dirty, in the case
   * of from/to/by/values).
   *
   * @param aAttribute The attribute being set
   * @param aValue     The updated value of the attribute.
   * @param aResult    The nsAttrValue object that may be used for storing the
   *                   parsed result.
   * @param aParseResult  Outparam used for reporting parse errors. Will be set
   *                      to NS_OK if everything succeeds.
   * @return  true if aAttribute is a recognized animation-related
   *          attribute; false otherwise.
   */
  virtual bool SetAttr(nsAtom* aAttribute, const nsAString& aValue,
                       nsAttrValue& aResult, nsresult* aParseResult = nullptr);

  /*
   * Unsets the given attribute.
   *
   * @returns true if aAttribute is a recognized animation-related
   *          attribute; false otherwise.
   */
  virtual bool UnsetAttr(nsAtom* aAttribute);

  /**
   * Indicate a new sample has occurred.
   *
   * @param aSampleTime The sample time for this timed element expressed in
   *                    simple time.
   * @param aSimpleDuration The simple duration for this timed element.
   * @param aRepeatIteration  The repeat iteration for this sample. The first
   *                          iteration has a value of 0.
   */
  void SampleAt(SMILTime aSampleTime, const SMILTimeValue& aSimpleDuration,
                uint32_t aRepeatIteration);

  /**
   * Indicate to sample using the last value defined for the animation function.
   * This value is not normally sampled due to the end-point exclusive timing
   * model but only occurs when the fill mode is "freeze" and the active
   * duration is an even multiple of the simple duration.
   *
   * @param aRepeatIteration  The repeat iteration for this sample. The first
   *                          iteration has a value of 0.
   */
  void SampleLastValue(uint32_t aRepeatIteration);

  /**
   * Indicate that this animation is now active. This is used to instruct the
   * animation function that it should now add its result to the animation
   * sandwich. The begin time is also provided for proper prioritization of
   * animation functions, and for this reason, this method must be called
   * before either of the Sample methods.
   *
   * @param aBeginTime The begin time for the newly active interval.
   */
  void Activate(SMILTime aBeginTime);

  /**
   * Indicate that this animation is no longer active. This is used to instruct
   * the animation function that it should no longer add its result to the
   * animation sandwich.
   *
   * @param aIsFrozen true if this animation should continue to contribute
   *                  to the animation sandwich using the most recent sample
   *                  parameters.
   */
  void Inactivate(bool aIsFrozen);

  /**
   * Combines the result of this animation function for the last sample with the
   * specified value.
   *
   * @param aSMILAttr This animation's target attribute. Used here for
   *                  doing attribute-specific parsing of from/to/by/values.
   *
   * @param aResult   The value to compose with.
   */
  void ComposeResult(const SMILAttr& aSMILAttr, SMILValue& aResult);

  /**
   * Returns the relative priority of this animation to another. The priority is
   * used for determining the position of the animation in the animation
   * sandwich -- higher priority animations are applied on top of lower
   * priority animations.
   *
   * @return  -1 if this animation has lower priority or 1 if this animation has
   *          higher priority
   *
   * This method should never return any other value, including 0.
   */
  int8_t CompareTo(const SMILAnimationFunction* aOther) const;

  /*
   * The following methods are provided so that the compositor can optimize its
   * operations by only composing those animation that will affect the final
   * result.
   */

  /**
   * Indicates if the animation is currently active or frozen. Inactive
   * animations will not contribute to the composed result.
   *
   * @return  true if the animation is active or frozen, false otherwise.
   */
  bool IsActiveOrFrozen() const {
    /*
     * - Frozen animations should be considered active for the purposes of
     * compositing.
     * - This function does not assume that our SMILValues (by/from/to/values)
     * have already been parsed.
     */
    return (mIsActive || mIsFrozen);
  }

  /**
   * Indicates if the animation is active.
   *
   * @return  true if the animation is active, false otherwise.
   */
  bool IsActive() const { return mIsActive; }

  /**
   * Indicates if this animation will replace the passed in result rather than
   * adding to it. Animations that replace the underlying value may be called
   * without first calling lower priority animations.
   *
   * @return  True if the animation will replace, false if it will add or
   *          otherwise build on the passed in value.
   */
  virtual bool WillReplace() const;

  /**
   * Indicates if the parameters for this animation have changed since the last
   * time it was composited. This allows rendering to be performed only when
   * necessary, particularly when no animations are active.
   *
   * Note that the caller is responsible for determining if the animation
   * target has changed (with help from my UpdateCachedTarget() method).
   *
   * @return  true if the animation parameters have changed, false
   *          otherwise.
   */
  bool HasChanged() const;

  /**
   * This method lets us clear the 'HasChanged' flag for inactive animations
   * after we've reacted to their change to the 'inactive' state, so that we
   * won't needlessly recompose their targets in every sample.
   *
   * This should only be called on an animation function that is inactive and
   * that returns true from HasChanged().
   */
  void ClearHasChanged() {
    MOZ_ASSERT(HasChanged(),
               "clearing mHasChanged flag, when it's already false");
    MOZ_ASSERT(!IsActiveOrFrozen(),
               "clearing mHasChanged flag for active animation");
    mHasChanged = false;
  }

  /**
   * Updates the cached record of our animation target, and returns a boolean
   * that indicates whether the target has changed since the last call to this
   * function. (This lets SMILCompositor check whether its animation
   * functions have changed value or target since the last sample.  If none of
   * them have, then the compositor doesn't need to do anything.)
   *
   * @param aNewTarget A SMILTargetIdentifier representing the animation
   *                   target of this function for this sample.
   * @return  true if |aNewTarget| is different from the old cached value;
   *          otherwise, false.
   */
  bool UpdateCachedTarget(const SMILTargetIdentifier& aNewTarget);

  /**
   * Returns true if this function was skipped in the previous sample (because
   * there was a higher-priority non-additive animation). If a skipped animation
   * function is later used, then the animation sandwich must be recomposited.
   */
  bool WasSkippedInPrevSample() const { return mWasSkippedInPrevSample; }

  /**
   * Mark this animation function as having been skipped. By marking the
   * function as skipped, if it is used in a subsequent sample we'll know to
   * recomposite the sandwich.
   */
  void SetWasSkipped() { mWasSkippedInPrevSample = true; }

  /**
   * Returns true if we need to recalculate the animation value on every sample.
   * (e.g. because it depends on context like the font-size)
   */
  bool ValueNeedsReparsingEverySample() const {
    return mValueNeedsReparsingEverySample;
  }

  // Comparator utility class, used for sorting SMILAnimationFunctions
  class Comparator {
   public:
    bool Equals(const SMILAnimationFunction* aElem1,
                const SMILAnimationFunction* aElem2) const {
      return (aElem1->CompareTo(aElem2) == 0);
    }
    bool LessThan(const SMILAnimationFunction* aElem1,
                  const SMILAnimationFunction* aElem2) const {
      return (aElem1->CompareTo(aElem2) < 0);
    }
  };

 protected:
  // alias declarations
  using SMILValueArray = FallibleTArray<SMILValue>;

  // Types
  enum SMILCalcMode : uint8_t {
    CALC_LINEAR,
    CALC_DISCRETE,
    CALC_PACED,
    CALC_SPLINE
  };

  // Used for sorting SMILAnimationFunctions
  SMILTime GetBeginTime() const { return mBeginTime; }

  // Property getters
  bool GetAccumulate() const;
  bool GetAdditive() const;
  virtual SMILCalcMode GetCalcMode() const;

  // Property setters
  nsresult SetAccumulate(const nsAString& aAccumulate, nsAttrValue& aResult);
  nsresult SetAdditive(const nsAString& aAdditive, nsAttrValue& aResult);
  nsresult SetCalcMode(const nsAString& aCalcMode, nsAttrValue& aResult);
  nsresult SetKeyTimes(const nsAString& aKeyTimes, nsAttrValue& aResult);
  nsresult SetKeySplines(const nsAString& aKeySplines, nsAttrValue& aResult);

  // Property un-setters
  void UnsetAccumulate();
  void UnsetAdditive();
  void UnsetCalcMode();
  void UnsetKeyTimes();
  void UnsetKeySplines();

  // Helpers
  virtual bool IsDisallowedAttribute(const nsAtom* aAttribute) const {
    return false;
  }
  virtual nsresult InterpolateResult(const SMILValueArray& aValues,
                                     SMILValue& aResult, SMILValue& aBaseValue);
  nsresult AccumulateResult(const SMILValueArray& aValues, SMILValue& aResult);

  nsresult ComputePacedPosition(const SMILValueArray& aValues,
                                double aSimpleProgress,
                                double& aIntervalProgress,
                                const SMILValue*& aFrom, const SMILValue*& aTo);
  double ComputePacedTotalDistance(const SMILValueArray& aValues) const;

  /**
   * Adjust the simple progress, that is, the point within the simple duration,
   * by applying any keyTimes.
   */
  double ScaleSimpleProgress(double aProgress, SMILCalcMode aCalcMode);
  /**
   * Adjust the progress within an interval, that is, between two animation
   * values, by applying any keySplines.
   */
  double ScaleIntervalProgress(double aProgress, uint32_t aIntervalIndex);

  // Convenience attribute getters
  bool HasAttr(nsAtom* aAttName) const;
  const nsAttrValue* GetAttr(nsAtom* aAttName) const;
  bool GetAttr(nsAtom* aAttName, nsAString& aResult) const;

  bool ParseAttr(nsAtom* aAttName, const SMILAttr& aSMILAttr,
                 SMILValue& aResult, bool& aPreventCachingOfSandwich) const;

  virtual nsresult GetValues(const SMILAttr& aSMILAttr,
                             SMILValueArray& aResult);

  virtual void CheckValueListDependentAttrs(uint32_t aNumValues);
  void CheckKeyTimes(uint32_t aNumValues);
  void CheckKeySplines(uint32_t aNumValues);

  virtual bool IsToAnimation() const {
    return !HasAttr(nsGkAtoms::values) && HasAttr(nsGkAtoms::to) &&
           !HasAttr(nsGkAtoms::from);
  }

  // Returns true if we know our composited value won't change over the
  // simple duration of this animation (for a fixed base value).
  virtual bool IsValueFixedForSimpleDuration() const;

  inline bool IsAdditive() const {
    /*
     * Animation is additive if:
     *
     * (1) additive = "sum" (GetAdditive() == true), or
     * (2) it is 'by animation' (by is set, from and values are not)
     *
     * Although animation is not additive if it is 'to animation'
     */
    bool isByAnimation = (!HasAttr(nsGkAtoms::values) &&
                          HasAttr(nsGkAtoms::by) && !HasAttr(nsGkAtoms::from));
    return !IsToAnimation() && (GetAdditive() || isByAnimation);
  }

  // Setters for error flags
  // These correspond to bit-indices in mErrorFlags, for tracking parse errors
  // in these attributes, when those parse errors should block us from doing
  // animation.
  enum AnimationAttributeIdx {
    BF_ACCUMULATE = 0,
    BF_ADDITIVE = 1,
    BF_CALC_MODE = 2,
    BF_KEY_TIMES = 3,
    BF_KEY_SPLINES = 4,
    BF_KEY_POINTS = 5  // <animateMotion> only
  };

  inline void SetAccumulateErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_ACCUMULATE, aNewValue);
  }
  inline void SetAdditiveErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_ADDITIVE, aNewValue);
  }
  inline void SetCalcModeErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_CALC_MODE, aNewValue);
  }
  inline void SetKeyTimesErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_KEY_TIMES, aNewValue);
  }
  inline void SetKeySplinesErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_KEY_SPLINES, aNewValue);
  }
  inline void SetKeyPointsErrorFlag(bool aNewValue) {
    SetErrorFlag(BF_KEY_POINTS, aNewValue);
  }
  inline void SetErrorFlag(AnimationAttributeIdx aField, bool aValue) {
    if (aValue) {
      mErrorFlags |= (0x01 << aField);
    } else {
      mErrorFlags &= ~(0x01 << aField);
    }
  }

  // Members
  // -------

  static nsAttrValue::EnumTable sAdditiveTable[];
  static nsAttrValue::EnumTable sCalcModeTable[];
  static nsAttrValue::EnumTable sAccumulateTable[];

  FallibleTArray<double> mKeyTimes;
  FallibleTArray<SMILKeySpline> mKeySplines;

  // These are the parameters provided by the previous sample. Currently we
  // perform lazy calculation. That is, we only calculate the result if and when
  // instructed by the compositor. This allows us to apply the result directly
  // to the animation value and allows the compositor to filter out functions
  // that it determines will not contribute to the final result.
  SMILTime mSampleTime;  // sample time within simple dur
  SMILTimeValue mSimpleDuration;
  uint32_t mRepeatIteration;

  SMILTime mBeginTime;  // document time

  // The owning animation element. This is used for sorting based on document
  // position and for fetching attribute values stored in the element.
  // Raw pointer is OK here, because this SMILAnimationFunction can't outlive
  // its owning animation element.
  mozilla::dom::SVGAnimationElement* mAnimationElement;

  // Which attributes have been set but have had errors. This is not used for
  // all attributes but only those which have specified error behaviour
  // associated with them.
  uint16_t mErrorFlags;

  // Allows us to check whether an animation function has changed target from
  // sample to sample (because if neither target nor animated value have
  // changed, we don't have to do anything).
  SMILWeakTargetIdentifier mLastTarget;

  // Boolean flags
  bool mIsActive : 1;
  bool mIsFrozen : 1;
  bool mLastValue : 1;
  bool mHasChanged : 1;
  bool mValueNeedsReparsingEverySample : 1;
  bool mPrevSampleWasSingleValueAnimation : 1;
  bool mWasSkippedInPrevSample : 1;
};

}  // namespace mozilla

#endif  // DOM_SMIL_SMILANIMATIONFUNCTION_H_