blob: a46ed6cbc32ff2a89057b815bd64cc073c235072 [file] [log] [blame]
// Copyright 2016 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 "third_party/blink/renderer/core/animation/css_transform_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/animation/length_units_checker.h"
#include "third_party/blink/renderer/core/css/css_function_value.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/css/resolver/transform_builder.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/transforms/transform_operations.h"
#include "third_party/blink/renderer/platform/transforms/translate_transform_operation.h"
namespace blink {
class CSSTransformNonInterpolableValue : public NonInterpolableValue {
public:
static scoped_refptr<CSSTransformNonInterpolableValue> Create(
TransformOperations&& transform) {
const bool is_single = true;
const bool is_additive = false;
return base::AdoptRef(new CSSTransformNonInterpolableValue(
is_single, std::move(transform), EmptyTransformOperations(),
is_additive, is_additive));
}
static scoped_refptr<CSSTransformNonInterpolableValue> CreateAdditive(
const CSSTransformNonInterpolableValue& other) {
DCHECK(other.is_single_);
const bool is_single = true;
const bool is_additive = true;
return base::AdoptRef(new CSSTransformNonInterpolableValue(
is_single, TransformOperations(other.start_),
TransformOperations(other.end_), is_additive, is_additive));
}
static scoped_refptr<CSSTransformNonInterpolableValue> Create(
const CSSTransformNonInterpolableValue& start,
double start_fraction,
const CSSTransformNonInterpolableValue& end,
double end_fraction) {
return base::AdoptRef(new CSSTransformNonInterpolableValue(
false, start.GetInterpolatedTransform(start_fraction),
end.GetInterpolatedTransform(end_fraction), start.IsAdditive(),
end.IsAdditive()));
}
scoped_refptr<CSSTransformNonInterpolableValue> Composite(
const CSSTransformNonInterpolableValue& other,
double other_progress) const {
DCHECK(is_single_);
DCHECK(!IsAdditive());
// This is the case where we have no B, so the equation is U + A.
if (other.is_single_) {
DCHECK_EQ(other_progress, 0);
DCHECK(other.IsAdditive());
TransformOperations result;
result.Operations() = Concat(Transform(), other.Transform());
return Create(std::move(result));
}
// Otherwise, we must compute (U + A)(1 - f) + (U + B)f - where U is only
// included if the keyframe is additive. This requires pre-pending the
// underlying ops to the necessary sides and then performing the
// interpolation.
DCHECK(other.is_start_additive_ || other.is_end_additive_);
TransformOperations start;
start.Operations() = other.is_start_additive_
? Concat(Transform(), other.start_)
: other.start_.Operations();
TransformOperations end;
end.Operations() = other.is_end_additive_ ? Concat(Transform(), other.end_)
: other.end_.Operations();
return Create(end.Blend(start, other_progress));
}
TransformOperations GetInterpolatedTransform(double progress) const {
if (progress == 0)
return start_;
if (progress == 1)
return end_;
DCHECK(!IsAdditive());
return end_.Blend(start_, progress);
}
bool IsSingle() const { return is_single_; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSTransformNonInterpolableValue(bool is_single,
TransformOperations&& start,
TransformOperations&& end,
bool is_start_additive,
bool is_end_additive)
: is_single_(is_single),
start_(std::move(start)),
end_(std::move(end)),
is_start_additive_(is_start_additive),
is_end_additive_(is_end_additive) {}
const TransformOperations& Transform() const {
DCHECK(is_single_);
return start_;
}
bool IsAdditive() const {
bool result = is_start_additive_ || is_end_additive_;
DCHECK(!result || is_single_);
return result;
}
Vector<scoped_refptr<TransformOperation>> Concat(
const TransformOperations& a,
const TransformOperations& b) const {
Vector<scoped_refptr<TransformOperation>> result;
result.ReserveCapacity(a.size() + b.size());
result.AppendVector(a.Operations());
result.AppendVector(b.Operations());
return result;
}
bool is_single_;
TransformOperations start_;
TransformOperations end_;
bool is_start_additive_;
bool is_end_additive_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSTransformNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSTransformNonInterpolableValue);
namespace {
InterpolationValue ConvertTransform(TransformOperations&& transform) {
return InterpolationValue(
std::make_unique<InterpolableNumber>(0),
CSSTransformNonInterpolableValue::Create(std::move(transform)));
}
InterpolationValue ConvertTransform(const TransformOperations& transform) {
return ConvertTransform(TransformOperations(transform));
}
class InheritedTransformChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
InheritedTransformChecker(const TransformOperations& inherited_transform)
: inherited_transform_(inherited_transform) {}
bool IsValid(const StyleResolverState& state,
const InterpolationValue& underlying) const final {
return inherited_transform_ == state.ParentStyle()->Transform();
}
private:
const TransformOperations inherited_transform_;
};
// Performs interpolation for the UnderlyingValueOwner, if necessary. This
// requires us to:
//
// i. Compute the interpolation for the CSSTransformNonInterpolableValue.
// ii. Reset the underlying_value_owner's interpolable_value (which is the
// progress) to 0. This is necessary to avoid double-interpolating in
// ApplyStandardPropertyValue.
void InterpolateUnderlyingValueOwnerIfNecessary(
UnderlyingValueOwner& underlying_value_owner) {
const CSSTransformNonInterpolableValue& underlying_non_interpolable_value =
ToCSSTransformNonInterpolableValue(
*underlying_value_owner.Value().non_interpolable_value);
// If the UnderlyingValueOwner is already single, it is either based on the
// underlying CSS style itself, or has already been interpolated.
if (underlying_non_interpolable_value.IsSingle())
return;
double underlying_progress =
ToInterpolableNumber(*underlying_value_owner.Value().interpolable_value)
.Value();
underlying_value_owner.SetInterpolableValue(
std::make_unique<InterpolableNumber>(0));
underlying_value_owner.SetNonInterpolableValue(
CSSTransformNonInterpolableValue::Create(
underlying_non_interpolable_value.GetInterpolatedTransform(
underlying_progress)));
}
} // namespace
InterpolationValue CSSTransformInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers&) const {
return ConvertTransform(EmptyTransformOperations());
}
InterpolationValue CSSTransformInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
return ConvertTransform(ComputedStyle::InitialStyle().Transform());
}
InterpolationValue CSSTransformInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
const TransformOperations& inherited_transform =
state.ParentStyle()->Transform();
conversion_checkers.push_back(
std::make_unique<InheritedTransformChecker>(inherited_transform));
return ConvertTransform(inherited_transform);
}
InterpolationValue CSSTransformInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState* state,
ConversionCheckers& conversion_checkers) const {
DCHECK(state);
if (auto* list_value = DynamicTo<CSSValueList>(value)) {
CSSPrimitiveValue::LengthTypeFlags types;
for (const CSSValue* item : *list_value) {
const auto& transform_function = To<CSSFunctionValue>(*item);
if (transform_function.FunctionType() == CSSValueID::kMatrix ||
transform_function.FunctionType() == CSSValueID::kMatrix3d) {
types.set(CSSPrimitiveValue::kUnitTypePixels);
continue;
}
for (const CSSValue* argument : transform_function) {
const auto& primitive_value = To<CSSPrimitiveValue>(*argument);
if (!primitive_value.IsLength())
continue;
primitive_value.AccumulateLengthUnitTypes(types);
}
}
std::unique_ptr<InterpolationType::ConversionChecker> length_units_checker =
LengthUnitsChecker::MaybeCreate(types, *state);
if (length_units_checker)
conversion_checkers.push_back(std::move(length_units_checker));
}
DCHECK(state);
TransformOperations transform = TransformBuilder::CreateTransformOperations(
value, state->CssToLengthConversionData());
return ConvertTransform(std::move(transform));
}
InterpolationValue CSSTransformInterpolationType::MakeAdditive(
InterpolationValue value) const {
value.non_interpolable_value =
CSSTransformNonInterpolableValue::CreateAdditive(
ToCSSTransformNonInterpolableValue(*value.non_interpolable_value));
return value;
}
PairwiseInterpolationValue CSSTransformInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
double start_fraction =
ToInterpolableNumber(*start.interpolable_value).Value();
double end_fraction = ToInterpolableNumber(*end.interpolable_value).Value();
return PairwiseInterpolationValue(
std::make_unique<InterpolableNumber>(0),
std::make_unique<InterpolableNumber>(1),
CSSTransformNonInterpolableValue::Create(
std::move(ToCSSTransformNonInterpolableValue(
*start.non_interpolable_value)),
start_fraction,
std::move(
ToCSSTransformNonInterpolableValue(*end.non_interpolable_value)),
end_fraction));
}
InterpolationValue
CSSTransformInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return ConvertTransform(style.Transform());
}
void CSSTransformInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
// If the first InvalidatableInterpolation in the stack doesn't depend on an
// underlying value, it becomes the underlying value, U. However at this point
// U has not yet been interpolated (as interpolation for
// CSSTransformInterpolationType only happens in either Composite or
// ApplyStandardPropertyValue), and so we have to do it here.
InterpolateUnderlyingValueOwnerIfNecessary(underlying_value_owner);
// Now that U has been resolved, do the actual compositing.
const CSSTransformNonInterpolableValue& underlying_non_interpolable_value =
ToCSSTransformNonInterpolableValue(
*underlying_value_owner.GetNonInterpolableValue());
const CSSTransformNonInterpolableValue& non_interpolable_value =
ToCSSTransformNonInterpolableValue(*value.non_interpolable_value);
double progress = ToInterpolableNumber(*value.interpolable_value).Value();
underlying_value_owner.SetNonInterpolableValue(
underlying_non_interpolable_value.Composite(non_interpolable_value,
progress));
}
void CSSTransformInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* untyped_non_interpolable_value,
StyleResolverState& state) const {
double progress = ToInterpolableNumber(interpolable_value).Value();
const CSSTransformNonInterpolableValue& non_interpolable_value =
ToCSSTransformNonInterpolableValue(*untyped_non_interpolable_value);
state.Style()->SetTransform(
non_interpolable_value.GetInterpolatedTransform(progress));
}
} // namespace blink