blob: c65204eb7f3b314a9c5d46e6656f71c8dc3213fc [file] [log] [blame]
// Copyright 2017 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_ray_interpolation_type.h"
#include <memory>
#include <utility>
#include "base/memory/ptr_util.h"
#include "third_party/blink/renderer/core/css/basic_shape_functions.h"
#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/core/style/style_ray.h"
namespace blink {
namespace {
class RayMode {
public:
RayMode(StyleRay::RaySize size, bool contain)
: size_(size), contain_(contain) {}
explicit RayMode(const StyleRay& style_ray)
: size_(style_ray.Size()), contain_(style_ray.Contain()) {}
StyleRay::RaySize Size() const { return size_; }
bool Contain() const { return contain_; }
bool operator==(const RayMode& other) const {
return size_ == other.size_ && contain_ == other.contain_;
}
bool operator!=(const RayMode& other) const { return !(*this == other); }
private:
StyleRay::RaySize size_;
bool contain_;
};
} // namespace
class CSSRayNonInterpolableValue : public NonInterpolableValue {
public:
static scoped_refptr<CSSRayNonInterpolableValue> Create(const RayMode& mode) {
return base::AdoptRef(new CSSRayNonInterpolableValue(mode));
}
const RayMode& Mode() const { return mode_; }
DECLARE_NON_INTERPOLABLE_VALUE_TYPE();
private:
CSSRayNonInterpolableValue(const RayMode& mode) : mode_(mode) {}
const RayMode mode_;
};
DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSRayNonInterpolableValue);
DEFINE_NON_INTERPOLABLE_VALUE_TYPE_CASTS(CSSRayNonInterpolableValue);
namespace {
// Returns the offset-path ray() value.
// If the offset-path is not a ray(), returns nullptr.
StyleRay* GetRay(const ComputedStyle& style) {
BasicShape* offset_path = style.OffsetPath();
if (!offset_path || offset_path->GetType() != BasicShape::kStyleRayType)
return nullptr;
return ToStyleRay(style.OffsetPath());
}
class UnderlyingRayModeChecker
: public CSSInterpolationType::CSSConversionChecker {
public:
static std::unique_ptr<UnderlyingRayModeChecker> Create(const RayMode& mode) {
return base::WrapUnique(new UnderlyingRayModeChecker(mode));
}
bool IsValid(const StyleResolverState&,
const InterpolationValue& underlying) const final {
return mode_ ==
ToCSSRayNonInterpolableValue(*underlying.non_interpolable_value)
.Mode();
}
private:
UnderlyingRayModeChecker(const RayMode& mode) : mode_(mode) {}
const RayMode mode_;
};
class InheritedRayChecker : public CSSInterpolationType::CSSConversionChecker {
public:
static std::unique_ptr<InheritedRayChecker> Create(
scoped_refptr<StyleRay> style_ray) {
return base::WrapUnique(new InheritedRayChecker(std::move(style_ray)));
}
private:
InheritedRayChecker(scoped_refptr<StyleRay> style_ray)
: style_ray_(std::move(style_ray)) {
DCHECK(style_ray_);
}
bool IsValid(const StyleResolverState& state,
const InterpolationValue&) const final {
return GetRay(*state.ParentStyle()) == style_ray_.get();
}
const scoped_refptr<StyleRay> style_ray_;
};
InterpolationValue CreateValue(float angle, const RayMode& mode) {
return InterpolationValue(InterpolableNumber::Create(angle),
CSSRayNonInterpolableValue::Create(mode));
}
} // namespace
void CSSRayInterpolationType::ApplyStandardPropertyValue(
const InterpolableValue& interpolable_value,
const NonInterpolableValue* non_interpolable_value,
StyleResolverState& state) const {
const CSSRayNonInterpolableValue& ray_non_interpolable_value =
ToCSSRayNonInterpolableValue(*non_interpolable_value);
state.Style()->SetOffsetPath(
StyleRay::Create(ToInterpolableNumber(interpolable_value).Value(),
ray_non_interpolable_value.Mode().Size(),
ray_non_interpolable_value.Mode().Contain()));
}
void CSSRayInterpolationType::Composite(
UnderlyingValueOwner& underlying_value_owner,
double underlying_fraction,
const InterpolationValue& value,
double interpolation_fraction) const {
const RayMode& underlying_mode =
ToCSSRayNonInterpolableValue(
*underlying_value_owner.Value().non_interpolable_value)
.Mode();
const RayMode& ray_mode =
ToCSSRayNonInterpolableValue(*value.non_interpolable_value).Mode();
if (underlying_mode == ray_mode) {
underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd(
underlying_fraction, *value.interpolable_value);
} else {
underlying_value_owner.Set(*this, value);
}
}
InterpolationValue CSSRayInterpolationType::MaybeConvertNeutral(
const InterpolationValue& underlying,
ConversionCheckers& conversion_checkers) const {
const RayMode& underlying_mode =
ToCSSRayNonInterpolableValue(*underlying.non_interpolable_value).Mode();
conversion_checkers.push_back(
UnderlyingRayModeChecker::Create(underlying_mode));
return CreateValue(0, underlying_mode);
}
InterpolationValue CSSRayInterpolationType::MaybeConvertInitial(
const StyleResolverState&,
ConversionCheckers&) const {
// 'none' is not a ray().
return nullptr;
}
InterpolationValue CSSRayInterpolationType::MaybeConvertInherit(
const StyleResolverState& state,
ConversionCheckers& conversion_checkers) const {
if (!state.ParentStyle())
return nullptr;
StyleRay* inherited_ray = GetRay(*state.ParentStyle());
if (!inherited_ray)
return nullptr;
conversion_checkers.push_back(InheritedRayChecker::Create(inherited_ray));
return CreateValue(inherited_ray->Angle(), RayMode(*inherited_ray));
}
PairwiseInterpolationValue CSSRayInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
const RayMode& start_mode =
ToCSSRayNonInterpolableValue(*start.non_interpolable_value).Mode();
const RayMode& end_mode =
ToCSSRayNonInterpolableValue(*end.non_interpolable_value).Mode();
if (start_mode != end_mode)
return nullptr;
return PairwiseInterpolationValue(std::move(start.interpolable_value),
std::move(end.interpolable_value),
std::move(start.non_interpolable_value));
}
InterpolationValue
CSSRayInterpolationType::MaybeConvertStandardPropertyUnderlyingValue(
const ComputedStyle& style) const {
StyleRay* underlying_ray = GetRay(style);
if (!underlying_ray)
return nullptr;
return CreateValue(underlying_ray->Angle(), RayMode(*underlying_ray));
}
InterpolationValue CSSRayInterpolationType::MaybeConvertValue(
const CSSValue& value,
const StyleResolverState* state,
ConversionCheckers&) const {
DCHECK(state);
if (!value.IsRayValue())
return nullptr;
scoped_refptr<BasicShape> shape = BasicShapeForValue(*state, value);
return CreateValue(ToStyleRay(*shape).Angle(), RayMode(ToStyleRay(*shape)));
}
} // namespace blink