From 4d02791fc5a96532045066d58ea958aed1d5b524 Mon Sep 17 00:00:00 2001 From: Robert Flack Date: Fri, 22 Jan 2021 16:02:54 +0000 Subject: [PATCH] Implement aspect-ratio interpolation support. Based on the discussion in https://github.com/w3c/csswg-drafts/issues/4953 aspect ratios should be interpolated by the log of their value preserving the same visual speed whether they are wide or narrow. This patch adds a new interpolation type for aspect ratios and uses it for interpolating the CSS aspect-ratio property. Bug: 1156160 Change-Id: I6cbff0abef54290de559a7625e24e3c4e1f1e0e9 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2586234 Reviewed-by: Kevin Ellis Reviewed-by: Xida Chen Commit-Queue: Robert Flack Cr-Commit-Position: refs/heads/master@{#846135} --- .../blink/renderer/core/animation/BUILD.gn | 4 + .../css_aspect_ratio_interpolation_type.cc | 164 ++++++++++++++++++ .../css_aspect_ratio_interpolation_type.h | 53 ++++++ .../animation/css_interpolation_types_map.cc | 5 + .../animation/interpolable_aspect_ratio.cc | 65 +++++++ .../animation/interpolable_aspect_ratio.h | 62 +++++++ .../core/animation/interpolable_value.h | 1 + .../renderer/core/css/css_properties.json5 | 2 +- .../css/resolver/style_builder_converter.cc | 2 +- .../css/resolver/style_builder_converter.h | 2 +- .../renderer/core/style/style_aspect_ratio.h | 2 + .../platform/runtime_enabled_features.json5 | 4 + .../animation/aspect-ratio-interpolation.html | 116 +++++++++++++ 13 files changed, 479 insertions(+), 3 deletions(-) create mode 100644 third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc create mode 100644 third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h create mode 100644 third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc create mode 100644 third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h create mode 100644 third_party/blink/web_tests/external/wpt/css/css-sizing/animation/aspect-ratio-interpolation.html diff --git a/third_party/blink/renderer/core/animation/BUILD.gn b/third_party/blink/renderer/core/animation/BUILD.gn index a690dc32877751..efc1cfc7c8148d 100644 --- a/third_party/blink/renderer/core/animation/BUILD.gn +++ b/third_party/blink/renderer/core/animation/BUILD.gn @@ -72,6 +72,8 @@ blink_core_sources("animation") { "css/css_transition_data.h", "css_angle_interpolation_type.cc", "css_angle_interpolation_type.h", + "css_aspect_ratio_interpolation_type.cc", + "css_aspect_ratio_interpolation_type.h", "css_basic_shape_interpolation_type.cc", "css_basic_shape_interpolation_type.h", "css_border_image_length_box_interpolation_type.cc", @@ -168,6 +170,8 @@ blink_core_sources("animation") { "image_slice_property_functions.h", "inert_effect.cc", "inert_effect.h", + "interpolable_aspect_ratio.cc", + "interpolable_aspect_ratio.h", "interpolable_filter.cc", "interpolable_filter.h", "interpolable_length.cc", diff --git a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc new file mode 100644 index 00000000000000..d0552b1a8d760d --- /dev/null +++ b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.cc @@ -0,0 +1,164 @@ +// Copyright 2021 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_aspect_ratio_interpolation_type.h" + +#include +#include + +#include "base/memory/ptr_util.h" +#include "third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h" +#include "third_party/blink/renderer/core/css/resolver/style_builder_converter.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_aspect_ratio.h" + +namespace blink { + +class CSSAspectRatioNonInterpolableValue final : public NonInterpolableValue { + public: + ~CSSAspectRatioNonInterpolableValue() final = default; + + static scoped_refptr Create( + StyleAspectRatio aspect_ratio) { + return base::AdoptRef( + new CSSAspectRatioNonInterpolableValue(aspect_ratio.GetType())); + } + + EAspectRatioType GetAspectRatioType() const { return type_; } + + bool IsCompatibleWith(const CSSAspectRatioNonInterpolableValue& other) const { + if (GetAspectRatioType() == EAspectRatioType::kAuto || + GetAspectRatioType() != other.GetAspectRatioType()) + return false; + return true; + } + + DECLARE_NON_INTERPOLABLE_VALUE_TYPE(); + + private: + explicit CSSAspectRatioNonInterpolableValue(EAspectRatioType type) + : type_(type) {} + + EAspectRatioType type_; +}; + +DEFINE_NON_INTERPOLABLE_VALUE_TYPE(CSSAspectRatioNonInterpolableValue); +template <> +struct DowncastTraits { + static bool AllowFrom(const NonInterpolableValue* value) { + return value && AllowFrom(*value); + } + static bool AllowFrom(const NonInterpolableValue& value) { + return value.GetType() == CSSAspectRatioNonInterpolableValue::static_type_; + } +}; + +class InheritedAspectRatioChecker + : public CSSInterpolationType::CSSConversionChecker { + public: + explicit InheritedAspectRatioChecker(StyleAspectRatio aspect_ratio) + : aspect_ratio_(aspect_ratio) {} + + private: + bool IsValid(const StyleResolverState& state, + const InterpolationValue& underlying) const final { + return state.ParentStyle()->AspectRatio() == aspect_ratio_; + } + + const StyleAspectRatio aspect_ratio_; +}; + +std::unique_ptr +CSSAspectRatioInterpolationType::CreateInterpolableAspectRatio( + const StyleAspectRatio& aspect_ratio) { + std::unique_ptr result = + InterpolableAspectRatio::MaybeCreate(aspect_ratio); + return std::move(result); +} + +PairwiseInterpolationValue CSSAspectRatioInterpolationType::MaybeMergeSingles( + InterpolationValue&& start, + InterpolationValue&& end) const { + if (!To(*start.non_interpolable_value) + .IsCompatibleWith(To( + *end.non_interpolable_value))) { + return nullptr; + } + return PairwiseInterpolationValue(std::move(start.interpolable_value), + std::move(end.interpolable_value), + std::move(start.non_interpolable_value)); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertNeutral( + const InterpolationValue& underlying, + ConversionCheckers& conversion_checkers) const { + return InterpolationValue(underlying.interpolable_value->CloneAndZero(), + underlying.non_interpolable_value); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertInitial( + const StyleResolverState&, + ConversionCheckers& conversion_checkers) const { + StyleAspectRatio initial_ratio = ComputedStyle::InitialStyle().AspectRatio(); + return InterpolationValue( + CreateInterpolableAspectRatio(initial_ratio), + CSSAspectRatioNonInterpolableValue::Create(initial_ratio)); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertInherit( + const StyleResolverState& state, + ConversionCheckers& conversion_checkers) const { + if (!state.ParentStyle()) + return nullptr; + + StyleAspectRatio inherited_aspect_ratio = state.ParentStyle()->AspectRatio(); + conversion_checkers.push_back( + std::make_unique(inherited_aspect_ratio)); + if (inherited_aspect_ratio.IsAuto()) + return nullptr; + + return InterpolationValue( + CreateInterpolableAspectRatio(inherited_aspect_ratio), + CSSAspectRatioNonInterpolableValue::Create(inherited_aspect_ratio)); +} + +InterpolationValue +CSSAspectRatioInterpolationType::MaybeConvertStandardPropertyUnderlyingValue( + const ComputedStyle& style) const { + return InterpolationValue( + CreateInterpolableAspectRatio(style.AspectRatio()), + CSSAspectRatioNonInterpolableValue::Create(style.AspectRatio())); +} + +InterpolationValue CSSAspectRatioInterpolationType::MaybeConvertValue( + const CSSValue& value, + const StyleResolverState* state, + ConversionCheckers&) const { + StyleAspectRatio ratio = + StyleBuilderConverter::ConvertAspectRatio(*state, value); + return InterpolationValue(CreateInterpolableAspectRatio(ratio), + CSSAspectRatioNonInterpolableValue::Create(ratio)); +} + +void CSSAspectRatioInterpolationType::ApplyStandardPropertyValue( + const InterpolableValue& interpolable_value, + const NonInterpolableValue* non_interpolable_value, + StyleResolverState& state) const { + const auto& aspect_ratio = To(interpolable_value); + state.Style()->SetAspectRatio(StyleAspectRatio( + To(non_interpolable_value) + ->GetAspectRatioType(), + aspect_ratio.GetRatio())); +} +void CSSAspectRatioInterpolationType::Composite( + UnderlyingValueOwner& underlying_value_owner, + double underlying_fraction, + const InterpolationValue& value, + double interpolation_fraction) const { + underlying_value_owner.MutableValue().interpolable_value->ScaleAndAdd( + underlying_fraction, *value.interpolable_value); +} + +} // namespace blink diff --git a/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h new file mode 100644 index 00000000000000..93d854058f75f5 --- /dev/null +++ b/third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h @@ -0,0 +1,53 @@ +// Copyright 2021 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ + +#include "third_party/blink/renderer/core/animation/css_interpolation_type.h" +#include "third_party/blink/renderer/core/css_value_keywords.h" + +namespace blink { + +class StyleAspectRatio; + +class CSSAspectRatioInterpolationType : public CSSInterpolationType { + public: + explicit CSSAspectRatioInterpolationType(PropertyHandle property) + : CSSInterpolationType(property) { + DCHECK_EQ(CSSPropertyID::kAspectRatio, + property.GetCSSProperty().PropertyID()); + } + + InterpolationValue MaybeConvertStandardPropertyUnderlyingValue( + const ComputedStyle&) const final; + PairwiseInterpolationValue MaybeMergeSingles( + InterpolationValue&& start, + InterpolationValue&& end) const final; + void ApplyStandardPropertyValue(const InterpolableValue&, + const NonInterpolableValue*, + StyleResolverState&) const final; + void Composite(UnderlyingValueOwner&, + double underlying_fraction, + const InterpolationValue&, + double interpolation_fraction) const final; + + static std::unique_ptr CreateInterpolableAspectRatio( + const StyleAspectRatio&); + + private: + InterpolationValue MaybeConvertNeutral(const InterpolationValue& underlying, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertInitial(const StyleResolverState&, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertInherit(const StyleResolverState&, + ConversionCheckers&) const final; + InterpolationValue MaybeConvertValue(const CSSValue&, + const StyleResolverState*, + ConversionCheckers&) const final; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_CSS_ASPECT_RATIO_INTERPOLATION_TYPE_H_ diff --git a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc index 71ea57ec1e4065..550c4bdfa2a1a6 100644 --- a/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc +++ b/third_party/blink/renderer/core/animation/css_interpolation_types_map.cc @@ -9,6 +9,7 @@ #include "third_party/blink/public/mojom/feature_policy/feature_policy_feature.mojom-blink.h" #include "third_party/blink/renderer/core/animation/css_angle_interpolation_type.h" +#include "third_party/blink/renderer/core/animation/css_aspect_ratio_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_basic_shape_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_border_image_length_box_interpolation_type.h" #include "third_party/blink/renderer/core/animation/css_clip_interpolation_type.h" @@ -179,6 +180,10 @@ const InterpolationTypes& CSSInterpolationTypesMap::Get( applicable_types->push_back( std::make_unique(used_property)); break; + case CSSPropertyID::kAspectRatio: + applicable_types->push_back( + std::make_unique(used_property)); + break; case CSSPropertyID::kFlexGrow: case CSSPropertyID::kFlexShrink: case CSSPropertyID::kFillOpacity: diff --git a/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc b/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc new file mode 100644 index 00000000000000..cfcbc5408f0d7b --- /dev/null +++ b/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.cc @@ -0,0 +1,65 @@ +// Copyright 2021 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/interpolable_aspect_ratio.h" +#include "third_party/blink/renderer/core/animation/interpolable_value.h" +#include "third_party/blink/renderer/core/css/resolver/style_resolver_state.h" +#include "third_party/blink/renderer/core/style/style_aspect_ratio.h" +#include "third_party/blink/renderer/platform/runtime_enabled_features.h" + +namespace blink { + +// static +std::unique_ptr InterpolableAspectRatio::MaybeCreate( + const StyleAspectRatio& aspect_ratio) { + if (!RuntimeEnabledFeatures::CSSAspectRatioInterpolationEnabled()) + return nullptr; + + // Auto aspect ratio cannot be interpolated to / from. + if (aspect_ratio.IsAuto()) + return nullptr; + return std::make_unique(aspect_ratio.GetRatio()); +} + +InterpolableAspectRatio::InterpolableAspectRatio( + const FloatSize& aspect_ratio) { + // The StyleAspectRatio::IsAuto check in MaybeCreate should return true if we + // have a degenerate aspect ratio. + DCHECK(aspect_ratio.Height() > 0 && aspect_ratio.Width() > 0); + + value_ = std::make_unique( + log(aspect_ratio.Width() / aspect_ratio.Height())); +} + +FloatSize InterpolableAspectRatio::GetRatio() const { + return FloatSize(exp(To(*value_).Value()), 1); +} + +void InterpolableAspectRatio::Scale(double scale) { + value_->Scale(scale); +} + +void InterpolableAspectRatio::Add(const InterpolableValue& other) { + value_->Add(*To(other).value_); +} + +void InterpolableAspectRatio::AssertCanInterpolateWith( + const InterpolableValue& other) const { + const InterpolableAspectRatio& other_aspect_ratio = + To(other); + value_->AssertCanInterpolateWith(*other_aspect_ratio.value_); +} + +void InterpolableAspectRatio::Interpolate(const InterpolableValue& to, + const double progress, + InterpolableValue& result) const { + const InterpolableAspectRatio& aspect_ratio_to = + To(to); + InterpolableAspectRatio& aspect_ratio_result = + To(result); + value_->Interpolate(*aspect_ratio_to.value_, progress, + *aspect_ratio_result.value_); +} + +} // namespace blink diff --git a/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h b/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h new file mode 100644 index 00000000000000..36d29a1d8df0ec --- /dev/null +++ b/third_party/blink/renderer/core/animation/interpolable_aspect_ratio.h @@ -0,0 +1,62 @@ +// Copyright 2021 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. + +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ + +#include +#include "third_party/blink/renderer/core/animation/interpolable_value.h" +#include "third_party/blink/renderer/core/style/style_aspect_ratio.h" + +namespace blink { + +// Represents a blink::StyleAspectRatio, converted into its logarithm for +// interpolation. +class CORE_EXPORT InterpolableAspectRatio final : public InterpolableValue { + public: + explicit InterpolableAspectRatio(const FloatSize& ratio); + explicit InterpolableAspectRatio(std::unique_ptr value) + : value_(std::move(value)) {} + + static std::unique_ptr MaybeCreate( + const StyleAspectRatio&); + + FloatSize GetRatio() const; + + // InterpolableValue implementation: + void Interpolate(const InterpolableValue& to, + const double progress, + InterpolableValue& result) const final; + bool IsAspectRatio() const final { return true; } + bool Equals(const InterpolableValue& other) const final { + NOTREACHED(); + return false; + } + void Scale(double scale) final; + void Add(const InterpolableValue& other) final; + void AssertCanInterpolateWith(const InterpolableValue& other) const final; + + private: + InterpolableAspectRatio* RawClone() const final { + return new InterpolableAspectRatio(value_->Clone()); + } + InterpolableAspectRatio* RawCloneAndZero() const final { + return new InterpolableAspectRatio(value_->CloneAndZero()); + } + + // Interpolable aspect ratio value is stored and interpolated as the log of + // the real aspect ratio. + std::unique_ptr value_; +}; + +template <> +struct DowncastTraits { + static bool AllowFrom(const InterpolableValue& interpolable_value) { + return interpolable_value.IsAspectRatio(); + } +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_ diff --git a/third_party/blink/renderer/core/animation/interpolable_value.h b/third_party/blink/renderer/core/animation/interpolable_value.h index 0a846d2f464d72..21428f412281df 100644 --- a/third_party/blink/renderer/core/animation/interpolable_value.h +++ b/third_party/blink/renderer/core/animation/interpolable_value.h @@ -39,6 +39,7 @@ class CORE_EXPORT InterpolableValue { virtual bool IsBool() const { return false; } virtual bool IsList() const { return false; } virtual bool IsLength() const { return false; } + virtual bool IsAspectRatio() const { return false; } virtual bool IsShadow() const { return false; } virtual bool IsFilter() const { return false; } virtual bool IsTransformList() const { return false; } diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 index f57b9b0391cefd..67fb2c8b4772a9 100644 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5 @@ -1115,7 +1115,7 @@ { name: "aspect-ratio", property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"], - interpolable: false, + interpolable: true, field_group: "box", field_template: "external", keywords: ["auto"], diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc index 6e41b3bcb6b85b..dd5e6a7d7f1638 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.cc @@ -2036,7 +2036,7 @@ bool ListHasAuto(const CSSValueList& list) { } // namespace StyleAspectRatio StyleBuilderConverter::ConvertAspectRatio( - StyleResolverState& state, + const StyleResolverState& state, const CSSValue& value) { auto* identifier_value = DynamicTo(value); if (identifier_value && identifier_value->GetValueID() == CSSValueID::kAuto) diff --git a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h index 2315285144af4e..d99ecbbf13bc33 100644 --- a/third_party/blink/renderer/core/css/resolver/style_builder_converter.h +++ b/third_party/blink/renderer/core/css/resolver/style_builder_converter.h @@ -277,7 +277,7 @@ class StyleBuilderConverter { static LengthSize ConvertIntrinsicSize(StyleResolverState&, const CSSValue&); - static StyleAspectRatio ConvertAspectRatio(StyleResolverState&, + static StyleAspectRatio ConvertAspectRatio(const StyleResolverState&, const CSSValue&); static bool ConvertInternalAlignSelfBlock(StyleResolverState& state, diff --git a/third_party/blink/renderer/core/style/style_aspect_ratio.h b/third_party/blink/renderer/core/style/style_aspect_ratio.h index f451519ec51248..e2d42141182fa4 100644 --- a/third_party/blink/renderer/core/style/style_aspect_ratio.h +++ b/third_party/blink/renderer/core/style/style_aspect_ratio.h @@ -5,7 +5,9 @@ #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_STYLE_ASPECT_RATIO_H_ #define THIRD_PARTY_BLINK_RENDERER_CORE_STYLE_STYLE_ASPECT_RATIO_H_ +#include "third_party/blink/renderer/platform/geometry/float_size.h" #include "third_party/blink/renderer/platform/geometry/int_size.h" +#include "third_party/blink/renderer/platform/geometry/layout_unit.h" #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" namespace blink { diff --git a/third_party/blink/renderer/platform/runtime_enabled_features.json5 b/third_party/blink/renderer/platform/runtime_enabled_features.json5 index a0b32ee17c0091..2dea8811fe72e0 100644 --- a/third_party/blink/renderer/platform/runtime_enabled_features.json5 +++ b/third_party/blink/renderer/platform/runtime_enabled_features.json5 @@ -440,6 +440,10 @@ name: "CSS3Text", status: "experimental", }, + { + name: "CSSAspectRatioInterpolation", + status: "experimental", + }, { name: "CSSAspectRatioProperty", status: "stable", diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/aspect-ratio-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/aspect-ratio-interpolation.html new file mode 100644 index 00000000000000..3c9afe06fa6989 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/aspect-ratio-interpolation.html @@ -0,0 +1,116 @@ + + +aspect-ratio interpolation + + + + + + + + + + + + + + +