|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsSMILCompositor.h" |
|
7 #include "nsSMILCSSProperty.h" |
|
8 #include "nsCSSProps.h" |
|
9 #include "nsHashKeys.h" |
|
10 |
|
11 // PLDHashEntryHdr methods |
|
12 bool |
|
13 nsSMILCompositor::KeyEquals(KeyTypePointer aKey) const |
|
14 { |
|
15 return aKey && aKey->Equals(mKey); |
|
16 } |
|
17 |
|
18 /*static*/ PLDHashNumber |
|
19 nsSMILCompositor::HashKey(KeyTypePointer aKey) |
|
20 { |
|
21 // Combine the 3 values into one numeric value, which will be hashed. |
|
22 // NOTE: We right-shift one of the pointers by 2 to get some randomness in |
|
23 // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since |
|
24 // our pointers will be word-aligned.) |
|
25 return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) + |
|
26 NS_PTR_TO_UINT32(aKey->mAttributeName.get()) + |
|
27 (aKey->mIsCSS ? 1 : 0); |
|
28 } |
|
29 |
|
30 // Cycle-collection support |
|
31 void |
|
32 nsSMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback) |
|
33 { |
|
34 if (!mKey.mElement) |
|
35 return; |
|
36 |
|
37 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement"); |
|
38 aCallback->NoteXPCOMChild(mKey.mElement); |
|
39 } |
|
40 |
|
41 // Other methods |
|
42 void |
|
43 nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc) |
|
44 { |
|
45 if (aFunc) { |
|
46 mAnimationFunctions.AppendElement(aFunc); |
|
47 } |
|
48 } |
|
49 |
|
50 void |
|
51 nsSMILCompositor::ComposeAttribute() |
|
52 { |
|
53 if (!mKey.mElement) |
|
54 return; |
|
55 |
|
56 // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually |
|
57 // give animated value to) |
|
58 nsAutoPtr<nsISMILAttr> smilAttr(CreateSMILAttr()); |
|
59 if (!smilAttr) { |
|
60 // Target attribute not found (or, out of memory) |
|
61 return; |
|
62 } |
|
63 if (mAnimationFunctions.IsEmpty()) { |
|
64 // No active animation functions. (We can still have a nsSMILCompositor in |
|
65 // that case if an animation function has *just* become inactive) |
|
66 smilAttr->ClearAnimValue(); |
|
67 return; |
|
68 } |
|
69 |
|
70 // SECOND: Sort the animationFunctions, to prepare for compositing. |
|
71 nsSMILAnimationFunction::Comparator comparator; |
|
72 mAnimationFunctions.Sort(comparator); |
|
73 |
|
74 // THIRD: Step backwards through animation functions to find out |
|
75 // which ones we actually care about. |
|
76 uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich(); |
|
77 |
|
78 // FOURTH: Get & cache base value |
|
79 nsSMILValue sandwichResultValue; |
|
80 if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) { |
|
81 sandwichResultValue = smilAttr->GetBaseValue(); |
|
82 } |
|
83 UpdateCachedBaseValue(sandwichResultValue); |
|
84 |
|
85 if (!mForceCompositing) { |
|
86 return; |
|
87 } |
|
88 |
|
89 // FIFTH: Compose animation functions |
|
90 uint32_t length = mAnimationFunctions.Length(); |
|
91 for (uint32_t i = firstFuncToCompose; i < length; ++i) { |
|
92 mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue); |
|
93 } |
|
94 if (sandwichResultValue.IsNull()) { |
|
95 smilAttr->ClearAnimValue(); |
|
96 return; |
|
97 } |
|
98 |
|
99 // SIXTH: Set the animated value to the final composited result. |
|
100 nsresult rv = smilAttr->SetAnimValue(sandwichResultValue); |
|
101 if (NS_FAILED(rv)) { |
|
102 NS_WARNING("nsISMILAttr::SetAnimValue failed"); |
|
103 } |
|
104 } |
|
105 |
|
106 void |
|
107 nsSMILCompositor::ClearAnimationEffects() |
|
108 { |
|
109 if (!mKey.mElement || !mKey.mAttributeName) |
|
110 return; |
|
111 |
|
112 nsAutoPtr<nsISMILAttr> smilAttr(CreateSMILAttr()); |
|
113 if (!smilAttr) { |
|
114 // Target attribute not found (or, out of memory) |
|
115 return; |
|
116 } |
|
117 smilAttr->ClearAnimValue(); |
|
118 } |
|
119 |
|
120 // Protected Helper Functions |
|
121 // -------------------------- |
|
122 nsISMILAttr* |
|
123 nsSMILCompositor::CreateSMILAttr() |
|
124 { |
|
125 if (mKey.mIsCSS) { |
|
126 nsCSSProperty propId = |
|
127 nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName), |
|
128 nsCSSProps::eEnabledForAllContent); |
|
129 if (nsSMILCSSProperty::IsPropertyAnimatable(propId)) { |
|
130 return new nsSMILCSSProperty(propId, mKey.mElement.get()); |
|
131 } |
|
132 } else { |
|
133 return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID, |
|
134 mKey.mAttributeName); |
|
135 } |
|
136 return nullptr; |
|
137 } |
|
138 |
|
139 uint32_t |
|
140 nsSMILCompositor::GetFirstFuncToAffectSandwich() |
|
141 { |
|
142 uint32_t i; |
|
143 for (i = mAnimationFunctions.Length(); i > 0; --i) { |
|
144 nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1]; |
|
145 // In the following, the lack of short-circuit behavior of |= means that we |
|
146 // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true) |
|
147 // but only call HasChanged and WasSkippedInPrevSample if necessary. This |
|
148 // is important since we need UpdateCachedTarget to run in order to detect |
|
149 // changes to the target in subsequent samples. |
|
150 mForceCompositing |= |
|
151 curAnimFunc->UpdateCachedTarget(mKey) || |
|
152 curAnimFunc->HasChanged() || |
|
153 curAnimFunc->WasSkippedInPrevSample(); |
|
154 |
|
155 if (curAnimFunc->WillReplace()) { |
|
156 --i; |
|
157 break; |
|
158 } |
|
159 } |
|
160 // Mark remaining animation functions as having been skipped so if we later |
|
161 // use them we'll know to force compositing. |
|
162 // Note that we only really need to do this if something has changed |
|
163 // (otherwise we would have set the flag on a previous sample) and if |
|
164 // something has changed mForceCompositing will be true. |
|
165 if (mForceCompositing) { |
|
166 for (uint32_t j = i; j > 0; --j) { |
|
167 mAnimationFunctions[j-1]->SetWasSkipped(); |
|
168 } |
|
169 } |
|
170 return i; |
|
171 } |
|
172 |
|
173 void |
|
174 nsSMILCompositor::UpdateCachedBaseValue(const nsSMILValue& aBaseValue) |
|
175 { |
|
176 if (!mCachedBaseValue) { |
|
177 // We don't have last sample's base value cached. Assume it's changed. |
|
178 mCachedBaseValue = new nsSMILValue(aBaseValue); |
|
179 NS_WARN_IF_FALSE(mCachedBaseValue, "failed to cache base value (OOM?)"); |
|
180 mForceCompositing = true; |
|
181 } else if (*mCachedBaseValue != aBaseValue) { |
|
182 // Base value has changed since last sample. |
|
183 *mCachedBaseValue = aBaseValue; |
|
184 mForceCompositing = true; |
|
185 } |
|
186 } |