| // Copyright 2015 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/svg_path_seg_interpolation_functions.h" |
| |
| #include <memory> |
| |
| namespace blink { |
| |
| std::unique_ptr<InterpolableNumber> ConsumeControlAxis(double value, |
| bool is_absolute, |
| double current_value) { |
| return InterpolableNumber::Create(is_absolute ? value |
| : current_value + value); |
| } |
| |
| double ConsumeInterpolableControlAxis(const InterpolableValue* number, |
| bool is_absolute, |
| double current_value) { |
| double value = ToInterpolableNumber(number)->Value(); |
| return is_absolute ? value : value - current_value; |
| } |
| |
| std::unique_ptr<InterpolableNumber> |
| ConsumeCoordinateAxis(double value, bool is_absolute, double& current_value) { |
| if (is_absolute) |
| current_value = value; |
| else |
| current_value += value; |
| return InterpolableNumber::Create(current_value); |
| } |
| |
| double ConsumeInterpolableCoordinateAxis(const InterpolableValue* number, |
| bool is_absolute, |
| double& current_value) { |
| double previous_value = current_value; |
| current_value = ToInterpolableNumber(number)->Value(); |
| return is_absolute ? current_value : current_value - previous_value; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeClosePath( |
| const PathSegmentData&, |
| PathCoordinates& coordinates) { |
| coordinates.current_x = coordinates.initial_x; |
| coordinates.current_y = coordinates.initial_y; |
| return InterpolableList::Create(0); |
| } |
| |
| PathSegmentData ConsumeInterpolableClosePath(const InterpolableValue&, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| coordinates.current_x = coordinates.initial_x; |
| coordinates.current_y = coordinates.initial_y; |
| |
| PathSegmentData segment; |
| segment.command = seg_type; |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeSingleCoordinate( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| std::unique_ptr<InterpolableList> result = InterpolableList::Create(2); |
| result->Set(0, ConsumeCoordinateAxis(segment.X(), is_absolute, |
| coordinates.current_x)); |
| result->Set(1, ConsumeCoordinateAxis(segment.Y(), is_absolute, |
| coordinates.current_y)); |
| |
| if (ToAbsolutePathSegType(segment.command) == kPathSegMoveToAbs) { |
| // Any upcoming 'closepath' commands bring us back to the location we have |
| // just moved to. |
| coordinates.initial_x = coordinates.current_x; |
| coordinates.initial_y = coordinates.current_y; |
| } |
| |
| return std::move(result); |
| } |
| |
| PathSegmentData ConsumeInterpolableSingleCoordinate( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| const InterpolableList& list = ToInterpolableList(value); |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| list.Get(0), is_absolute, coordinates.current_x)); |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| list.Get(1), is_absolute, coordinates.current_y)); |
| |
| if (ToAbsolutePathSegType(seg_type) == kPathSegMoveToAbs) { |
| // Any upcoming 'closepath' commands bring us back to the location we have |
| // just moved to. |
| coordinates.initial_x = coordinates.current_x; |
| coordinates.initial_y = coordinates.current_y; |
| } |
| |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeCurvetoCubic( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| std::unique_ptr<InterpolableList> result = InterpolableList::Create(6); |
| result->Set( |
| 0, ConsumeControlAxis(segment.X1(), is_absolute, coordinates.current_x)); |
| result->Set( |
| 1, ConsumeControlAxis(segment.Y1(), is_absolute, coordinates.current_y)); |
| result->Set( |
| 2, ConsumeControlAxis(segment.X2(), is_absolute, coordinates.current_x)); |
| result->Set( |
| 3, ConsumeControlAxis(segment.Y2(), is_absolute, coordinates.current_y)); |
| result->Set(4, ConsumeCoordinateAxis(segment.X(), is_absolute, |
| coordinates.current_x)); |
| result->Set(5, ConsumeCoordinateAxis(segment.Y(), is_absolute, |
| coordinates.current_y)); |
| return std::move(result); |
| } |
| |
| PathSegmentData ConsumeInterpolableCurvetoCubic(const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| const InterpolableList& list = ToInterpolableList(value); |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.point1.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute, |
| coordinates.current_x)); |
| segment.point1.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute, |
| coordinates.current_y)); |
| segment.point2.SetX(ConsumeInterpolableControlAxis(list.Get(2), is_absolute, |
| coordinates.current_x)); |
| segment.point2.SetY(ConsumeInterpolableControlAxis(list.Get(3), is_absolute, |
| coordinates.current_y)); |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| list.Get(4), is_absolute, coordinates.current_x)); |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| list.Get(5), is_absolute, coordinates.current_y)); |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeCurvetoQuadratic( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| std::unique_ptr<InterpolableList> result = InterpolableList::Create(4); |
| result->Set( |
| 0, ConsumeControlAxis(segment.X1(), is_absolute, coordinates.current_x)); |
| result->Set( |
| 1, ConsumeControlAxis(segment.Y1(), is_absolute, coordinates.current_y)); |
| result->Set(2, ConsumeCoordinateAxis(segment.X(), is_absolute, |
| coordinates.current_x)); |
| result->Set(3, ConsumeCoordinateAxis(segment.Y(), is_absolute, |
| coordinates.current_y)); |
| return std::move(result); |
| } |
| |
| PathSegmentData ConsumeInterpolableCurvetoQuadratic( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| const InterpolableList& list = ToInterpolableList(value); |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.point1.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute, |
| coordinates.current_x)); |
| segment.point1.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute, |
| coordinates.current_y)); |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| list.Get(2), is_absolute, coordinates.current_x)); |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| list.Get(3), is_absolute, coordinates.current_y)); |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeArc(const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| std::unique_ptr<InterpolableList> result = InterpolableList::Create(7); |
| result->Set(0, ConsumeCoordinateAxis(segment.X(), is_absolute, |
| coordinates.current_x)); |
| result->Set(1, ConsumeCoordinateAxis(segment.Y(), is_absolute, |
| coordinates.current_y)); |
| result->Set(2, InterpolableNumber::Create(segment.R1())); |
| result->Set(3, InterpolableNumber::Create(segment.R2())); |
| result->Set(4, InterpolableNumber::Create(segment.ArcAngle())); |
| // TODO(alancutter): Make these flags part of the NonInterpolableValue. |
| result->Set(5, InterpolableNumber::Create(segment.LargeArcFlag())); |
| result->Set(6, InterpolableNumber::Create(segment.SweepFlag())); |
| return std::move(result); |
| } |
| |
| PathSegmentData ConsumeInterpolableArc(const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| const InterpolableList& list = ToInterpolableList(value); |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| list.Get(0), is_absolute, coordinates.current_x)); |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| list.Get(1), is_absolute, coordinates.current_y)); |
| segment.ArcRadii().SetX(ToInterpolableNumber(list.Get(2))->Value()); |
| segment.ArcRadii().SetY(ToInterpolableNumber(list.Get(3))->Value()); |
| segment.SetArcAngle(ToInterpolableNumber(list.Get(4))->Value()); |
| segment.arc_large = ToInterpolableNumber(list.Get(5))->Value() >= 0.5; |
| segment.arc_sweep = ToInterpolableNumber(list.Get(6))->Value() >= 0.5; |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeLinetoHorizontal( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| return ConsumeCoordinateAxis(segment.X(), is_absolute, coordinates.current_x); |
| } |
| |
| PathSegmentData ConsumeInterpolableLinetoHorizontal( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| &value, is_absolute, coordinates.current_x)); |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeLinetoVertical( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| return ConsumeCoordinateAxis(segment.Y(), is_absolute, coordinates.current_y); |
| } |
| |
| PathSegmentData ConsumeInterpolableLinetoVertical( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| &value, is_absolute, coordinates.current_y)); |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> ConsumeCurvetoCubicSmooth( |
| const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| bool is_absolute = IsAbsolutePathSegType(segment.command); |
| std::unique_ptr<InterpolableList> result = InterpolableList::Create(4); |
| result->Set( |
| 0, ConsumeControlAxis(segment.X2(), is_absolute, coordinates.current_x)); |
| result->Set( |
| 1, ConsumeControlAxis(segment.Y2(), is_absolute, coordinates.current_y)); |
| result->Set(2, ConsumeCoordinateAxis(segment.X(), is_absolute, |
| coordinates.current_x)); |
| result->Set(3, ConsumeCoordinateAxis(segment.Y(), is_absolute, |
| coordinates.current_y)); |
| return std::move(result); |
| } |
| |
| PathSegmentData ConsumeInterpolableCurvetoCubicSmooth( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| const InterpolableList& list = ToInterpolableList(value); |
| bool is_absolute = IsAbsolutePathSegType(seg_type); |
| PathSegmentData segment; |
| segment.command = seg_type; |
| segment.point2.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute, |
| coordinates.current_x)); |
| segment.point2.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute, |
| coordinates.current_y)); |
| segment.target_point.SetX(ConsumeInterpolableCoordinateAxis( |
| list.Get(2), is_absolute, coordinates.current_x)); |
| segment.target_point.SetY(ConsumeInterpolableCoordinateAxis( |
| list.Get(3), is_absolute, coordinates.current_y)); |
| return segment; |
| } |
| |
| std::unique_ptr<InterpolableValue> |
| SVGPathSegInterpolationFunctions::ConsumePathSeg(const PathSegmentData& segment, |
| PathCoordinates& coordinates) { |
| switch (segment.command) { |
| case kPathSegClosePath: |
| return ConsumeClosePath(segment, coordinates); |
| |
| case kPathSegMoveToAbs: |
| case kPathSegMoveToRel: |
| case kPathSegLineToAbs: |
| case kPathSegLineToRel: |
| case kPathSegCurveToQuadraticSmoothAbs: |
| case kPathSegCurveToQuadraticSmoothRel: |
| return ConsumeSingleCoordinate(segment, coordinates); |
| |
| case kPathSegCurveToCubicAbs: |
| case kPathSegCurveToCubicRel: |
| return ConsumeCurvetoCubic(segment, coordinates); |
| |
| case kPathSegCurveToQuadraticAbs: |
| case kPathSegCurveToQuadraticRel: |
| return ConsumeCurvetoQuadratic(segment, coordinates); |
| |
| case kPathSegArcAbs: |
| case kPathSegArcRel: |
| return ConsumeArc(segment, coordinates); |
| |
| case kPathSegLineToHorizontalAbs: |
| case kPathSegLineToHorizontalRel: |
| return ConsumeLinetoHorizontal(segment, coordinates); |
| |
| case kPathSegLineToVerticalAbs: |
| case kPathSegLineToVerticalRel: |
| return ConsumeLinetoVertical(segment, coordinates); |
| |
| case kPathSegCurveToCubicSmoothAbs: |
| case kPathSegCurveToCubicSmoothRel: |
| return ConsumeCurvetoCubicSmooth(segment, coordinates); |
| |
| case kPathSegUnknown: |
| default: |
| NOTREACHED(); |
| return nullptr; |
| } |
| } |
| |
| PathSegmentData SVGPathSegInterpolationFunctions::ConsumeInterpolablePathSeg( |
| const InterpolableValue& value, |
| SVGPathSegType seg_type, |
| PathCoordinates& coordinates) { |
| switch (seg_type) { |
| case kPathSegClosePath: |
| return ConsumeInterpolableClosePath(value, seg_type, coordinates); |
| |
| case kPathSegMoveToAbs: |
| case kPathSegMoveToRel: |
| case kPathSegLineToAbs: |
| case kPathSegLineToRel: |
| case kPathSegCurveToQuadraticSmoothAbs: |
| case kPathSegCurveToQuadraticSmoothRel: |
| return ConsumeInterpolableSingleCoordinate(value, seg_type, coordinates); |
| |
| case kPathSegCurveToCubicAbs: |
| case kPathSegCurveToCubicRel: |
| return ConsumeInterpolableCurvetoCubic(value, seg_type, coordinates); |
| |
| case kPathSegCurveToQuadraticAbs: |
| case kPathSegCurveToQuadraticRel: |
| return ConsumeInterpolableCurvetoQuadratic(value, seg_type, coordinates); |
| |
| case kPathSegArcAbs: |
| case kPathSegArcRel: |
| return ConsumeInterpolableArc(value, seg_type, coordinates); |
| |
| case kPathSegLineToHorizontalAbs: |
| case kPathSegLineToHorizontalRel: |
| return ConsumeInterpolableLinetoHorizontal(value, seg_type, coordinates); |
| |
| case kPathSegLineToVerticalAbs: |
| case kPathSegLineToVerticalRel: |
| return ConsumeInterpolableLinetoVertical(value, seg_type, coordinates); |
| |
| case kPathSegCurveToCubicSmoothAbs: |
| case kPathSegCurveToCubicSmoothRel: |
| return ConsumeInterpolableCurvetoCubicSmooth(value, seg_type, |
| coordinates); |
| |
| case kPathSegUnknown: |
| default: |
| NOTREACHED(); |
| return PathSegmentData(); |
| } |
| } |
| |
| } // namespace blink |