summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/blink/renderer/core/animation/css_transform_interpolation_type.cc
blob: 25afc795a40fa917507d4deb9cd64acb4137e066 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// 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/interpolable_transform_list.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 {
namespace {
InterpolationValue ConvertTransform(TransformOperations&& transform) {
  return InterpolationValue(
      InterpolableTransformList::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_;
};

class AlwaysInvalidateChecker
    : public CSSInterpolationType::CSSConversionChecker {
 public:
  bool IsValid(const StyleResolverState& state,
               const InterpolationValue& underlying) const final {
    return false;
  }
};
}  // 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() &&
            !primitive_value.IsCalculatedPercentageWithLength()) {
          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));
  }

  return InterpolationValue(
      InterpolableTransformList::ConvertCSSValue(value, state));
}

InterpolationValue
CSSTransformInterpolationType::PreInterpolationCompositeIfNeeded(
    InterpolationValue value,
    const InterpolationValue& underlying,
    EffectModel::CompositeOperation composite,
    ConversionCheckers& conversion_checkers) const {
  // Due to the post-interpolation composite optimization, the interpolation
  // stack aggressively caches interpolated values. When we are doing
  // pre-interpolation compositing, this can cause us to bake-in the composited
  // result even when the underlying value is changing. This checker is a hack
  // to disable that caching in this case.
  // TODO(crbug.com/1009230): Remove this once our interpolation code isn't
  // caching composited values.
  conversion_checkers.push_back(std::make_unique<AlwaysInvalidateChecker>());

  InterpolableTransformList& transform_list =
      To<InterpolableTransformList>(*value.interpolable_value);
  const InterpolableTransformList& underlying_transform_list =
      To<InterpolableTransformList>(*underlying.interpolable_value);

  // Addition of transform lists uses concatenation, whilst accumulation
  // performs a similar matching to interpolation but then adds the components.
  // See https://drafts.csswg.org/css-transforms-2/#combining-transform-lists
  if (composite == EffectModel::CompositeOperation::kCompositeAdd) {
    transform_list.PreConcat(underlying_transform_list);
  } else {
    DCHECK_EQ(composite, EffectModel::CompositeOperation::kCompositeAccumulate);
    transform_list.AccumulateOnto(underlying_transform_list);
  }
  return value;
}

PairwiseInterpolationValue CSSTransformInterpolationType::MaybeMergeSingles(
    InterpolationValue&& start,
    InterpolationValue&& end) const {
  // We don't do any checking here; InterpolableTransformList::Interpolate will
  // handle discrete animation for us if needed.
  return PairwiseInterpolationValue(std::move(start.interpolable_value),
                                    std::move(end.interpolable_value));
}

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 {
  // We do our compositing behavior in |PreInterpolationCompositeIfNeeded|; see
  // the documentation on that method.
  underlying_value_owner.Set(*this, value);
}

void CSSTransformInterpolationType::ApplyStandardPropertyValue(
    const InterpolableValue& interpolable_value,
    const NonInterpolableValue* untyped_non_interpolable_value,
    StyleResolverState& state) const {
  state.Style()->SetTransform(
      To<InterpolableTransformList>(interpolable_value).operations());
}

}  // namespace blink