DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (31ec81b5d7bb)

VCS Links

Line Code
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
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 NS_SMILANIMATIONFUNCTION_H_
#define NS_SMILANIMATIONFUNCTION_H_

#include "nsISMILAttr.h"
#include "nsGkAtoms.h"
#include "nsString.h"
#include "nsSMILTargetIdentifier.h"
#include "nsSMILTimeValue.h"
#include "nsSMILKeySpline.h"
#include "nsSMILValue.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "nsAttrValue.h"
#include "nsSMILTypes.h"

namespace mozilla {
namespace dom {
class SVGAnimationElement;
}
}

//----------------------------------------------------------------------
// nsSMILAnimationFunction
//
// 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 nsSMILAnimationFunction
{
public:
  nsSMILAnimationFunction();

  /*
   * 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(nsIAtom* 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(nsIAtom* 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(nsSMILTime aSampleTime,
                const nsSMILTimeValue& 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(nsSMILTime 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 nsISMILAttr& aSMILAttr, nsSMILValue& 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 nsSMILAnimationFunction* 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 nsSMILValues (by/from/to/values)
     * have already been parsed.
     */
    return (mIsActive || mIsFrozen);
  }

  /**
   * 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()
  {
    NS_ABORT_IF_FALSE(HasChanged(),
                      "clearing mHasChanged flag, when it's already false");
    NS_ABORT_IF_FALSE(!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 nsSMILCompositor 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 nsSMILTargetIdentifier 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 nsSMILTargetIdentifier& 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;
  }

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

protected:
  // Typedefs
  typedef nsTArray<nsSMILValue> nsSMILValueArray;

  // Types
  enum nsSMILCalcMode
  {
    CALC_LINEAR,
    CALC_DISCRETE,
    CALC_PACED,
    CALC_SPLINE
  };

  // Used for sorting nsSMILAnimationFunctions
  nsSMILTime GetBeginTime() const { return mBeginTime; }

  // Property getters
  bool                   GetAccumulate() const;
  bool                   GetAdditive() const;
  virtual nsSMILCalcMode 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 nsresult InterpolateResult(const nsSMILValueArray& aValues,
                                     nsSMILValue& aResult,
                                     nsSMILValue& aBaseValue);
  nsresult AccumulateResult(const nsSMILValueArray& aValues,
                            nsSMILValue& aResult);

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

  /**
   * Adjust the simple progress, that is, the point within the simple duration,
   * by applying any keyTimes.
   */
  double   ScaleSimpleProgress(double aProgress, nsSMILCalcMode 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 -- use these instead of querying
  // mAnimationElement as these may need to be overridden by subclasses
  virtual bool               HasAttr(nsIAtom* aAttName) const;
  virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
  virtual bool               GetAttr(nsIAtom* aAttName,
                                     nsAString& aResult) const;

  bool     ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr,
                     nsSMILValue& aResult,
                     bool& aPreventCachingOfSandwich) const;

  virtual nsresult GetValues(const nsISMILAttr& aSMILAttr,
                             nsSMILValueArray& 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[];

  nsTArray<double>              mKeyTimes;
  nsTArray<nsSMILKeySpline>     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.
  nsSMILTime                    mSampleTime; // sample time within simple dur
  nsSMILTimeValue               mSimpleDuration;
  uint32_t                      mRepeatIteration;

  nsSMILTime                    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 nsSMILAnimationFunction 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).
  nsSMILWeakTargetIdentifier    mLastTarget;

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

#endif // NS_SMILANIMATIONFUNCTION_H_