| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/animation/InvalidatableInterpolation.h" |
| |
| #include <memory> |
| #include "core/animation/CSSInterpolationEnvironment.h" |
| #include "core/animation/StringKeyframe.h" |
| #include "core/css/resolver/StyleResolverState.h" |
| |
| namespace blink { |
| |
| void InvalidatableInterpolation::Interpolate(int, double fraction) { |
| if (fraction == current_fraction_) |
| return; |
| |
| if (current_fraction_ == 0 || current_fraction_ == 1 || fraction == 0 || |
| fraction == 1) { |
| ClearConversionCache(); |
| } |
| |
| current_fraction_ = fraction; |
| if (is_conversion_cached_ && cached_pair_conversion_) |
| cached_pair_conversion_->InterpolateValue(fraction, cached_value_); |
| // We defer the interpolation to ensureValidConversion() if |
| // m_cachedPairConversion is null. |
| } |
| |
| std::unique_ptr<PairwisePrimitiveInterpolation> |
| InvalidatableInterpolation::MaybeConvertPairwise( |
| const InterpolationEnvironment& environment, |
| const UnderlyingValueOwner& underlying_value_owner) const { |
| DCHECK(current_fraction_ != 0 && current_fraction_ != 1); |
| for (const auto& interpolation_type : *interpolation_types_) { |
| if ((start_keyframe_->IsNeutral() || end_keyframe_->IsNeutral()) && |
| (!underlying_value_owner || |
| underlying_value_owner.GetType() != *interpolation_type)) |
| continue; |
| ConversionCheckers conversion_checkers; |
| PairwiseInterpolationValue result = |
| interpolation_type->MaybeConvertPairwise( |
| *start_keyframe_, *end_keyframe_, environment, |
| underlying_value_owner.Value(), conversion_checkers); |
| AddConversionCheckers(*interpolation_type, conversion_checkers); |
| if (result) { |
| return PairwisePrimitiveInterpolation::Create( |
| *interpolation_type, std::move(result.start_interpolable_value), |
| std::move(result.end_interpolable_value), |
| std::move(result.non_interpolable_value)); |
| } |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<TypedInterpolationValue> |
| InvalidatableInterpolation::ConvertSingleKeyframe( |
| const PropertySpecificKeyframe& keyframe, |
| const InterpolationEnvironment& environment, |
| const UnderlyingValueOwner& underlying_value_owner) const { |
| if (keyframe.IsNeutral() && !underlying_value_owner) |
| return nullptr; |
| for (const auto& interpolation_type : *interpolation_types_) { |
| if (keyframe.IsNeutral() && |
| underlying_value_owner.GetType() != *interpolation_type) |
| continue; |
| ConversionCheckers conversion_checkers; |
| InterpolationValue result = interpolation_type->MaybeConvertSingle( |
| keyframe, environment, underlying_value_owner.Value(), |
| conversion_checkers); |
| AddConversionCheckers(*interpolation_type, conversion_checkers); |
| if (result) { |
| return TypedInterpolationValue::Create( |
| *interpolation_type, std::move(result.interpolable_value), |
| std::move(result.non_interpolable_value)); |
| } |
| } |
| DCHECK(keyframe.IsNeutral()); |
| return nullptr; |
| } |
| |
| void InvalidatableInterpolation::AddConversionCheckers( |
| const InterpolationType& type, |
| ConversionCheckers& conversion_checkers) const { |
| for (size_t i = 0; i < conversion_checkers.size(); i++) { |
| conversion_checkers[i]->SetType(type); |
| conversion_checkers_.push_back(std::move(conversion_checkers[i])); |
| } |
| } |
| |
| std::unique_ptr<TypedInterpolationValue> |
| InvalidatableInterpolation::MaybeConvertUnderlyingValue( |
| const InterpolationEnvironment& environment) const { |
| for (const auto& interpolation_type : *interpolation_types_) { |
| InterpolationValue result = |
| interpolation_type->MaybeConvertUnderlyingValue(environment); |
| if (result) { |
| return TypedInterpolationValue::Create( |
| *interpolation_type, std::move(result.interpolable_value), |
| std::move(result.non_interpolable_value)); |
| } |
| } |
| return nullptr; |
| } |
| |
| bool InvalidatableInterpolation::DependsOnUnderlyingValue() const { |
| return (start_keyframe_->UnderlyingFraction() != 0 && |
| current_fraction_ != 1) || |
| (end_keyframe_->UnderlyingFraction() != 0 && current_fraction_ != 0); |
| } |
| |
| bool InvalidatableInterpolation::IsNeutralKeyframeActive() const { |
| return (start_keyframe_->IsNeutral() && current_fraction_ != 1) || |
| (end_keyframe_->IsNeutral() && current_fraction_ != 0); |
| } |
| |
| void InvalidatableInterpolation::ClearConversionCache() const { |
| is_conversion_cached_ = false; |
| cached_pair_conversion_.reset(); |
| conversion_checkers_.clear(); |
| cached_value_.reset(); |
| } |
| |
| bool InvalidatableInterpolation::IsConversionCacheValid( |
| const InterpolationEnvironment& environment, |
| const UnderlyingValueOwner& underlying_value_owner) const { |
| if (!is_conversion_cached_) |
| return false; |
| if (IsNeutralKeyframeActive()) { |
| if (cached_pair_conversion_ && cached_pair_conversion_->IsFlip()) |
| return false; |
| // Pairwise interpolation can never happen between different |
| // InterpolationTypes, neutral values always represent the underlying value. |
| if (!underlying_value_owner || !cached_value_ || |
| cached_value_->GetType() != underlying_value_owner.GetType()) |
| return false; |
| } |
| for (const auto& checker : conversion_checkers_) { |
| if (!checker->IsValid(environment, underlying_value_owner.Value())) |
| return false; |
| } |
| return true; |
| } |
| |
| const TypedInterpolationValue* |
| InvalidatableInterpolation::EnsureValidConversion( |
| const InterpolationEnvironment& environment, |
| const UnderlyingValueOwner& underlying_value_owner) const { |
| DCHECK(!std::isnan(current_fraction_)); |
| DCHECK(interpolation_types_ && |
| interpolation_types_version_ == |
| environment.GetInterpolationTypesMap().Version()); |
| if (IsConversionCacheValid(environment, underlying_value_owner)) |
| return cached_value_.get(); |
| ClearConversionCache(); |
| if (current_fraction_ == 0) { |
| cached_value_ = ConvertSingleKeyframe(*start_keyframe_, environment, |
| underlying_value_owner); |
| } else if (current_fraction_ == 1) { |
| cached_value_ = ConvertSingleKeyframe(*end_keyframe_, environment, |
| underlying_value_owner); |
| } else { |
| std::unique_ptr<PairwisePrimitiveInterpolation> pairwise_conversion = |
| MaybeConvertPairwise(environment, underlying_value_owner); |
| if (pairwise_conversion) { |
| cached_value_ = pairwise_conversion->InitialValue(); |
| cached_pair_conversion_ = std::move(pairwise_conversion); |
| } else { |
| cached_pair_conversion_ = FlipPrimitiveInterpolation::Create( |
| ConvertSingleKeyframe(*start_keyframe_, environment, |
| underlying_value_owner), |
| ConvertSingleKeyframe(*end_keyframe_, environment, |
| underlying_value_owner)); |
| } |
| cached_pair_conversion_->InterpolateValue(current_fraction_, cached_value_); |
| } |
| is_conversion_cached_ = true; |
| return cached_value_.get(); |
| } |
| |
| void InvalidatableInterpolation::EnsureValidInterpolationTypes( |
| const InterpolationEnvironment& environment) const { |
| const InterpolationTypesMap& map = environment.GetInterpolationTypesMap(); |
| size_t latest_version = map.Version(); |
| if (interpolation_types_ && interpolation_types_version_ == latest_version) { |
| return; |
| } |
| const InterpolationTypes* latest_interpolation_types = &map.Get(property_); |
| DCHECK(latest_interpolation_types); |
| if (interpolation_types_ != latest_interpolation_types) { |
| ClearConversionCache(); |
| } |
| interpolation_types_ = latest_interpolation_types; |
| interpolation_types_version_ = latest_version; |
| } |
| |
| void InvalidatableInterpolation::SetFlagIfInheritUsed( |
| InterpolationEnvironment& environment) const { |
| if (!property_.IsCSSProperty() && !property_.IsPresentationAttribute()) |
| return; |
| StyleResolverState& state = |
| ToCSSInterpolationEnvironment(environment).GetState(); |
| if (!state.ParentStyle()) |
| return; |
| const CSSValue* start_value = |
| ToCSSPropertySpecificKeyframe(*start_keyframe_).Value(); |
| const CSSValue* end_value = |
| ToCSSPropertySpecificKeyframe(*end_keyframe_).Value(); |
| if ((start_value && start_value->IsInheritedValue()) || |
| (end_value && end_value->IsInheritedValue())) { |
| state.ParentStyle()->SetHasExplicitlyInheritedProperties(); |
| } |
| } |
| |
| double InvalidatableInterpolation::UnderlyingFraction() const { |
| if (current_fraction_ == 0) |
| return start_keyframe_->UnderlyingFraction(); |
| if (current_fraction_ == 1) |
| return end_keyframe_->UnderlyingFraction(); |
| return cached_pair_conversion_->InterpolateUnderlyingFraction( |
| start_keyframe_->UnderlyingFraction(), |
| end_keyframe_->UnderlyingFraction(), current_fraction_); |
| } |
| |
| void InvalidatableInterpolation::ApplyStack( |
| const ActiveInterpolations& interpolations, |
| InterpolationEnvironment& environment) { |
| DCHECK(!interpolations.IsEmpty()); |
| size_t starting_index = 0; |
| |
| // Compute the underlying value to composite onto. |
| UnderlyingValueOwner underlying_value_owner; |
| const InvalidatableInterpolation& first_interpolation = |
| ToInvalidatableInterpolation(*interpolations.at(starting_index)); |
| first_interpolation.EnsureValidInterpolationTypes(environment); |
| if (first_interpolation.DependsOnUnderlyingValue()) { |
| underlying_value_owner.Set( |
| first_interpolation.MaybeConvertUnderlyingValue(environment)); |
| } else { |
| const TypedInterpolationValue* first_value = |
| first_interpolation.EnsureValidConversion(environment, |
| underlying_value_owner); |
| // Fast path for replace interpolations that are the only one to apply. |
| if (interpolations.size() == 1) { |
| if (first_value) { |
| first_interpolation.SetFlagIfInheritUsed(environment); |
| first_value->GetType().Apply(first_value->GetInterpolableValue(), |
| first_value->GetNonInterpolableValue(), |
| environment); |
| } |
| return; |
| } |
| underlying_value_owner.Set(first_value); |
| starting_index++; |
| } |
| |
| // Composite interpolations onto the underlying value. |
| bool should_apply = false; |
| for (size_t i = starting_index; i < interpolations.size(); i++) { |
| const InvalidatableInterpolation& current_interpolation = |
| ToInvalidatableInterpolation(*interpolations.at(i)); |
| DCHECK(current_interpolation.DependsOnUnderlyingValue()); |
| current_interpolation.EnsureValidInterpolationTypes(environment); |
| const TypedInterpolationValue* current_value = |
| current_interpolation.EnsureValidConversion(environment, |
| underlying_value_owner); |
| if (!current_value) |
| continue; |
| should_apply = true; |
| current_interpolation.SetFlagIfInheritUsed(environment); |
| double underlying_fraction = current_interpolation.UnderlyingFraction(); |
| if (underlying_fraction == 0 || !underlying_value_owner || |
| underlying_value_owner.GetType() != current_value->GetType()) |
| underlying_value_owner.Set(current_value); |
| else |
| current_value->GetType().Composite( |
| underlying_value_owner, underlying_fraction, current_value->Value(), |
| current_interpolation.current_fraction_); |
| } |
| |
| if (should_apply && underlying_value_owner) |
| underlying_value_owner.GetType().Apply( |
| *underlying_value_owner.Value().interpolable_value, |
| underlying_value_owner.Value().non_interpolable_value.Get(), |
| environment); |
| } |
| |
| } // namespace blink |