michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsSMILCompositor.h" michael@0: #include "nsSMILCSSProperty.h" michael@0: #include "nsCSSProps.h" michael@0: #include "nsHashKeys.h" michael@0: michael@0: // PLDHashEntryHdr methods michael@0: bool michael@0: nsSMILCompositor::KeyEquals(KeyTypePointer aKey) const michael@0: { michael@0: return aKey && aKey->Equals(mKey); michael@0: } michael@0: michael@0: /*static*/ PLDHashNumber michael@0: nsSMILCompositor::HashKey(KeyTypePointer aKey) michael@0: { michael@0: // Combine the 3 values into one numeric value, which will be hashed. michael@0: // NOTE: We right-shift one of the pointers by 2 to get some randomness in michael@0: // its 2 lowest-order bits. (Those shifted-off bits will always be 0 since michael@0: // our pointers will be word-aligned.) michael@0: return (NS_PTR_TO_UINT32(aKey->mElement.get()) >> 2) + michael@0: NS_PTR_TO_UINT32(aKey->mAttributeName.get()) + michael@0: (aKey->mIsCSS ? 1 : 0); michael@0: } michael@0: michael@0: // Cycle-collection support michael@0: void michael@0: nsSMILCompositor::Traverse(nsCycleCollectionTraversalCallback* aCallback) michael@0: { michael@0: if (!mKey.mElement) michael@0: return; michael@0: michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, "Compositor mKey.mElement"); michael@0: aCallback->NoteXPCOMChild(mKey.mElement); michael@0: } michael@0: michael@0: // Other methods michael@0: void michael@0: nsSMILCompositor::AddAnimationFunction(nsSMILAnimationFunction* aFunc) michael@0: { michael@0: if (aFunc) { michael@0: mAnimationFunctions.AppendElement(aFunc); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSMILCompositor::ComposeAttribute() michael@0: { michael@0: if (!mKey.mElement) michael@0: return; michael@0: michael@0: // FIRST: Get the nsISMILAttr (to grab base value from, and to eventually michael@0: // give animated value to) michael@0: nsAutoPtr smilAttr(CreateSMILAttr()); michael@0: if (!smilAttr) { michael@0: // Target attribute not found (or, out of memory) michael@0: return; michael@0: } michael@0: if (mAnimationFunctions.IsEmpty()) { michael@0: // No active animation functions. (We can still have a nsSMILCompositor in michael@0: // that case if an animation function has *just* become inactive) michael@0: smilAttr->ClearAnimValue(); michael@0: return; michael@0: } michael@0: michael@0: // SECOND: Sort the animationFunctions, to prepare for compositing. michael@0: nsSMILAnimationFunction::Comparator comparator; michael@0: mAnimationFunctions.Sort(comparator); michael@0: michael@0: // THIRD: Step backwards through animation functions to find out michael@0: // which ones we actually care about. michael@0: uint32_t firstFuncToCompose = GetFirstFuncToAffectSandwich(); michael@0: michael@0: // FOURTH: Get & cache base value michael@0: nsSMILValue sandwichResultValue; michael@0: if (!mAnimationFunctions[firstFuncToCompose]->WillReplace()) { michael@0: sandwichResultValue = smilAttr->GetBaseValue(); michael@0: } michael@0: UpdateCachedBaseValue(sandwichResultValue); michael@0: michael@0: if (!mForceCompositing) { michael@0: return; michael@0: } michael@0: michael@0: // FIFTH: Compose animation functions michael@0: uint32_t length = mAnimationFunctions.Length(); michael@0: for (uint32_t i = firstFuncToCompose; i < length; ++i) { michael@0: mAnimationFunctions[i]->ComposeResult(*smilAttr, sandwichResultValue); michael@0: } michael@0: if (sandwichResultValue.IsNull()) { michael@0: smilAttr->ClearAnimValue(); michael@0: return; michael@0: } michael@0: michael@0: // SIXTH: Set the animated value to the final composited result. michael@0: nsresult rv = smilAttr->SetAnimValue(sandwichResultValue); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("nsISMILAttr::SetAnimValue failed"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsSMILCompositor::ClearAnimationEffects() michael@0: { michael@0: if (!mKey.mElement || !mKey.mAttributeName) michael@0: return; michael@0: michael@0: nsAutoPtr smilAttr(CreateSMILAttr()); michael@0: if (!smilAttr) { michael@0: // Target attribute not found (or, out of memory) michael@0: return; michael@0: } michael@0: smilAttr->ClearAnimValue(); michael@0: } michael@0: michael@0: // Protected Helper Functions michael@0: // -------------------------- michael@0: nsISMILAttr* michael@0: nsSMILCompositor::CreateSMILAttr() michael@0: { michael@0: if (mKey.mIsCSS) { michael@0: nsCSSProperty propId = michael@0: nsCSSProps::LookupProperty(nsDependentAtomString(mKey.mAttributeName), michael@0: nsCSSProps::eEnabledForAllContent); michael@0: if (nsSMILCSSProperty::IsPropertyAnimatable(propId)) { michael@0: return new nsSMILCSSProperty(propId, mKey.mElement.get()); michael@0: } michael@0: } else { michael@0: return mKey.mElement->GetAnimatedAttr(mKey.mAttributeNamespaceID, michael@0: mKey.mAttributeName); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t michael@0: nsSMILCompositor::GetFirstFuncToAffectSandwich() michael@0: { michael@0: uint32_t i; michael@0: for (i = mAnimationFunctions.Length(); i > 0; --i) { michael@0: nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1]; michael@0: // In the following, the lack of short-circuit behavior of |= means that we michael@0: // will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true) michael@0: // but only call HasChanged and WasSkippedInPrevSample if necessary. This michael@0: // is important since we need UpdateCachedTarget to run in order to detect michael@0: // changes to the target in subsequent samples. michael@0: mForceCompositing |= michael@0: curAnimFunc->UpdateCachedTarget(mKey) || michael@0: curAnimFunc->HasChanged() || michael@0: curAnimFunc->WasSkippedInPrevSample(); michael@0: michael@0: if (curAnimFunc->WillReplace()) { michael@0: --i; michael@0: break; michael@0: } michael@0: } michael@0: // Mark remaining animation functions as having been skipped so if we later michael@0: // use them we'll know to force compositing. michael@0: // Note that we only really need to do this if something has changed michael@0: // (otherwise we would have set the flag on a previous sample) and if michael@0: // something has changed mForceCompositing will be true. michael@0: if (mForceCompositing) { michael@0: for (uint32_t j = i; j > 0; --j) { michael@0: mAnimationFunctions[j-1]->SetWasSkipped(); michael@0: } michael@0: } michael@0: return i; michael@0: } michael@0: michael@0: void michael@0: nsSMILCompositor::UpdateCachedBaseValue(const nsSMILValue& aBaseValue) michael@0: { michael@0: if (!mCachedBaseValue) { michael@0: // We don't have last sample's base value cached. Assume it's changed. michael@0: mCachedBaseValue = new nsSMILValue(aBaseValue); michael@0: NS_WARN_IF_FALSE(mCachedBaseValue, "failed to cache base value (OOM?)"); michael@0: mForceCompositing = true; michael@0: } else if (*mCachedBaseValue != aBaseValue) { michael@0: // Base value has changed since last sample. michael@0: *mCachedBaseValue = aBaseValue; michael@0: mForceCompositing = true; michael@0: } michael@0: }