blob: 3efeb2bc6b047bc23dba15c84272fef843c59cad [file] [log] [blame]
// 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 "third_party/blink/renderer/core/animation/css_scale_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.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/style/computed_style.h"
namespace blink {
namespace {
struct Scale {
Scale(double x, double y, double z) { Init(x, y, z, false); }
explicit Scale() { Init(1, 1, 1, true); }
explicit Scale(const ScaleTransformOperation* scale) {
if (scale)
Init(scale->X(), scale->Y(), scale->Z(), false);
else
Init(1, 1, 1, true);
}
explicit Scale(const InterpolableValue& value) {
const InterpolableList& list = ToInterpolableList(value);
if (list.length() == 0) {
Init(1, 1, 1, true);
return;
}
Init(ToInterpolableNumber(*list.Get(0)).Value(),
ToInterpolableNumber(*list.Get(1)).Value(),
ToInterpolableNumber(*list.Get(2)).Value(), false);
}
void Init(double x, double y, double z, bool is_value_none) {
array[0] = x;
array[1] = y;
array[2] = z;
is_none = is_value_none;
}
InterpolationValue CreateInterpolationValue() const;
bool operator==(const Scale& other) const {
for (size_t i = 0; i < 3; i++) {
if (array[i] != other.array[i])
return false;
}
return is_none == other.is_none;
}
double array[3];
bool is_none;
};
std::unique_ptr<InterpolableValue> CreateScaleIdentity() {
auto list = std::make_unique<InterpolableList>(3);
for (wtf_size_t i = 0; i < 3; i++)
list->Set(i, std::make_unique<InterpolableNumber>(1));
return std::move(list);
}
class InheritedScaleChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
explicit InheritedScaleChecker(const Scale& scale) : scale_(scale) {}
private:
bool IsValid(const StyleResolverState& state,
const InterpolationValue&) const final {
return scale_ == Scale(state.ParentStyle()->Scale());
}
const Scale scale_;
};
} // namespace
class CSSScaleNonInterpolableValue : public NonInterpolableValue {
public:
~CSSScaleNonInterpolableValue() final = default;
static scoped_refptr<CSSScaleNonInterpolableValue> Create(
const Scale& scale) {
return base::AdoptRef(
new CSSScaleNonInterpolableValue(scale, scale, false, false));
}
static scoped_refptr<CSSScaleNonInterpolableValue> CreateAdditive(
const CSSScaleNonInterpolableValue& other) {
const bool is_additive = true;
return base::AdoptRef(new CSSScaleNonInterpolableValue(
other.start_, other.end_, is_additive, is_additive));
}
static scoped_refptr<CSSScaleNonInterpolableValue> Merge(
const CSSScaleNonInterpolableValue& start,
const CSSScaleNonInterpolableValue& end) {
return base::AdoptRef(new CSSScaleNonInterpolableValue(
start.Start(), end.end(), start.IsStartAdditive(),
end.IsEndAdditive()));
}
const Scale& Start() const { return start_; }
const Scale& end() const { return end_; }
bool IsStartAdditive() const { return is_start_additive_; }
bool IsEndAdditive() const { return is_end_additive_; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSScaleNonInterpolableValue(const Scale& start,
const Scale& end,
bool is_start_additive,
bool is_end_additive)
: start_(start),
end_(end),
is_start_additive_(is_start_additive),
is_end_additive_(is_end_additive) {}
const Scale start_;
const Scale end_;
bool is_start_additive_;
bool is_end_additive_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSScaleNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSScaleNonInterpolableValue);
InterpolationValue Scale::CreateInterpolationValue() const {
if (is_none) {
return InterpolationValue(std::make_unique<InterpolableList>(0),
CSSScaleNonInterpolableValue::Create(*this));
}
auto list = std::make_unique<InterpolableList>(3);
for (wtf_size_t i = 0; i < 3; i++)
list->Set(i, std::make_unique<InterpolableNumber>(array[i]));
return InterpolationValue(std::move(list),
CSSScaleNonInterpolableValue::Create(*this));
}
InterpolationValue CSSScaleInterpolationType::MaybeConvertNeutral(
const InterpolationValue&,
ConversionCheckers&) const {
return Scale(1, 1, 1).CreateInterpolationValue();
}
InterpolationValue CSSScaleInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
return Scale().CreateInterpolationValue();
}
InterpolationValue CSSScaleInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
Scale inherited_scale(state.ParentStyle()->Scale());
conversion_checkers.push_back(
std::make_unique<InheritedScaleChecker>(inherited_scale));
return inherited_scale.CreateInterpolationValue();
}
InterpolationValue CSSScaleInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState*,
ConversionCheckers&) const {
if (!value.IsBaseValueList())
return Scale().CreateInterpolationValue();
const auto& list = To<CSSValueList>(value);
DCHECK(list.length() >= 1 && list.length() <= 3);
if (list.length() == 1) {
double scale = To<CSSPrimitiveValue>(list.Item(0)).GetDoubleValue();
// single value defines a 2d scale according to the spec
// see https://drafts.csswg.org/css-transforms-2/#propdef-scale
return Scale(scale, scale, 1).CreateInterpolationValue();
} else if (list.length() == 2) {
double x_scale = To<CSSPrimitiveValue>(list.Item(0)).GetDoubleValue();
double y_scale = To<CSSPrimitiveValue>(list.Item(1)).GetDoubleValue();
return Scale(x_scale, y_scale, 1).CreateInterpolationValue();
} else {
double x_scale = To<CSSPrimitiveValue>(list.Item(0)).GetDoubleValue();
double y_scale = To<CSSPrimitiveValue>(list.Item(1)).GetDoubleValue();
double z_scale = To<CSSPrimitiveValue>(list.Item(2)).GetDoubleValue();
return Scale(x_scale, y_scale, z_scale).CreateInterpolationValue();
}
}
InterpolationValue CSSScaleInterpolationType::PreInterpolationCompositeIfNeeded(
InterpolationValue value,
const InterpolationValue& underlying,
EffectModel::CompositeOperation,
ConversionCheckers&) const {
value.non_interpolable_value = CSSScaleNonInterpolableValue::CreateAdditive(
ToCSSScaleNonInterpolableValue(*value.non_interpolable_value));
return value;
}
PairwiseInterpolationValue CSSScaleInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
wtf_size_t start_list_length =
ToInterpolableList(*start.interpolable_value).length();
wtf_size_t end_list_length =
ToInterpolableList(*end.interpolable_value).length();
if (start_list_length < end_list_length)
start.interpolable_value = CreateScaleIdentity();
else if (end_list_length < start_list_length)
end.interpolable_value = CreateScaleIdentity();
return PairwiseInterpolationValue(
std::move(start.interpolable_value), std::move(end.interpolable_value),
CSSScaleNonInterpolableValue::Merge(
ToCSSScaleNonInterpolableValue(*start.non_interpolable_value),
ToCSSScaleNonInterpolableValue(*end.non_interpolable_value)));
}
InterpolationValue
CSSScaleInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
return Scale(style.Scale()).CreateInterpolationValue();
}
void CSSScaleInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
if (ToInterpolableList(
*underlying_value_owner.MutableValue().interpolable_value)
.length() == 0) {
underlying_value_owner.MutableValue().interpolable_value =
CreateScaleIdentity();
}
const CSSScaleNonInterpolableValue& metadata =
ToCSSScaleNonInterpolableValue(*value.non_interpolable_value);
DCHECK(metadata.IsStartAdditive() || metadata.IsEndAdditive());
InterpolableList& underlying_list = ToInterpolableList(
*underlying_value_owner.MutableValue().interpolable_value);
for (wtf_size_t i = 0; i < 3; i++) {
InterpolableNumber& underlying =
ToInterpolableNumber(*underlying_list.GetMutable(i));
double start = metadata.Start().array[i] *
(metadata.IsStartAdditive() ? underlying.Value() : 1);
double end = metadata.end().array[i] *
(metadata.IsEndAdditive() ? underlying.Value() : 1);
underlying.Set(Blend(start, end, interpolation_fraction));
}
}
void CSSScaleInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue*,
StyleResolverState& state) const {
Scale scale(interpolable_value);
if (scale.is_none) {
state.Style()->SetScale(nullptr);
return;
}
state.Style()->SetScale(ScaleTransformOperation::Create(
scale.array[0], scale.array[1], scale.array[2],
TransformOperation::kScale3D));
}
} // namespace blink