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