1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/animation/svg_path_seg_interpolation_functions.h"
6 
7 #include <memory>
8 
9 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
10 
11 namespace blink {
12 
ConsumeControlAxis(double value,bool is_absolute,double current_value)13 std::unique_ptr<InterpolableNumber> ConsumeControlAxis(double value,
14                                                        bool is_absolute,
15                                                        double current_value) {
16   return std::make_unique<InterpolableNumber>(
17       is_absolute ? value : current_value + value);
18 }
19 
ConsumeInterpolableControlAxis(const InterpolableValue * number,bool is_absolute,double current_value)20 float ConsumeInterpolableControlAxis(const InterpolableValue* number,
21                                      bool is_absolute,
22                                      double current_value) {
23   double value = To<InterpolableNumber>(number)->Value();
24   return clampTo<float>(is_absolute ? value : value - current_value);
25 }
26 
27 std::unique_ptr<InterpolableNumber>
ConsumeCoordinateAxis(double value,bool is_absolute,double & current_value)28 ConsumeCoordinateAxis(double value, bool is_absolute, double& current_value) {
29   if (is_absolute)
30     current_value = value;
31   else
32     current_value += value;
33   return std::make_unique<InterpolableNumber>(current_value);
34 }
35 
ConsumeInterpolableCoordinateAxis(const InterpolableValue * number,bool is_absolute,double & current_value)36 float ConsumeInterpolableCoordinateAxis(const InterpolableValue* number,
37                                         bool is_absolute,
38                                         double& current_value) {
39   double previous_value = current_value;
40   current_value = To<InterpolableNumber>(number)->Value();
41   return clampTo<float>(is_absolute ? current_value
42                                     : current_value - previous_value);
43 }
44 
ConsumeClosePath(const PathSegmentData &,PathCoordinates & coordinates)45 std::unique_ptr<InterpolableValue> ConsumeClosePath(
46     const PathSegmentData&,
47     PathCoordinates& coordinates) {
48   coordinates.current_x = coordinates.initial_x;
49   coordinates.current_y = coordinates.initial_y;
50   return std::make_unique<InterpolableList>(0);
51 }
52 
ConsumeInterpolableClosePath(const InterpolableValue &,SVGPathSegType seg_type,PathCoordinates & coordinates)53 PathSegmentData ConsumeInterpolableClosePath(const InterpolableValue&,
54                                              SVGPathSegType seg_type,
55                                              PathCoordinates& coordinates) {
56   coordinates.current_x = coordinates.initial_x;
57   coordinates.current_y = coordinates.initial_y;
58 
59   PathSegmentData segment;
60   segment.command = seg_type;
61   return segment;
62 }
63 
ConsumeSingleCoordinate(const PathSegmentData & segment,PathCoordinates & coordinates)64 std::unique_ptr<InterpolableValue> ConsumeSingleCoordinate(
65     const PathSegmentData& segment,
66     PathCoordinates& coordinates) {
67   bool is_absolute = IsAbsolutePathSegType(segment.command);
68   auto result = std::make_unique<InterpolableList>(2);
69   result->Set(0, ConsumeCoordinateAxis(segment.X(), is_absolute,
70                                        coordinates.current_x));
71   result->Set(1, ConsumeCoordinateAxis(segment.Y(), is_absolute,
72                                        coordinates.current_y));
73 
74   if (ToAbsolutePathSegType(segment.command) == kPathSegMoveToAbs) {
75     // Any upcoming 'closepath' commands bring us back to the location we have
76     // just moved to.
77     coordinates.initial_x = coordinates.current_x;
78     coordinates.initial_y = coordinates.current_y;
79   }
80 
81   return std::move(result);
82 }
83 
ConsumeInterpolableSingleCoordinate(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)84 PathSegmentData ConsumeInterpolableSingleCoordinate(
85     const InterpolableValue& value,
86     SVGPathSegType seg_type,
87     PathCoordinates& coordinates) {
88   const auto& list = To<InterpolableList>(value);
89   bool is_absolute = IsAbsolutePathSegType(seg_type);
90   PathSegmentData segment;
91   segment.command = seg_type;
92   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
93       list.Get(0), is_absolute, coordinates.current_x));
94   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
95       list.Get(1), is_absolute, coordinates.current_y));
96 
97   if (ToAbsolutePathSegType(seg_type) == kPathSegMoveToAbs) {
98     // Any upcoming 'closepath' commands bring us back to the location we have
99     // just moved to.
100     coordinates.initial_x = coordinates.current_x;
101     coordinates.initial_y = coordinates.current_y;
102   }
103 
104   return segment;
105 }
106 
ConsumeCurvetoCubic(const PathSegmentData & segment,PathCoordinates & coordinates)107 std::unique_ptr<InterpolableValue> ConsumeCurvetoCubic(
108     const PathSegmentData& segment,
109     PathCoordinates& coordinates) {
110   bool is_absolute = IsAbsolutePathSegType(segment.command);
111   auto result = std::make_unique<InterpolableList>(6);
112   result->Set(
113       0, ConsumeControlAxis(segment.X1(), is_absolute, coordinates.current_x));
114   result->Set(
115       1, ConsumeControlAxis(segment.Y1(), is_absolute, coordinates.current_y));
116   result->Set(
117       2, ConsumeControlAxis(segment.X2(), is_absolute, coordinates.current_x));
118   result->Set(
119       3, ConsumeControlAxis(segment.Y2(), is_absolute, coordinates.current_y));
120   result->Set(4, ConsumeCoordinateAxis(segment.X(), is_absolute,
121                                        coordinates.current_x));
122   result->Set(5, ConsumeCoordinateAxis(segment.Y(), is_absolute,
123                                        coordinates.current_y));
124   return std::move(result);
125 }
126 
ConsumeInterpolableCurvetoCubic(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)127 PathSegmentData ConsumeInterpolableCurvetoCubic(const InterpolableValue& value,
128                                                 SVGPathSegType seg_type,
129                                                 PathCoordinates& coordinates) {
130   const auto& list = To<InterpolableList>(value);
131   bool is_absolute = IsAbsolutePathSegType(seg_type);
132   PathSegmentData segment;
133   segment.command = seg_type;
134   segment.point1.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute,
135                                                      coordinates.current_x));
136   segment.point1.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute,
137                                                      coordinates.current_y));
138   segment.point2.SetX(ConsumeInterpolableControlAxis(list.Get(2), is_absolute,
139                                                      coordinates.current_x));
140   segment.point2.SetY(ConsumeInterpolableControlAxis(list.Get(3), is_absolute,
141                                                      coordinates.current_y));
142   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
143       list.Get(4), is_absolute, coordinates.current_x));
144   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
145       list.Get(5), is_absolute, coordinates.current_y));
146   return segment;
147 }
148 
ConsumeCurvetoQuadratic(const PathSegmentData & segment,PathCoordinates & coordinates)149 std::unique_ptr<InterpolableValue> ConsumeCurvetoQuadratic(
150     const PathSegmentData& segment,
151     PathCoordinates& coordinates) {
152   bool is_absolute = IsAbsolutePathSegType(segment.command);
153   auto result = std::make_unique<InterpolableList>(4);
154   result->Set(
155       0, ConsumeControlAxis(segment.X1(), is_absolute, coordinates.current_x));
156   result->Set(
157       1, ConsumeControlAxis(segment.Y1(), is_absolute, coordinates.current_y));
158   result->Set(2, ConsumeCoordinateAxis(segment.X(), is_absolute,
159                                        coordinates.current_x));
160   result->Set(3, ConsumeCoordinateAxis(segment.Y(), is_absolute,
161                                        coordinates.current_y));
162   return std::move(result);
163 }
164 
ConsumeInterpolableCurvetoQuadratic(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)165 PathSegmentData ConsumeInterpolableCurvetoQuadratic(
166     const InterpolableValue& value,
167     SVGPathSegType seg_type,
168     PathCoordinates& coordinates) {
169   const auto& list = To<InterpolableList>(value);
170   bool is_absolute = IsAbsolutePathSegType(seg_type);
171   PathSegmentData segment;
172   segment.command = seg_type;
173   segment.point1.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute,
174                                                      coordinates.current_x));
175   segment.point1.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute,
176                                                      coordinates.current_y));
177   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
178       list.Get(2), is_absolute, coordinates.current_x));
179   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
180       list.Get(3), is_absolute, coordinates.current_y));
181   return segment;
182 }
183 
ConsumeArc(const PathSegmentData & segment,PathCoordinates & coordinates)184 std::unique_ptr<InterpolableValue> ConsumeArc(const PathSegmentData& segment,
185                                               PathCoordinates& coordinates) {
186   bool is_absolute = IsAbsolutePathSegType(segment.command);
187   auto result = std::make_unique<InterpolableList>(7);
188   result->Set(0, ConsumeCoordinateAxis(segment.X(), is_absolute,
189                                        coordinates.current_x));
190   result->Set(1, ConsumeCoordinateAxis(segment.Y(), is_absolute,
191                                        coordinates.current_y));
192   result->Set(2, std::make_unique<InterpolableNumber>(segment.R1()));
193   result->Set(3, std::make_unique<InterpolableNumber>(segment.R2()));
194   result->Set(4, std::make_unique<InterpolableNumber>(segment.ArcAngle()));
195   // TODO(alancutter): Make these flags part of the NonInterpolableValue.
196   result->Set(5, std::make_unique<InterpolableNumber>(segment.LargeArcFlag()));
197   result->Set(6, std::make_unique<InterpolableNumber>(segment.SweepFlag()));
198   return std::move(result);
199 }
200 
ConsumeInterpolableArc(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)201 PathSegmentData ConsumeInterpolableArc(const InterpolableValue& value,
202                                        SVGPathSegType seg_type,
203                                        PathCoordinates& coordinates) {
204   const auto& list = To<InterpolableList>(value);
205   bool is_absolute = IsAbsolutePathSegType(seg_type);
206   PathSegmentData segment;
207   segment.command = seg_type;
208   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
209       list.Get(0), is_absolute, coordinates.current_x));
210   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
211       list.Get(1), is_absolute, coordinates.current_y));
212   segment.ArcRadii().SetX(To<InterpolableNumber>(list.Get(2))->Value());
213   segment.ArcRadii().SetY(To<InterpolableNumber>(list.Get(3))->Value());
214   segment.SetArcAngle(To<InterpolableNumber>(list.Get(4))->Value());
215   segment.arc_large = To<InterpolableNumber>(list.Get(5))->Value() >= 0.5;
216   segment.arc_sweep = To<InterpolableNumber>(list.Get(6))->Value() >= 0.5;
217   return segment;
218 }
219 
ConsumeLinetoHorizontal(const PathSegmentData & segment,PathCoordinates & coordinates)220 std::unique_ptr<InterpolableValue> ConsumeLinetoHorizontal(
221     const PathSegmentData& segment,
222     PathCoordinates& coordinates) {
223   bool is_absolute = IsAbsolutePathSegType(segment.command);
224   return ConsumeCoordinateAxis(segment.X(), is_absolute, coordinates.current_x);
225 }
226 
ConsumeInterpolableLinetoHorizontal(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)227 PathSegmentData ConsumeInterpolableLinetoHorizontal(
228     const InterpolableValue& value,
229     SVGPathSegType seg_type,
230     PathCoordinates& coordinates) {
231   bool is_absolute = IsAbsolutePathSegType(seg_type);
232   PathSegmentData segment;
233   segment.command = seg_type;
234   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
235       &value, is_absolute, coordinates.current_x));
236   return segment;
237 }
238 
ConsumeLinetoVertical(const PathSegmentData & segment,PathCoordinates & coordinates)239 std::unique_ptr<InterpolableValue> ConsumeLinetoVertical(
240     const PathSegmentData& segment,
241     PathCoordinates& coordinates) {
242   bool is_absolute = IsAbsolutePathSegType(segment.command);
243   return ConsumeCoordinateAxis(segment.Y(), is_absolute, coordinates.current_y);
244 }
245 
ConsumeInterpolableLinetoVertical(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)246 PathSegmentData ConsumeInterpolableLinetoVertical(
247     const InterpolableValue& value,
248     SVGPathSegType seg_type,
249     PathCoordinates& coordinates) {
250   bool is_absolute = IsAbsolutePathSegType(seg_type);
251   PathSegmentData segment;
252   segment.command = seg_type;
253   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
254       &value, is_absolute, coordinates.current_y));
255   return segment;
256 }
257 
ConsumeCurvetoCubicSmooth(const PathSegmentData & segment,PathCoordinates & coordinates)258 std::unique_ptr<InterpolableValue> ConsumeCurvetoCubicSmooth(
259     const PathSegmentData& segment,
260     PathCoordinates& coordinates) {
261   bool is_absolute = IsAbsolutePathSegType(segment.command);
262   auto result = std::make_unique<InterpolableList>(4);
263   result->Set(
264       0, ConsumeControlAxis(segment.X2(), is_absolute, coordinates.current_x));
265   result->Set(
266       1, ConsumeControlAxis(segment.Y2(), is_absolute, coordinates.current_y));
267   result->Set(2, ConsumeCoordinateAxis(segment.X(), is_absolute,
268                                        coordinates.current_x));
269   result->Set(3, ConsumeCoordinateAxis(segment.Y(), is_absolute,
270                                        coordinates.current_y));
271   return std::move(result);
272 }
273 
ConsumeInterpolableCurvetoCubicSmooth(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)274 PathSegmentData ConsumeInterpolableCurvetoCubicSmooth(
275     const InterpolableValue& value,
276     SVGPathSegType seg_type,
277     PathCoordinates& coordinates) {
278   const auto& list = To<InterpolableList>(value);
279   bool is_absolute = IsAbsolutePathSegType(seg_type);
280   PathSegmentData segment;
281   segment.command = seg_type;
282   segment.point2.SetX(ConsumeInterpolableControlAxis(list.Get(0), is_absolute,
283                                                      coordinates.current_x));
284   segment.point2.SetY(ConsumeInterpolableControlAxis(list.Get(1), is_absolute,
285                                                      coordinates.current_y));
286   segment.target_point.SetX(ConsumeInterpolableCoordinateAxis(
287       list.Get(2), is_absolute, coordinates.current_x));
288   segment.target_point.SetY(ConsumeInterpolableCoordinateAxis(
289       list.Get(3), is_absolute, coordinates.current_y));
290   return segment;
291 }
292 
293 std::unique_ptr<InterpolableValue>
ConsumePathSeg(const PathSegmentData & segment,PathCoordinates & coordinates)294 SVGPathSegInterpolationFunctions::ConsumePathSeg(const PathSegmentData& segment,
295                                                  PathCoordinates& coordinates) {
296   switch (segment.command) {
297     case kPathSegClosePath:
298       return ConsumeClosePath(segment, coordinates);
299 
300     case kPathSegMoveToAbs:
301     case kPathSegMoveToRel:
302     case kPathSegLineToAbs:
303     case kPathSegLineToRel:
304     case kPathSegCurveToQuadraticSmoothAbs:
305     case kPathSegCurveToQuadraticSmoothRel:
306       return ConsumeSingleCoordinate(segment, coordinates);
307 
308     case kPathSegCurveToCubicAbs:
309     case kPathSegCurveToCubicRel:
310       return ConsumeCurvetoCubic(segment, coordinates);
311 
312     case kPathSegCurveToQuadraticAbs:
313     case kPathSegCurveToQuadraticRel:
314       return ConsumeCurvetoQuadratic(segment, coordinates);
315 
316     case kPathSegArcAbs:
317     case kPathSegArcRel:
318       return ConsumeArc(segment, coordinates);
319 
320     case kPathSegLineToHorizontalAbs:
321     case kPathSegLineToHorizontalRel:
322       return ConsumeLinetoHorizontal(segment, coordinates);
323 
324     case kPathSegLineToVerticalAbs:
325     case kPathSegLineToVerticalRel:
326       return ConsumeLinetoVertical(segment, coordinates);
327 
328     case kPathSegCurveToCubicSmoothAbs:
329     case kPathSegCurveToCubicSmoothRel:
330       return ConsumeCurvetoCubicSmooth(segment, coordinates);
331 
332     case kPathSegUnknown:
333     default:
334       NOTREACHED();
335       return nullptr;
336   }
337 }
338 
ConsumeInterpolablePathSeg(const InterpolableValue & value,SVGPathSegType seg_type,PathCoordinates & coordinates)339 PathSegmentData SVGPathSegInterpolationFunctions::ConsumeInterpolablePathSeg(
340     const InterpolableValue& value,
341     SVGPathSegType seg_type,
342     PathCoordinates& coordinates) {
343   switch (seg_type) {
344     case kPathSegClosePath:
345       return ConsumeInterpolableClosePath(value, seg_type, coordinates);
346 
347     case kPathSegMoveToAbs:
348     case kPathSegMoveToRel:
349     case kPathSegLineToAbs:
350     case kPathSegLineToRel:
351     case kPathSegCurveToQuadraticSmoothAbs:
352     case kPathSegCurveToQuadraticSmoothRel:
353       return ConsumeInterpolableSingleCoordinate(value, seg_type, coordinates);
354 
355     case kPathSegCurveToCubicAbs:
356     case kPathSegCurveToCubicRel:
357       return ConsumeInterpolableCurvetoCubic(value, seg_type, coordinates);
358 
359     case kPathSegCurveToQuadraticAbs:
360     case kPathSegCurveToQuadraticRel:
361       return ConsumeInterpolableCurvetoQuadratic(value, seg_type, coordinates);
362 
363     case kPathSegArcAbs:
364     case kPathSegArcRel:
365       return ConsumeInterpolableArc(value, seg_type, coordinates);
366 
367     case kPathSegLineToHorizontalAbs:
368     case kPathSegLineToHorizontalRel:
369       return ConsumeInterpolableLinetoHorizontal(value, seg_type, coordinates);
370 
371     case kPathSegLineToVerticalAbs:
372     case kPathSegLineToVerticalRel:
373       return ConsumeInterpolableLinetoVertical(value, seg_type, coordinates);
374 
375     case kPathSegCurveToCubicSmoothAbs:
376     case kPathSegCurveToCubicSmoothRel:
377       return ConsumeInterpolableCurvetoCubicSmooth(value, seg_type,
378                                                    coordinates);
379 
380     case kPathSegUnknown:
381     default:
382       NOTREACHED();
383       return PathSegmentData();
384   }
385 }
386 
387 }  // namespace blink
388