| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/animation/effect_stack.h" |
| |
| #include <algorithm> |
| #include "third_party/blink/renderer/core/animation/compositor_animations.h" |
| #include "third_party/blink/renderer/core/animation/css/css_animations.h" |
| #include "third_party/blink/renderer/core/animation/invalidatable_interpolation.h" |
| #include "third_party/blink/renderer/platform/runtime_enabled_features.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| void CopyToActiveInterpolationsMap( |
| const HeapVector<Member<Interpolation>>& source, |
| EffectStack::PropertyHandleFilter property_handle_filter, |
| ActiveInterpolationsMap& target) { |
| for (const auto& interpolation : source) { |
| PropertyHandle property = interpolation->GetProperty(); |
| if (property_handle_filter && !property_handle_filter(property)) |
| continue; |
| |
| ActiveInterpolationsMap::AddResult entry = |
| target.insert(property, MakeGarbageCollected<ActiveInterpolations>()); |
| ActiveInterpolations* active_interpolations = entry.stored_value->value; |
| |
| // Interpolations that depend on underlying values (e.g. have a non-replace |
| // composite mode) should be added onto the 'stack' of active |
| // interpolations. However any 'replace' effect erases everything that came |
| // before it, so we must clear the stack when that happens. |
| const bool effect_depends_on_underlying_value = |
| interpolation->IsInvalidatableInterpolation() && |
| To<InvalidatableInterpolation>(*interpolation.Get()) |
| .DependsOnUnderlyingValue(); |
| if (!effect_depends_on_underlying_value) { |
| active_interpolations->clear(); |
| } |
| active_interpolations->push_back(interpolation); |
| } |
| } |
| |
| |
| void CopyNewAnimationsToActiveInterpolationsMap( |
| const HeapVector<Member<const InertEffect>>& new_animations, |
| EffectStack::PropertyHandleFilter property_handle_filter, |
| ActiveInterpolationsMap& result) { |
| for (const auto& new_animation : new_animations) { |
| HeapVector<Member<Interpolation>> sample; |
| new_animation->Sample(sample); |
| if (!sample.empty()) |
| CopyToActiveInterpolationsMap(sample, property_handle_filter, result); |
| } |
| } |
| |
| } // namespace |
| |
| bool EffectStack::CompareSampledEffects( |
| const Member<SampledEffect>& sampled_effect1, |
| const Member<SampledEffect>& sampled_effect2) { |
| if (sampled_effect1->Effect() && sampled_effect2->Effect()) { |
| Animation* animation1 = sampled_effect1->Effect()->GetAnimation(); |
| Animation* animation2 = sampled_effect2->Effect()->GetAnimation(); |
| if (animation1 && animation2) { |
| return Animation::HasLowerCompositeOrdering( |
| animation1, animation2, |
| Animation::CompareAnimationsOrdering::kPointerOrder); |
| } |
| } |
| return sampled_effect1->SequenceNumber() < sampled_effect2->SequenceNumber(); |
| } |
| |
| EffectStack::EffectStack() = default; |
| |
| bool EffectStack::HasActiveAnimationsOnCompositor( |
| const PropertyHandle& property) const { |
| for (const auto& sampled_effect : sampled_effects_) { |
| if (sampled_effect->Effect() && |
| sampled_effect->Effect()->HasPlayingAnimation() && |
| sampled_effect->Effect()->HasActiveAnimationsOnCompositor(property)) |
| return true; |
| } |
| return false; |
| } |
| |
| bool EffectStack::AffectsProperties(PropertyHandleFilter filter) const { |
| for (const auto& sampled_effect : sampled_effects_) { |
| for (const auto& interpolation : sampled_effect->Interpolations()) { |
| if (filter(interpolation->GetProperty())) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool EffectStack::AffectsProperties(const CSSBitset& bitset, |
| KeyframeEffect::Priority priority) const { |
| for (const auto& sampled_effect : sampled_effects_) { |
| if (sampled_effect->GetPriority() != priority) |
| continue; |
| for (const auto& interpolation : sampled_effect->Interpolations()) { |
| const PropertyHandle& property = interpolation->GetProperty(); |
| if (property.IsCSSCustomProperty() || !property.IsCSSProperty()) |
| continue; |
| if (bitset.Has(property.GetCSSProperty().PropertyID())) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| HashSet<PropertyHandle> EffectStack::AffectedProperties( |
| KeyframeEffect::Priority priority) const { |
| HashSet<PropertyHandle> affected; |
| |
| for (const auto& sampled_effect : sampled_effects_) { |
| if (sampled_effect->GetPriority() != priority) |
| continue; |
| for (const auto& interpolation : sampled_effect->Interpolations()) |
| affected.insert(interpolation->GetProperty()); |
| } |
| |
| return affected; |
| } |
| |
| bool EffectStack::HasRevert() const { |
| for (const auto& sampled_effect : sampled_effects_) { |
| if (sampled_effect->Effect() && sampled_effect->Effect()->HasRevert()) |
| return true; |
| } |
| return false; |
| } |
| |
| ActiveInterpolationsMap EffectStack::ActiveInterpolations( |
| EffectStack* effect_stack, |
| const HeapVector<Member<const InertEffect>>* new_animations, |
| const HeapHashSet<Member<const Animation>>* suppressed_animations, |
| KeyframeEffect::Priority priority, |
| PropertyHandleFilter property_handle_filter, |
| KeyframeEffect* partial_effect_stack_cutoff) { |
| ActiveInterpolationsMap result; |
| |
| if (effect_stack) { |
| HeapVector<Member<SampledEffect>>& sampled_effects = |
| effect_stack->sampled_effects_; |
| effect_stack->RemoveRedundantSampledEffects(); |
| std::sort(sampled_effects.begin(), sampled_effects.end(), |
| CompareSampledEffects); |
| bool reached_cuttoff = false; |
| for (const auto& sampled_effect : sampled_effects) { |
| if (reached_cuttoff) |
| break; |
| if (partial_effect_stack_cutoff && |
| sampled_effect->Effect() == partial_effect_stack_cutoff) |
| reached_cuttoff = true; |
| |
| if (sampled_effect->GetPriority() != priority || |
| // TODO(majidvp): Instead of accessing the effect's animation move the |
| // check inside KeyframeEffect. http://crbug.com/812410 |
| (suppressed_animations && sampled_effect->Effect() && |
| suppressed_animations->Contains( |
| sampled_effect->Effect()->GetAnimation()))) |
| continue; |
| CopyToActiveInterpolationsMap(sampled_effect->Interpolations(), |
| property_handle_filter, result); |
| } |
| } |
| |
| if (new_animations) { |
| CopyNewAnimationsToActiveInterpolationsMap(*new_animations, |
| property_handle_filter, result); |
| } |
| return result; |
| } |
| |
| void EffectStack::RemoveRedundantSampledEffects() { |
| HashSet<PropertyHandle> replaced_properties; |
| for (wtf_size_t i = sampled_effects_.size(); i--;) { |
| SampledEffect& sampled_effect = *sampled_effects_[i]; |
| if (sampled_effect.WillNeverChange()) { |
| sampled_effect.RemoveReplacedInterpolations(replaced_properties); |
| sampled_effect.UpdateReplacedProperties(replaced_properties); |
| } |
| } |
| |
| wtf_size_t new_size = 0; |
| for (auto& sampled_effect : sampled_effects_) { |
| if (!sampled_effect->Interpolations().empty()) |
| sampled_effects_[new_size++].Swap(sampled_effect); |
| else if (sampled_effect->Effect()) |
| sampled_effect->Effect()->NotifySampledEffectRemovedFromEffectStack(); |
| } |
| sampled_effects_.Shrink(new_size); |
| } |
| |
| void EffectStack::Trace(Visitor* visitor) const { |
| visitor->Trace(sampled_effects_); |
| } |
| |
| } // namespace blink |