Skip to content

Commit

Permalink
Implement aspect-ratio interpolation support.
Browse files Browse the repository at this point in the history
Based on the discussion in w3c/csswg-drafts#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 <kevers@chromium.org>
Reviewed-by: Xida Chen <xidachen@chromium.org>
Commit-Queue: Robert Flack <flackr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846135}
  • Loading branch information
Robert Flack authored and Chromium LUCI CQ committed Jan 22, 2021
1 parent ece6c26 commit 4d02791
Show file tree
Hide file tree
Showing 13 changed files with 479 additions and 3 deletions.
4 changes: 4 additions & 0 deletions third_party/blink/renderer/core/animation/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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 <memory>
#include <utility>

#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<CSSAspectRatioNonInterpolableValue> 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<CSSAspectRatioNonInterpolableValue> {
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<InterpolableValue>
CSSAspectRatioInterpolationType::CreateInterpolableAspectRatio(
const StyleAspectRatio& aspect_ratio) {
std::unique_ptr<InterpolableAspectRatio> result =
InterpolableAspectRatio::MaybeCreate(aspect_ratio);
return std::move(result);
}

PairwiseInterpolationValue CSSAspectRatioInterpolationType::MaybeMergeSingles(
InterpolationValue&& start,
InterpolationValue&& end) const {
if (!To<CSSAspectRatioNonInterpolableValue>(*start.non_interpolable_value)
.IsCompatibleWith(To<CSSAspectRatioNonInterpolableValue>(
*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<InheritedAspectRatioChecker>(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<InterpolableAspectRatio>(interpolable_value);
state.Style()->SetAspectRatio(StyleAspectRatio(
To<CSSAspectRatioNonInterpolableValue>(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
Original file line number Diff line number Diff line change
@@ -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<InterpolableValue> 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_
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -179,6 +180,10 @@ const InterpolationTypes& CSSInterpolationTypesMap::Get(
applicable_types->push_back(
std::make_unique<CSSLengthInterpolationType>(used_property));
break;
case CSSPropertyID::kAspectRatio:
applicable_types->push_back(
std::make_unique<CSSAspectRatioInterpolationType>(used_property));
break;
case CSSPropertyID::kFlexGrow:
case CSSPropertyID::kFlexShrink:
case CSSPropertyID::kFillOpacity:
Expand Down
Original file line number Diff line number Diff line change
@@ -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> 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<InterpolableAspectRatio>(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<InterpolableNumber>(
log(aspect_ratio.Width() / aspect_ratio.Height()));
}

FloatSize InterpolableAspectRatio::GetRatio() const {
return FloatSize(exp(To<InterpolableNumber>(*value_).Value()), 1);
}

void InterpolableAspectRatio::Scale(double scale) {
value_->Scale(scale);
}

void InterpolableAspectRatio::Add(const InterpolableValue& other) {
value_->Add(*To<InterpolableAspectRatio>(other).value_);
}

void InterpolableAspectRatio::AssertCanInterpolateWith(
const InterpolableValue& other) const {
const InterpolableAspectRatio& other_aspect_ratio =
To<InterpolableAspectRatio>(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<InterpolableAspectRatio>(to);
InterpolableAspectRatio& aspect_ratio_result =
To<InterpolableAspectRatio>(result);
value_->Interpolate(*aspect_ratio_to.value_, progress,
*aspect_ratio_result.value_);
}

} // namespace blink
Original file line number Diff line number Diff line change
@@ -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 <memory>
#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<InterpolableValue> value)
: value_(std::move(value)) {}

static std::unique_ptr<InterpolableAspectRatio> 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<InterpolableValue> value_;
};

template <>
struct DowncastTraits<InterpolableAspectRatio> {
static bool AllowFrom(const InterpolableValue& interpolable_value) {
return interpolable_value.IsAspectRatio();
}
};

} // namespace blink

#endif // THIRD_PARTY_BLINK_RENDERER_CORE_ANIMATION_INTERPOLABLE_ASPECT_RATIO_H_
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
Loading

0 comments on commit 4d02791

Please sign in to comment.