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 the Mozilla Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2008
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Daniel Holbert <dholbert@mozilla.com>
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsSMILCompositor.h"
39 : #include "nsSMILCSSProperty.h"
40 : #include "nsCSSProps.h"
41 : #include "nsHashKeys.h"
42 :
43 : // PLDHashEntryHdr methods
44 : bool
45 0 : nsSMILCompositor::KeyEquals(KeyTypePointer aKey) const
46 : {
47 0 : return aKey && aKey->Equals(mKey);
48 : }
49 :
50 : /*static*/ PLDHashNumber
51 0 : nsSMILCompositor::HashKey(KeyTypePointer aKey)
52 : {
53 : // Combine the 3 values into one numeric value, which will be hashed.
54 : // NOTE: We right-shift one of the pointers by 2 to get some randomness in
55 : // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since
56 : // our pointers will be word-aligned.)
57 0 : return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) +
58 0 : NS_PTR_TO_UINT32(aKey->mAttributeName.get()) +
59 0 : (aKey->mIsCSS ? 1 : 0);
60 : }
61 :
62 : // Cycle-collection support
63 : void
64 0 : nsSMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback)
65 : {
66 0 : if (!mKey.mElement)
67 0 : return;
68 :
69 0 : NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement");
70 0 : aCallback->NoteXPCOMChild(mKey.mElement);
71 : }
72 :
73 : // Other methods
74 : void
75 0 : nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc)
76 : {
77 0 : if (aFunc) {
78 0 : mAnimationFunctions.AppendElement(aFunc);
79 : }
80 0 : }
81 :
82 : void
83 0 : nsSMILCompositor::ComposeAttribute()
84 : {
85 0 : if (!mKey.mElement)
86 0 : return;
87 :
88 : // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually
89 : // give animated value to)
90 0 : nsAutoPtr<nsISMILAttr> smilAttr(CreateSMILAttr());
91 0 : if (!smilAttr) {
92 : // Target attribute not found (or, out of memory)
93 : return;
94 : }
95 0 : if (mAnimationFunctions.IsEmpty()) {
96 : // No active animation functions. (We can still have a nsSMILCompositor in
97 : // that case if an animation function has *just* become inactive)
98 0 : smilAttr->ClearAnimValue();
99 : return;
100 : }
101 :
102 : // SECOND: Sort the animationFunctions, to prepare for compositing.
103 : nsSMILAnimationFunction::Comparator comparator;
104 0 : mAnimationFunctions.Sort(comparator);
105 :
106 : // THIRD: Step backwards through animation functions to find out
107 : // which ones we actually care about.
108 0 : PRUint32 firstFuncToCompose = GetFirstFuncToAffectSandwich();
109 :
110 : // FOURTH: Get & cache base value
111 0 : nsSMILValue sandwichResultValue;
112 0 : if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) {
113 0 : sandwichResultValue = smilAttr->GetBaseValue();
114 : }
115 0 : UpdateCachedBaseValue(sandwichResultValue);
116 :
117 0 : if (!mForceCompositing) {
118 : return;
119 : }
120 :
121 : // FIFTH: Compose animation functions
122 0 : PRUint32 length = mAnimationFunctions.Length();
123 0 : for (PRUint32 i = firstFuncToCompose; i < length; ++i) {
124 0 : mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue);
125 : }
126 0 : if (sandwichResultValue.IsNull()) {
127 0 : smilAttr->ClearAnimValue();
128 : return;
129 : }
130 :
131 : // SIXTH: Set the animated value to the final composited result.
132 0 : nsresult rv = smilAttr->SetAnimValue(sandwichResultValue);
133 0 : if (NS_FAILED(rv)) {
134 0 : NS_WARNING("nsISMILAttr::SetAnimValue failed");
135 : }
136 : }
137 :
138 : void
139 0 : nsSMILCompositor::ClearAnimationEffects()
140 : {
141 0 : if (!mKey.mElement || !mKey.mAttributeName)
142 0 : return;
143 :
144 0 : nsAutoPtr<nsISMILAttr> smilAttr(CreateSMILAttr());
145 0 : if (!smilAttr) {
146 : // Target attribute not found (or, out of memory)
147 : return;
148 : }
149 0 : smilAttr->ClearAnimValue();
150 : }
151 :
152 : // Protected Helper Functions
153 : // --------------------------
154 : nsISMILAttr*
155 0 : nsSMILCompositor::CreateSMILAttr()
156 : {
157 0 : if (mKey.mIsCSS) {
158 : nsCSSProperty propId =
159 0 : nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName));
160 0 : if (nsSMILCSSProperty::IsPropertyAnimatable(propId)) {
161 0 : return new nsSMILCSSProperty(propId, mKey.mElement.get());
162 : }
163 : } else {
164 0 : return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID,
165 0 : mKey.mAttributeName);
166 : }
167 0 : return nsnull;
168 : }
169 :
170 : PRUint32
171 0 : nsSMILCompositor::GetFirstFuncToAffectSandwich()
172 : {
173 : PRUint32 i;
174 0 : for (i = mAnimationFunctions.Length(); i > 0; --i) {
175 0 : nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1];
176 : // In the following, the lack of short-circuit behavior of |= means that we
177 : // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
178 : // but only call HasChanged and WasSkippedInPrevSample if necessary. This
179 : // is important since we need UpdateCachedTarget to run in order to detect
180 : // changes to the target in subsequent samples.
181 : mForceCompositing |=
182 0 : curAnimFunc->UpdateCachedTarget(mKey) ||
183 0 : curAnimFunc->HasChanged() ||
184 0 : curAnimFunc->WasSkippedInPrevSample();
185 :
186 0 : if (curAnimFunc->WillReplace()) {
187 0 : --i;
188 0 : break;
189 : }
190 : }
191 : // Mark remaining animation functions as having been skipped so if we later
192 : // use them we'll know to force compositing.
193 : // Note that we only really need to do this if something has changed
194 : // (otherwise we would have set the flag on a previous sample) and if
195 : // something has changed mForceCompositing will be true.
196 0 : if (mForceCompositing) {
197 0 : for (PRUint32 j = i; j > 0; --j) {
198 0 : mAnimationFunctions[j-1]->SetWasSkipped();
199 : }
200 : }
201 0 : return i;
202 : }
203 :
204 : void
205 0 : nsSMILCompositor::UpdateCachedBaseValue(const nsSMILValue& aBaseValue)
206 : {
207 0 : if (!mCachedBaseValue) {
208 : // We don't have last sample's base value cached. Assume it's changed.
209 0 : mCachedBaseValue = new nsSMILValue(aBaseValue);
210 0 : NS_WARN_IF_FALSE(mCachedBaseValue, "failed to cache base value (OOM?)");
211 0 : mForceCompositing = true;
212 0 : } else if (*mCachedBaseValue != aBaseValue) {
213 : // Base value has changed since last sample.
214 0 : *mCachedBaseValue = aBaseValue;
215 0 : mForceCompositing = true;
216 : }
217 0 : }
|