1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Mozilla SMIL module.
16 : *
17 : * The Initial Developer of the Original Code is Brian Birtles.
18 : * Portions created by the Initial Developer are Copyright (C) 2005
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Brian Birtles <birtles@gmail.com>
23 : * Daniel Holbert <dholbert@mozilla.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifndef NS_SMILANIMATIONFUNCTION_H_
40 : #define NS_SMILANIMATIONFUNCTION_H_
41 :
42 : #include "nsISMILAttr.h"
43 : #include "nsGkAtoms.h"
44 : #include "nsString.h"
45 : #include "nsSMILTargetIdentifier.h"
46 : #include "nsSMILTimeValue.h"
47 : #include "nsSMILKeySpline.h"
48 : #include "nsSMILValue.h"
49 : #include "nsAutoPtr.h"
50 : #include "nsTArray.h"
51 : #include "nsAttrValue.h"
52 : #include "nsSMILTypes.h"
53 :
54 : class nsISMILAnimationElement;
55 :
56 : //----------------------------------------------------------------------
57 : // nsSMILAnimationFunction
58 : //
59 : // The animation function calculates animation values. It it is provided with
60 : // time parameters (sample time, repeat iteration etc.) and it uses this to
61 : // build an appropriate animation value by performing interpolation and
62 : // addition operations.
63 : //
64 : // It is responsible for implementing the animation parameters of an animation
65 : // element (e.g. from, by, to, values, calcMode, additive, accumulate, keyTimes,
66 : // keySplines)
67 : //
68 : class nsSMILAnimationFunction
69 0 : {
70 : public:
71 : nsSMILAnimationFunction();
72 :
73 : /*
74 : * Sets the owning animation element which this class uses to query attribute
75 : * values and compare document positions.
76 : */
77 : void SetAnimationElement(nsISMILAnimationElement* aAnimationElement);
78 :
79 : /*
80 : * Sets animation-specific attributes (or marks them dirty, in the case
81 : * of from/to/by/values).
82 : *
83 : * @param aAttribute The attribute being set
84 : * @param aValue The updated value of the attribute.
85 : * @param aResult The nsAttrValue object that may be used for storing the
86 : * parsed result.
87 : * @param aParseResult Outparam used for reporting parse errors. Will be set
88 : * to NS_OK if everything succeeds.
89 : * @return true if aAttribute is a recognized animation-related
90 : * attribute; false otherwise.
91 : */
92 : virtual bool SetAttr(nsIAtom* aAttribute, const nsAString& aValue,
93 : nsAttrValue& aResult, nsresult* aParseResult = nsnull);
94 :
95 : /*
96 : * Unsets the given attribute.
97 : *
98 : * @returns true if aAttribute is a recognized animation-related
99 : * attribute; false otherwise.
100 : */
101 : virtual bool UnsetAttr(nsIAtom* aAttribute);
102 :
103 : /**
104 : * Indicate a new sample has occurred.
105 : *
106 : * @param aSampleTime The sample time for this timed element expressed in
107 : * simple time.
108 : * @param aSimpleDuration The simple duration for this timed element.
109 : * @param aRepeatIteration The repeat iteration for this sample. The first
110 : * iteration has a value of 0.
111 : */
112 : void SampleAt(nsSMILTime aSampleTime,
113 : const nsSMILTimeValue& aSimpleDuration,
114 : PRUint32 aRepeatIteration);
115 :
116 : /**
117 : * Indicate to sample using the last value defined for the animation function.
118 : * This value is not normally sampled due to the end-point exclusive timing
119 : * model but only occurs when the fill mode is "freeze" and the active
120 : * duration is an even multiple of the simple duration.
121 : *
122 : * @param aRepeatIteration The repeat iteration for this sample. The first
123 : * iteration has a value of 0.
124 : */
125 : void SampleLastValue(PRUint32 aRepeatIteration);
126 :
127 : /**
128 : * Indicate that this animation is now active. This is used to instruct the
129 : * animation function that it should now add its result to the animation
130 : * sandwich. The begin time is also provided for proper prioritization of
131 : * animation functions, and for this reason, this method must be called
132 : * before either of the Sample methods.
133 : *
134 : * @param aBeginTime The begin time for the newly active interval.
135 : */
136 : void Activate(nsSMILTime aBeginTime);
137 :
138 : /**
139 : * Indicate that this animation is no longer active. This is used to instruct
140 : * the animation function that it should no longer add its result to the
141 : * animation sandwich.
142 : *
143 : * @param aIsFrozen true if this animation should continue to contribute
144 : * to the animation sandwich using the most recent sample
145 : * parameters.
146 : */
147 : void Inactivate(bool aIsFrozen);
148 :
149 : /**
150 : * Combines the result of this animation function for the last sample with the
151 : * specified value.
152 : *
153 : * @param aSMILAttr This animation's target attribute. Used here for
154 : * doing attribute-specific parsing of from/to/by/values.
155 : *
156 : * @param aResult The value to compose with.
157 : */
158 : void ComposeResult(const nsISMILAttr& aSMILAttr, nsSMILValue& aResult);
159 :
160 : /**
161 : * Returns the relative priority of this animation to another. The priority is
162 : * used for determining the position of the animation in the animation
163 : * sandwich -- higher priority animations are applied on top of lower
164 : * priority animations.
165 : *
166 : * @return -1 if this animation has lower priority or 1 if this animation has
167 : * higher priority
168 : *
169 : * This method should never return any other value, including 0.
170 : */
171 : PRInt8 CompareTo(const nsSMILAnimationFunction* aOther) const;
172 :
173 : /*
174 : * The following methods are provided so that the compositor can optimize its
175 : * operations by only composing those animation that will affect the final
176 : * result.
177 : */
178 :
179 : /**
180 : * Indicates if the animation is currently active or frozen. Inactive
181 : * animations will not contribute to the composed result.
182 : *
183 : * @return true if the animation is active or frozen, false otherwise.
184 : */
185 0 : bool IsActiveOrFrozen() const
186 : {
187 : /*
188 : * - Frozen animations should be considered active for the purposes of
189 : * compositing.
190 : * - This function does not assume that our nsSMILValues (by/from/to/values)
191 : * have already been parsed.
192 : */
193 0 : return (mIsActive || mIsFrozen);
194 : }
195 :
196 : /**
197 : * Indicates if this animation will replace the passed in result rather than
198 : * adding to it. Animations that replace the underlying value may be called
199 : * without first calling lower priority animations.
200 : *
201 : * @return True if the animation will replace, false if it will add or
202 : * otherwise build on the passed in value.
203 : */
204 : virtual bool WillReplace() const;
205 :
206 : /**
207 : * Indicates if the parameters for this animation have changed since the last
208 : * time it was composited. This allows rendering to be performed only when
209 : * necessary, particularly when no animations are active.
210 : *
211 : * Note that the caller is responsible for determining if the animation
212 : * target has changed (with help from my UpdateCachedTarget() method).
213 : *
214 : * @return true if the animation parameters have changed, false
215 : * otherwise.
216 : */
217 : bool HasChanged() const;
218 :
219 : /**
220 : * This method lets us clear the 'HasChanged' flag for inactive animations
221 : * after we've reacted to their change to the 'inactive' state, so that we
222 : * won't needlessly recompose their targets in every sample.
223 : *
224 : * This should only be called on an animation function that is inactive and
225 : * that returns true from HasChanged().
226 : */
227 0 : void ClearHasChanged()
228 : {
229 0 : NS_ABORT_IF_FALSE(HasChanged(),
230 : "clearing mHasChanged flag, when it's already false");
231 0 : NS_ABORT_IF_FALSE(!IsActiveOrFrozen(),
232 : "clearing mHasChanged flag for active animation");
233 0 : mHasChanged = false;
234 0 : }
235 :
236 : /**
237 : * Updates the cached record of our animation target, and returns a boolean
238 : * that indicates whether the target has changed since the last call to this
239 : * function. (This lets nsSMILCompositor check whether its animation
240 : * functions have changed value or target since the last sample. If none of
241 : * them have, then the compositor doesn't need to do anything.)
242 : *
243 : * @param aNewTarget A nsSMILTargetIdentifier representing the animation
244 : * target of this function for this sample.
245 : * @return true if |aNewTarget| is different from the old cached value;
246 : * otherwise, false.
247 : */
248 : bool UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget);
249 :
250 : /**
251 : * Returns true if this function was skipped in the previous sample (because
252 : * there was a higher-priority non-additive animation). If a skipped animation
253 : * function is later used, then the animation sandwich must be recomposited.
254 : */
255 0 : bool WasSkippedInPrevSample() const {
256 0 : return mWasSkippedInPrevSample;
257 : }
258 :
259 : /**
260 : * Mark this animation function as having been skipped. By marking the
261 : * function as skipped, if it is used in a subsequent sample we'll know to
262 : * recomposite the sandwich.
263 : */
264 0 : void SetWasSkipped() {
265 0 : mWasSkippedInPrevSample = true;
266 0 : }
267 :
268 : // Comparator utility class, used for sorting nsSMILAnimationFunctions
269 : class Comparator {
270 : public:
271 0 : bool Equals(const nsSMILAnimationFunction* aElem1,
272 : const nsSMILAnimationFunction* aElem2) const {
273 0 : return (aElem1->CompareTo(aElem2) == 0);
274 : }
275 0 : bool LessThan(const nsSMILAnimationFunction* aElem1,
276 : const nsSMILAnimationFunction* aElem2) const {
277 0 : return (aElem1->CompareTo(aElem2) < 0);
278 : }
279 : };
280 :
281 : protected:
282 : // Typedefs
283 : typedef nsTArray<nsSMILValue> nsSMILValueArray;
284 :
285 : // Types
286 : enum nsSMILCalcMode
287 : {
288 : CALC_LINEAR,
289 : CALC_DISCRETE,
290 : CALC_PACED,
291 : CALC_SPLINE
292 : };
293 :
294 : // Used for sorting nsSMILAnimationFunctions
295 0 : nsSMILTime GetBeginTime() const { return mBeginTime; }
296 :
297 : // Property getters
298 : bool GetAccumulate() const;
299 : bool GetAdditive() const;
300 : virtual nsSMILCalcMode GetCalcMode() const;
301 :
302 : // Property setters
303 : nsresult SetAccumulate(const nsAString& aAccumulate, nsAttrValue& aResult);
304 : nsresult SetAdditive(const nsAString& aAdditive, nsAttrValue& aResult);
305 : nsresult SetCalcMode(const nsAString& aCalcMode, nsAttrValue& aResult);
306 : nsresult SetKeyTimes(const nsAString& aKeyTimes, nsAttrValue& aResult);
307 : nsresult SetKeySplines(const nsAString& aKeySplines, nsAttrValue& aResult);
308 :
309 : // Property un-setters
310 : void UnsetAccumulate();
311 : void UnsetAdditive();
312 : void UnsetCalcMode();
313 : void UnsetKeyTimes();
314 : void UnsetKeySplines();
315 :
316 : // Helpers
317 : virtual nsresult InterpolateResult(const nsSMILValueArray& aValues,
318 : nsSMILValue& aResult,
319 : nsSMILValue& aBaseValue);
320 : nsresult AccumulateResult(const nsSMILValueArray& aValues,
321 : nsSMILValue& aResult);
322 :
323 : nsresult ComputePacedPosition(const nsSMILValueArray& aValues,
324 : double aSimpleProgress,
325 : double& aIntervalProgress,
326 : const nsSMILValue*& aFrom,
327 : const nsSMILValue*& aTo);
328 : double ComputePacedTotalDistance(const nsSMILValueArray& aValues) const;
329 :
330 : /**
331 : * Adjust the simple progress, that is, the point within the simple duration,
332 : * by applying any keyTimes.
333 : */
334 : double ScaleSimpleProgress(double aProgress, nsSMILCalcMode aCalcMode);
335 : /**
336 : * Adjust the progress within an interval, that is, between two animation
337 : * values, by applying any keySplines.
338 : */
339 : double ScaleIntervalProgress(double aProgress, PRUint32 aIntervalIndex);
340 :
341 : // Convenience attribute getters -- use these instead of querying
342 : // mAnimationElement as these may need to be overridden by subclasses
343 : virtual bool HasAttr(nsIAtom* aAttName) const;
344 : virtual const nsAttrValue* GetAttr(nsIAtom* aAttName) const;
345 : virtual bool GetAttr(nsIAtom* aAttName,
346 : nsAString& aResult) const;
347 :
348 : bool ParseAttr(nsIAtom* aAttName, const nsISMILAttr& aSMILAttr,
349 : nsSMILValue& aResult,
350 : bool& aPreventCachingOfSandwich) const;
351 :
352 : virtual nsresult GetValues(const nsISMILAttr& aSMILAttr,
353 : nsSMILValueArray& aResult);
354 :
355 : virtual void CheckValueListDependentAttrs(PRUint32 aNumValues);
356 : void CheckKeyTimes(PRUint32 aNumValues);
357 : void CheckKeySplines(PRUint32 aNumValues);
358 :
359 0 : virtual bool IsToAnimation() const {
360 0 : return !HasAttr(nsGkAtoms::values) &&
361 0 : HasAttr(nsGkAtoms::to) &&
362 0 : !HasAttr(nsGkAtoms::from);
363 : }
364 :
365 : // Returns true if we know our composited value won't change over the
366 : // simple duration of this animation (for a fixed base value).
367 : virtual bool IsValueFixedForSimpleDuration() const;
368 :
369 0 : inline bool IsAdditive() const {
370 : /*
371 : * Animation is additive if:
372 : *
373 : * (1) additive = "sum" (GetAdditive() == true), or
374 : * (2) it is 'by animation' (by is set, from and values are not)
375 : *
376 : * Although animation is not additive if it is 'to animation'
377 : */
378 0 : bool isByAnimation = (!HasAttr(nsGkAtoms::values) &&
379 0 : HasAttr(nsGkAtoms::by) &&
380 0 : !HasAttr(nsGkAtoms::from));
381 0 : return !IsToAnimation() && (GetAdditive() || isByAnimation);
382 : }
383 :
384 : // Setters for error flags
385 : // These correspond to bit-indices in mErrorFlags, for tracking parse errors
386 : // in these attributes, when those parse errors should block us from doing
387 : // animation.
388 : enum AnimationAttributeIdx {
389 : BF_ACCUMULATE = 0,
390 : BF_ADDITIVE = 1,
391 : BF_CALC_MODE = 2,
392 : BF_KEY_TIMES = 3,
393 : BF_KEY_SPLINES = 4,
394 : BF_KEY_POINTS = 5 // <animateMotion> only
395 : };
396 :
397 0 : inline void SetAccumulateErrorFlag(bool aNewValue) {
398 0 : SetErrorFlag(BF_ACCUMULATE, aNewValue);
399 0 : }
400 0 : inline void SetAdditiveErrorFlag(bool aNewValue) {
401 0 : SetErrorFlag(BF_ADDITIVE, aNewValue);
402 0 : }
403 0 : inline void SetCalcModeErrorFlag(bool aNewValue) {
404 0 : SetErrorFlag(BF_CALC_MODE, aNewValue);
405 0 : }
406 0 : inline void SetKeyTimesErrorFlag(bool aNewValue) {
407 0 : SetErrorFlag(BF_KEY_TIMES, aNewValue);
408 0 : }
409 0 : inline void SetKeySplinesErrorFlag(bool aNewValue) {
410 0 : SetErrorFlag(BF_KEY_SPLINES, aNewValue);
411 0 : }
412 0 : inline void SetKeyPointsErrorFlag(bool aNewValue) {
413 0 : SetErrorFlag(BF_KEY_POINTS, aNewValue);
414 0 : }
415 : // Helper method -- based on SET_BOOLBIT in nsHTMLInputElement.cpp
416 0 : inline void SetErrorFlag(AnimationAttributeIdx aField, bool aValue) {
417 0 : if (aValue) {
418 0 : mErrorFlags |= (0x01 << aField);
419 : } else {
420 0 : mErrorFlags &= ~(0x01 << aField);
421 : }
422 0 : }
423 :
424 : // Members
425 : // -------
426 :
427 : static nsAttrValue::EnumTable sAdditiveTable[];
428 : static nsAttrValue::EnumTable sCalcModeTable[];
429 : static nsAttrValue::EnumTable sAccumulateTable[];
430 :
431 : nsTArray<double> mKeyTimes;
432 : nsTArray<nsSMILKeySpline> mKeySplines;
433 :
434 : // These are the parameters provided by the previous sample. Currently we
435 : // perform lazy calculation. That is, we only calculate the result if and when
436 : // instructed by the compositor. This allows us to apply the result directly
437 : // to the animation value and allows the compositor to filter out functions
438 : // that it determines will not contribute to the final result.
439 : nsSMILTime mSampleTime; // sample time within simple dur
440 : nsSMILTimeValue mSimpleDuration;
441 : PRUint32 mRepeatIteration;
442 :
443 : nsSMILTime mBeginTime; // document time
444 :
445 : // The owning animation element. This is used for sorting based on document
446 : // position and for fetching attribute values stored in the element.
447 : // Raw pointer is OK here, because this nsSMILAnimationFunction can't outlive
448 : // its owning animation element.
449 : nsISMILAnimationElement* mAnimationElement;
450 :
451 : // Which attributes have been set but have had errors. This is not used for
452 : // all attributes but only those which have specified error behaviour
453 : // associated with them.
454 : PRUint16 mErrorFlags;
455 :
456 : // This is for the very specific case where we have a 'to' animation that is
457 : // frozen part way through the simple duration and there are other active
458 : // lower-priority animations targetting the same attribute. In this case
459 : // SMILANIM 3.3.6 says:
460 : //
461 : // The value for F(t) when a to-animation is frozen (at the end of the
462 : // simple duration) is just the to value. If a to-animation is frozen
463 : // anywhere within the simple duration (e.g., using a repeatCount of "2.5"),
464 : // the value for F(t) when the animation is frozen is the value computed for
465 : // the end of the active duration. Even if other, lower priority animations
466 : // are active while a to-animation is frozen, the value for F(t) does not
467 : // change.
468 : //
469 : // To implement this properly we'd need to force a resample of all the lower
470 : // priority animations at the active end of this animation--something which
471 : // would introduce unwanted coupling between the timing and animation model.
472 : // Instead we just save the value calculated when this animation is frozen (in
473 : // which case this animation will be sampled at the active end and the lower
474 : // priority animations should be sampled at a time pretty close to this,
475 : // provided we have a reasonable frame rate and we aren't seeking).
476 : //
477 : // @see
478 : // http://www.w3.org/TR/2001/REC-smil-animation-20010904/#FromToByAndAdditive
479 : nsSMILValue mFrozenValue;
480 :
481 : // Allows us to check whether an animation function has changed target from
482 : // sample to sample (because if neither target nor animated value have
483 : // changed, we don't have to do anything).
484 : nsSMILWeakTargetIdentifier mLastTarget;
485 :
486 : // Boolean flags
487 : bool mIsActive:1;
488 : bool mIsFrozen:1;
489 : bool mLastValue:1;
490 : bool mHasChanged:1;
491 : bool mValueNeedsReparsingEverySample:1;
492 : bool mPrevSampleWasSingleValueAnimation:1;
493 : bool mWasSkippedInPrevSample:1;
494 : };
495 :
496 : #endif // NS_SMILANIMATIONFUNCTION_H_
|