| // Copyright 2015 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/cssom/interpolate_property_value.h" |
| |
| #include <algorithm> |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "cobalt/base/enable_if.h" |
| #include "cobalt/base/polymorphic_downcast.h" |
| #include "cobalt/cssom/calc_value.h" |
| #include "cobalt/cssom/cobalt_ui_nav_focus_transform_function.h" |
| #include "cobalt/cssom/cobalt_ui_nav_spotlight_transform_function.h" |
| #include "cobalt/cssom/interpolated_transform_property_value.h" |
| #include "cobalt/cssom/keyword_value.h" |
| #include "cobalt/cssom/length_value.h" |
| #include "cobalt/cssom/linear_gradient_value.h" |
| #include "cobalt/cssom/local_src_value.h" |
| #include "cobalt/cssom/matrix_function.h" |
| #include "cobalt/cssom/media_feature_keyword_value.h" |
| #include "cobalt/cssom/number_value.h" |
| #include "cobalt/cssom/property_key_list_value.h" |
| #include "cobalt/cssom/property_value_visitor.h" |
| #include "cobalt/cssom/rgba_color_value.h" |
| #include "cobalt/cssom/rotate_function.h" |
| #include "cobalt/cssom/scale_function.h" |
| #include "cobalt/cssom/string_value.h" |
| #include "cobalt/cssom/time_list_value.h" |
| #include "cobalt/cssom/timing_function_list_value.h" |
| #include "cobalt/cssom/transform_function.h" |
| #include "cobalt/cssom/transform_function_list_value.h" |
| #include "cobalt/cssom/transform_function_visitor.h" |
| #include "cobalt/cssom/translate_function.h" |
| #include "cobalt/cssom/unicode_range_value.h" |
| #include "cobalt/cssom/url_src_value.h" |
| #include "cobalt/cssom/url_value.h" |
| #include "cobalt/math/matrix_interpolation.h" |
| #include "cobalt/math/transform_2d.h" |
| |
| namespace cobalt { |
| namespace cssom { |
| |
| // This InterpolateVisitor allows us to define how to interpolate between |
| // a CSS style value, for each different CSS style value. Input to the visitor |
| // conceptually is a start CSS style value and an end CSS style value that are |
| // of the same type. Technically though, we can only visit on one of them, so |
| // the end value is passed into the constructor and the start value is visited. |
| // https://www.w3.org/TR/css3-transitions/#animatable-types |
| class InterpolateVisitor : public PropertyValueVisitor { |
| public: |
| InterpolateVisitor(const scoped_refptr<PropertyValue>& end_value, |
| float progress) |
| : end_value_(end_value), progress_(progress) {} |
| |
| const scoped_refptr<PropertyValue>& interpolated_value() const { |
| return interpolated_value_; |
| } |
| |
| void VisitAbsoluteURL(AbsoluteURLValue* start_absolute_url_value) override; |
| void VisitCalc(CalcValue* start_calc_value) override; |
| void VisitFilterFunctionList( |
| FilterFunctionListValue* start_filter_function_list_value) override; |
| void VisitFontStyle(FontStyleValue* start_font_style_value) override; |
| void VisitFontWeight(FontWeightValue* start_font_weight_value) override; |
| void VisitInteger(IntegerValue* integer_value) override; |
| void VisitKeyword(KeywordValue* start_keyword_value) override; |
| void VisitLength(LengthValue* start_length_value) override; |
| void VisitLinearGradient( |
| LinearGradientValue* start_linear_gradient_value) override; |
| void VisitLocalSrc(LocalSrcValue* local_src_value) override; |
| void VisitMediaFeatureKeywordValue( |
| MediaFeatureKeywordValue* media_feature_keyword_value) override; |
| void VisitNumber(NumberValue* start_number_value) override; |
| void VisitPercentage(PercentageValue* start_percentage_value) override; |
| void VisitPropertyList(PropertyListValue* property_list_value) override; |
| void VisitPropertyKeyList( |
| PropertyKeyListValue* property_key_list_value) override; |
| void VisitRadialGradient(RadialGradientValue* radial_gradient_value) override; |
| void VisitRatio(RatioValue* start_ratio_value) override; |
| void VisitResolution(ResolutionValue* start_resolution_value) override; |
| void VisitRGBAColor(RGBAColorValue* start_color_value) override; |
| void VisitShadow(ShadowValue* shadow_value) override; |
| void VisitString(StringValue* start_string_value) override; |
| void VisitTransformPropertyValue( |
| TransformPropertyValue* start_transform_property_value) override; |
| void VisitTimeList(TimeListValue* start_time_list_value) override; |
| void VisitTimingFunctionList( |
| TimingFunctionListValue* start_timing_function_list_value) override; |
| void VisitUnicodeRange(UnicodeRangeValue* unicode_range_value) override; |
| void VisitURL(URLValue* url_value) override; |
| void VisitUrlSrc(UrlSrcValue* url_src_value) override; |
| |
| private: |
| scoped_refptr<PropertyValue> end_value_; |
| float progress_; |
| |
| scoped_refptr<PropertyValue> interpolated_value_; |
| }; |
| |
| namespace { |
| |
| // Round to nearest integer for integer types. |
| template <typename T> |
| typename base::enable_if<std::numeric_limits<T>::is_integer, T>::type Round( |
| float value) { |
| return static_cast<T>(value + 0.5); |
| } |
| |
| // Pass through the value in the case of non-integer types. |
| template <typename T> |
| typename base::enable_if<!std::numeric_limits<T>::is_integer, T>::type Round( |
| float value) { |
| return static_cast<T>(value); |
| } |
| |
| // Linearly interpolate from value a to value b, and then apply a round on the |
| // results before returning if we are interpolating integer types (as specified |
| // by https://www.w3.org/TR/css3-transitions/#animatable-types). |
| template <typename T> |
| T Lerp(const T& a, const T& b, float progress) { |
| return Round<T>(a * (1 - progress) + b * progress); |
| } |
| |
| } // namespace |
| |
| namespace { |
| // Defines how each different type of transform function should be animated. |
| class AnimateTransformFunction : public TransformFunctionVisitor { |
| public: |
| // Returns an animated version of the transform function given the start, end |
| // and progress. Note that end may be NULL if the destination transform is |
| // 'none'. In this case, we should use an appropriate identity transform |
| // to animate towards. |
| static std::unique_ptr<TransformFunction> Animate( |
| const TransformFunction* start, const TransformFunction* end, |
| float progress) { |
| AnimateTransformFunction visitor(end, progress); |
| const_cast<TransformFunction*>(start)->Accept(&visitor); |
| return std::move(visitor.animated_); |
| } |
| |
| private: |
| void VisitMatrix(const MatrixFunction* matrix_function) override; |
| void VisitRotate(const RotateFunction* rotate_function) override; |
| void VisitScale(const ScaleFunction* scale_function) override; |
| void VisitTranslate(const TranslateFunction* translate_function) override; |
| void VisitCobaltUiNavFocusTransform( |
| const CobaltUiNavFocusTransformFunction* focus_function) override; |
| void VisitCobaltUiNavSpotlightTransform( |
| const CobaltUiNavSpotlightTransformFunction* spotlight_function) override; |
| |
| AnimateTransformFunction(const TransformFunction* end, float progress) |
| : end_(end), progress_(progress) {} |
| |
| const TransformFunction* end_; |
| float progress_; |
| std::unique_ptr<TransformFunction> animated_; |
| }; |
| |
| void AnimateTransformFunction::VisitMatrix( |
| const MatrixFunction* matrix_function) { |
| const MatrixFunction* matrix_end = |
| base::polymorphic_downcast<const MatrixFunction*>(end_); |
| math::Matrix3F interpolated_matrix = math::InterpolateMatrices( |
| matrix_function->value(), |
| matrix_end ? matrix_end->value() : math::Matrix3F::Identity(), progress_); |
| |
| animated_.reset(new MatrixFunction(interpolated_matrix)); |
| } |
| |
| void AnimateTransformFunction::VisitRotate( |
| const RotateFunction* rotate_function) { |
| const RotateFunction* rotate_end = |
| base::polymorphic_downcast<const RotateFunction*>(end_); |
| |
| // The rotate function's identity is the value 0. |
| float end_angle = |
| rotate_end ? rotate_end->clockwise_angle_in_radians() : 0.0f; |
| |
| animated_.reset(new RotateFunction(Lerp( |
| rotate_function->clockwise_angle_in_radians(), end_angle, progress_))); |
| } |
| |
| void AnimateTransformFunction::VisitScale(const ScaleFunction* scale_function) { |
| float end_x_factor, end_y_factor; |
| if (end_ == NULL) { |
| // Use the scale identity function, which is the value 1. |
| end_x_factor = 1.0f; |
| end_y_factor = 1.0f; |
| } else { |
| const ScaleFunction* end_scale = |
| base::polymorphic_downcast<const ScaleFunction*>(end_); |
| end_x_factor = end_scale->x_factor(); |
| end_y_factor = end_scale->y_factor(); |
| } |
| |
| animated_.reset(new ScaleFunction( |
| Lerp(scale_function->x_factor(), end_x_factor, progress_), |
| Lerp(scale_function->y_factor(), end_y_factor, progress_))); |
| } |
| |
| namespace { |
| std::unique_ptr<TranslateFunction> InterpolateTranslateFunctions( |
| const TranslateFunction* a, const TranslateFunction* b, float progress) { |
| if (b) { |
| DCHECK_EQ(a->axis(), b->axis()); |
| } |
| |
| float end_length_offset = b ? b->length_component_in_pixels() : 0.0f; |
| float lerped_length_offset = |
| Lerp(a->length_component_in_pixels(), end_length_offset, progress); |
| |
| float end_percentage_offset = b ? b->percentage_component() : 0.0f; |
| float lerped_percentage_offset = |
| Lerp(a->percentage_component(), end_percentage_offset, progress); |
| |
| bool result_is_calc = (b && a->offset_type() != b->offset_type()) || |
| a->offset_type() == TranslateFunction::kCalc; |
| |
| if (result_is_calc) { |
| return base::WrapUnique(new TranslateFunction( |
| a->axis(), |
| new CalcValue(new LengthValue(lerped_length_offset, kPixelsUnit), |
| new PercentageValue(lerped_percentage_offset)))); |
| } else if (a->offset_type() == TranslateFunction::kLength) { |
| DCHECK_EQ(0.0f, lerped_percentage_offset); |
| return base::WrapUnique(new TranslateFunction( |
| a->axis(), new LengthValue(lerped_length_offset, kPixelsUnit))); |
| } else if (a->offset_type() == TranslateFunction::kPercentage) { |
| DCHECK_EQ(0.0f, lerped_length_offset); |
| return base::WrapUnique(new TranslateFunction( |
| a->axis(), new PercentageValue(lerped_percentage_offset))); |
| } else { |
| NOTREACHED(); |
| return std::unique_ptr<TranslateFunction>(); |
| } |
| } |
| } // namespace |
| |
| void AnimateTransformFunction::VisitTranslate( |
| const TranslateFunction* translate_function) { |
| const TranslateFunction* translate_end = |
| base::polymorphic_downcast<const TranslateFunction*>(end_); |
| |
| animated_ = InterpolateTranslateFunctions(translate_function, translate_end, |
| progress_); |
| } |
| |
| void AnimateTransformFunction::VisitCobaltUiNavFocusTransform( |
| const CobaltUiNavFocusTransformFunction* focus_function) { |
| const CobaltUiNavFocusTransformFunction* focus_end = |
| base::polymorphic_downcast<const CobaltUiNavFocusTransformFunction*>( |
| end_); |
| // Since the focus transform changes over time, it would be incorrect |
| // to grab a snapshot of the current value and interpolate that with the |
| // identity matrix when transitioning to transform none. Instead, the |
| // focus transform needs to know how close to the identity transform it |
| // needs to interpolate its value when evaluated. |
| float progress_to_identity_end = 1.0f; |
| // Maintain the translation scale values if interpolating to identity since |
| // the interpolation to identity is also phasing out the translation scales. |
| float x_translation_scale_end = focus_function->x_translation_scale(); |
| float y_translation_scale_end = focus_function->y_translation_scale(); |
| if (focus_end) { |
| progress_to_identity_end = focus_end->progress_to_identity(); |
| x_translation_scale_end = focus_end->x_translation_scale(); |
| y_translation_scale_end = focus_end->y_translation_scale(); |
| } |
| animated_.reset(new CobaltUiNavFocusTransformFunction( |
| Lerp(focus_function->x_translation_scale(), x_translation_scale_end, |
| progress_), |
| Lerp(focus_function->y_translation_scale(), y_translation_scale_end, |
| progress_), |
| Lerp(focus_function->progress_to_identity(), progress_to_identity_end, |
| progress_))); |
| } |
| |
| void AnimateTransformFunction::VisitCobaltUiNavSpotlightTransform( |
| const CobaltUiNavSpotlightTransformFunction* spotlight_function) { |
| const CobaltUiNavSpotlightTransformFunction* spotlight_end = |
| base::polymorphic_downcast<const CobaltUiNavSpotlightTransformFunction*>( |
| end_); |
| // Since the spotlight transform changes over time, it would be incorrect |
| // to grab a snapshot of the current value and interpolate that with the |
| // identity matrix when transitioning to transform none. Instead, the |
| // spotlight transform needs to know how close to the identity transform it |
| // needs to interpolate its value when evaluated. |
| float progress_to_identity_end = |
| spotlight_end ? spotlight_end->progress_to_identity() : 1.0f; |
| animated_.reset(new CobaltUiNavSpotlightTransformFunction( |
| Lerp(spotlight_function->progress_to_identity(), progress_to_identity_end, |
| progress_))); |
| } |
| |
| // Returns true if two given transform function lists have the same number of |
| // elements, and each element type matches the corresponding element type at |
| // the same index in the other list. |
| bool TransformListsHaveSameType(const TransformFunctionListValue::Builder& a, |
| const TransformFunctionListValue::Builder& b) { |
| if (a.size() != b.size()) { |
| return false; |
| } |
| |
| for (size_t i = 0; i < a.size(); ++i) { |
| if (a[i]->GetTypeId() != b[i]->GetTypeId()) { |
| return false; |
| } else if (a[i]->GetTypeId() == base::GetTypeId<TranslateFunction>() && |
| base::polymorphic_downcast<const TranslateFunction*>(a[i].get()) |
| ->axis() != |
| base::polymorphic_downcast<const TranslateFunction*>( |
| b[i].get()) |
| ->axis()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| scoped_refptr<PropertyValue> AnimateTransform(PropertyValue* start_value, |
| PropertyValue* end_value, |
| float progress) { |
| // The process for animating a transform list are described here: |
| // https://www.w3.org/TR/2012/WD-css3-transforms-20120228/#animation |
| |
| // If both start and end values are "none", then the animated value is |
| // "none" also and we are done. |
| if (start_value->Equals(*KeywordValue::GetNone()) && |
| end_value->Equals(*KeywordValue::GetNone())) { |
| return KeywordValue::GetNone(); |
| } |
| |
| // At this point, either start_value or end_value may be "none" (though not |
| // both). We shuffle things around here to ensure that start_value is always |
| // not "none" so that subsequent code is simplified. |
| if (start_value->Equals(*KeywordValue::GetNone())) { |
| std::swap(start_value, end_value); |
| progress = 1 - progress; |
| } |
| |
| TransformFunctionListValue* start_transform = |
| base::polymorphic_downcast<TransformFunctionListValue*>(start_value); |
| TransformFunctionListValue* end_transform = |
| end_value->Equals(*KeywordValue::GetNone()) |
| ? NULL |
| : base::polymorphic_downcast<TransformFunctionListValue*>(end_value); |
| |
| const TransformFunctionListValue::Builder* start_functions = |
| &start_transform->value(); |
| |
| const TransformFunctionListValue::Builder* end_functions = |
| end_transform ? &end_transform->value() : NULL; |
| |
| // We first check to see if there is a match between transform types in |
| // the start transform list and transform types in the end transform list. |
| // This is necessary to know how to proceed in animating this transform. |
| // Note that a value of "none" implies that we will use identity transforms |
| // for that transform list, so in that case, there is indeed a match. |
| bool matching_list_types = |
| end_functions == NULL || |
| TransformListsHaveSameType(*start_functions, *end_functions); |
| if (matching_list_types) { |
| TransformFunctionListValue::Builder animated_functions; |
| // The lists have the same number of values and each corresponding transform |
| // matches in type. In this case, we do a transition on each |
| // corresponding transform individually. |
| for (size_t i = 0; i < start_functions->size(); ++i) { |
| animated_functions.push_back(AnimateTransformFunction::Animate( |
| (*start_functions)[i].get(), |
| end_functions ? (*end_functions)[i].get() : NULL, progress)); |
| } |
| return new TransformFunctionListValue(std::move(animated_functions)); |
| } else { |
| // The transform lists do not match up type for type. Collapse each list |
| // into a matrix and animate the matrix using the algorithm described here: |
| // https://www.w3.org/TR/2012/WD-css3-transforms-20120228/#matrix-decomposition |
| DCHECK(end_transform); |
| return new InterpolatedTransformPropertyValue(start_transform, |
| end_transform, progress); |
| } |
| } |
| } // namespace |
| |
| void InterpolateVisitor::VisitAbsoluteURL( |
| AbsoluteURLValue* start_absolute_url_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitCalc(CalcValue* start_calc_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitFilterFunctionList( |
| FilterFunctionListValue* start_filter_function_list_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitFontStyle( |
| FontStyleValue* start_font_style_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitFontWeight( |
| FontWeightValue* start_font_weight_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitInteger(IntegerValue* integer_value) { |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitKeyword(KeywordValue* start_keyword_value) { |
| if (start_keyword_value->Equals(*end_value_)) { |
| interpolated_value_ = start_keyword_value; |
| } |
| |
| if (start_keyword_value->value() == KeywordValue::kNone) { |
| if (end_value_->GetTypeId() == |
| base::GetTypeId<TransformFunctionListValue>()) { |
| interpolated_value_ = |
| AnimateTransform(start_keyword_value, end_value_, progress_); |
| } |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void InterpolateVisitor::VisitLength(LengthValue* start_length_value) { |
| const LengthValue& end_length_value = |
| *base::polymorphic_downcast<LengthValue*>(end_value_.get()); |
| interpolated_value_ = scoped_refptr<PropertyValue>(new LengthValue( |
| Lerp(start_length_value->value(), end_length_value.value(), progress_), |
| cssom::kPixelsUnit)); |
| } |
| |
| void InterpolateVisitor::VisitLinearGradient( |
| LinearGradientValue* start_linear_gradient_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitLocalSrc(LocalSrcValue* local_src_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitMediaFeatureKeywordValue( |
| MediaFeatureKeywordValue* media_feature_keyword_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitNumber(NumberValue* start_number_value) { |
| DCHECK(start_number_value->GetTypeId() == end_value_->GetTypeId()); |
| const NumberValue& end_number_value = |
| *base::polymorphic_downcast<NumberValue*>(end_value_.get()); |
| |
| interpolated_value_ = scoped_refptr<PropertyValue>(new NumberValue( |
| Lerp(start_number_value->value(), end_number_value.value(), progress_))); |
| } |
| |
| void InterpolateVisitor::VisitPercentage( |
| PercentageValue* start_percentage_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitPropertyList( |
| PropertyListValue* property_list_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitPropertyKeyList( |
| PropertyKeyListValue* property_key_list_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitRadialGradient( |
| RadialGradientValue* radial_gradient_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitRatio(RatioValue* start_ratio_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitResolution( |
| ResolutionValue* start_resolution_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitRGBAColor(RGBAColorValue* start_color_value) { |
| DCHECK(start_color_value->GetTypeId() == end_value_->GetTypeId()); |
| const RGBAColorValue& end_color_value = |
| *base::polymorphic_downcast<RGBAColorValue*>(end_value_.get()); |
| |
| interpolated_value_ = scoped_refptr<PropertyValue>(new RGBAColorValue( |
| Lerp(start_color_value->r(), end_color_value.r(), progress_), |
| Lerp(start_color_value->g(), end_color_value.g(), progress_), |
| Lerp(start_color_value->b(), end_color_value.b(), progress_), |
| Lerp(start_color_value->a(), end_color_value.a(), progress_))); |
| } |
| |
| void InterpolateVisitor::VisitShadow(ShadowValue* shadow_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitString(StringValue* start_string_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitTimeList(TimeListValue* start_time_list_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitTransformPropertyValue( |
| TransformPropertyValue* start_transform_property_value) { |
| if (start_transform_property_value->GetTypeId() == |
| base::GetTypeId<TransformFunctionListValue>()) { |
| if (end_value_->GetTypeId() == |
| base::GetTypeId<InterpolatedTransformPropertyValue>()) { |
| // Use matrix interpolation since the individual transform functions are |
| // unknown. |
| interpolated_value_ = new InterpolatedTransformPropertyValue( |
| start_transform_property_value, |
| base::polymorphic_downcast<InterpolatedTransformPropertyValue*>( |
| end_value_.get()), |
| progress_); |
| } else { |
| // Try to interpolate each transform function in the transform function |
| // list. This can only be done if the function types match. |
| interpolated_value_ = AnimateTransform(start_transform_property_value, |
| end_value_, progress_); |
| } |
| } else if (end_value_->Equals(*KeywordValue::GetNone())) { |
| // Interpolate to identity matrix. |
| TransformFunctionListValue::Builder builder; |
| builder.emplace_back(new MatrixFunction(math::Matrix3F::Identity())); |
| interpolated_value_ = new InterpolatedTransformPropertyValue( |
| start_transform_property_value, |
| new TransformFunctionListValue(std::move(builder)), progress_); |
| } else { |
| interpolated_value_ = new InterpolatedTransformPropertyValue( |
| start_transform_property_value, |
| base::polymorphic_downcast<TransformPropertyValue*>(end_value_.get()), |
| progress_); |
| } |
| } |
| |
| void InterpolateVisitor::VisitTimingFunctionList( |
| TimingFunctionListValue* start_timing_function_list_value) { |
| NOTIMPLEMENTED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitUnicodeRange( |
| UnicodeRangeValue* unicode_range_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitURL(URLValue* url_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| void InterpolateVisitor::VisitUrlSrc(UrlSrcValue* url_src_value) { |
| NOTREACHED(); |
| interpolated_value_ = end_value_; |
| } |
| |
| scoped_refptr<PropertyValue> InterpolatePropertyValue( |
| float progress, const scoped_refptr<PropertyValue>& start_value, |
| const scoped_refptr<PropertyValue>& end_value) { |
| InterpolateVisitor visitor(end_value, progress); |
| start_value->Accept(&visitor); |
| |
| return visitor.interpolated_value(); |
| } |
| |
| } // namespace cssom |
| } // namespace cobalt |