1 /*
2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "third_party/blink/renderer/core/svg/svg_angle.h"
23
24 #include "third_party/blink/renderer/core/svg/svg_animate_element.h"
25 #include "third_party/blink/renderer/core/svg/svg_enumeration_map.h"
26 #include "third_party/blink/renderer/core/svg/svg_parser_utilities.h"
27 #include "third_party/blink/renderer/platform/heap/heap.h"
28 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
29 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
30
31 namespace blink {
32
33 template <>
GetEnumerationMap()34 const SVGEnumerationMap& GetEnumerationMap<SVGMarkerOrientType>() {
35 static const SVGEnumerationMap::Entry enum_items[] = {
36 {kSVGMarkerOrientAuto, "auto"},
37 {kSVGMarkerOrientAngle, "angle"},
38 {kSVGMarkerOrientAutoStartReverse, "auto-start-reverse"},
39 };
40 static const SVGEnumerationMap entries(enum_items, kSVGMarkerOrientAngle);
41 return entries;
42 }
43
SVGMarkerOrientEnumeration(SVGAngle * angle)44 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
45 : SVGEnumeration<SVGMarkerOrientType>(kSVGMarkerOrientAngle),
46 angle_(angle) {}
47
48 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration() = default;
49
Trace(Visitor * visitor)50 void SVGMarkerOrientEnumeration::Trace(Visitor* visitor) {
51 visitor->Trace(angle_);
52 SVGEnumeration<SVGMarkerOrientType>::Trace(visitor);
53 }
54
NotifyChange()55 void SVGMarkerOrientEnumeration::NotifyChange() {
56 DCHECK(angle_);
57 angle_->OrientTypeChanged();
58 }
59
Add(SVGPropertyBase *,SVGElement *)60 void SVGMarkerOrientEnumeration::Add(SVGPropertyBase*, SVGElement*) {
61 // SVGMarkerOrientEnumeration is only animated via SVGAngle
62 NOTREACHED();
63 }
64
CalculateAnimatedValue(const SVGAnimateElement &,float percentage,unsigned repeat_count,SVGPropertyBase * from,SVGPropertyBase * to,SVGPropertyBase * to_at_end_of_duration_value,SVGElement * context_element)65 void SVGMarkerOrientEnumeration::CalculateAnimatedValue(
66 const SVGAnimateElement&,
67 float percentage,
68 unsigned repeat_count,
69 SVGPropertyBase* from,
70 SVGPropertyBase* to,
71 SVGPropertyBase* to_at_end_of_duration_value,
72 SVGElement* context_element) {
73 // SVGMarkerOrientEnumeration is only animated via SVGAngle
74 NOTREACHED();
75 }
76
CalculateDistance(SVGPropertyBase * to,SVGElement * context_element)77 float SVGMarkerOrientEnumeration::CalculateDistance(
78 SVGPropertyBase* to,
79 SVGElement* context_element) {
80 // SVGMarkerOrientEnumeration is only animated via SVGAngle
81 NOTREACHED();
82 return -1.0;
83 }
84
SVGAngle()85 SVGAngle::SVGAngle()
86 : unit_type_(kSvgAngletypeUnspecified),
87 value_in_specified_units_(0),
88 orient_type_(MakeGarbageCollected<SVGMarkerOrientEnumeration>(this)) {}
89
SVGAngle(SVGAngleType unit_type,float value_in_specified_units,SVGMarkerOrientType orient_type)90 SVGAngle::SVGAngle(SVGAngleType unit_type,
91 float value_in_specified_units,
92 SVGMarkerOrientType orient_type)
93 : unit_type_(unit_type),
94 value_in_specified_units_(value_in_specified_units),
95 orient_type_(MakeGarbageCollected<SVGMarkerOrientEnumeration>(this)) {
96 orient_type_->SetEnumValue(orient_type);
97 }
98
99 SVGAngle::~SVGAngle() = default;
100
Trace(Visitor * visitor)101 void SVGAngle::Trace(Visitor* visitor) {
102 visitor->Trace(orient_type_);
103 SVGPropertyHelper<SVGAngle>::Trace(visitor);
104 }
105
Clone() const106 SVGAngle* SVGAngle::Clone() const {
107 return MakeGarbageCollected<SVGAngle>(unit_type_, value_in_specified_units_,
108 orient_type_->EnumValue());
109 }
110
Value() const111 float SVGAngle::Value() const {
112 switch (unit_type_) {
113 case kSvgAngletypeGrad:
114 return grad2deg(value_in_specified_units_);
115 case kSvgAngletypeRad:
116 return rad2deg(value_in_specified_units_);
117 case kSvgAngletypeTurn:
118 return turn2deg(value_in_specified_units_);
119 case kSvgAngletypeUnspecified:
120 case kSvgAngletypeUnknown:
121 case kSvgAngletypeDeg:
122 return value_in_specified_units_;
123 }
124
125 NOTREACHED();
126 return 0;
127 }
128
SetValue(float value)129 void SVGAngle::SetValue(float value) {
130 switch (unit_type_) {
131 case kSvgAngletypeGrad:
132 value_in_specified_units_ = deg2grad(value);
133 break;
134 case kSvgAngletypeRad:
135 value_in_specified_units_ = deg2rad(value);
136 break;
137 case kSvgAngletypeTurn:
138 value_in_specified_units_ = deg2turn(value);
139 break;
140 case kSvgAngletypeUnspecified:
141 case kSvgAngletypeUnknown:
142 case kSvgAngletypeDeg:
143 value_in_specified_units_ = value;
144 break;
145 }
146 orient_type_->SetEnumValue(kSVGMarkerOrientAngle);
147 }
148
149 template <typename CharType>
StringToAngleType(const CharType * & ptr,const CharType * end)150 static SVGAngle::SVGAngleType StringToAngleType(const CharType*& ptr,
151 const CharType* end) {
152 // If there's no unit given, the angle type is unspecified.
153 if (ptr == end)
154 return SVGAngle::kSvgAngletypeUnspecified;
155
156 SVGAngle::SVGAngleType type = SVGAngle::kSvgAngletypeUnknown;
157 if (IsHTMLSpace<CharType>(ptr[0])) {
158 type = SVGAngle::kSvgAngletypeUnspecified;
159 ptr++;
160 } else if (end - ptr >= 3) {
161 if (ptr[0] == 'd' && ptr[1] == 'e' && ptr[2] == 'g') {
162 type = SVGAngle::kSvgAngletypeDeg;
163 ptr += 3;
164 } else if (ptr[0] == 'r' && ptr[1] == 'a' && ptr[2] == 'd') {
165 type = SVGAngle::kSvgAngletypeRad;
166 ptr += 3;
167 } else if (end - ptr >= 4) {
168 if (ptr[0] == 'g' && ptr[1] == 'r' && ptr[2] == 'a' && ptr[3] == 'd') {
169 type = SVGAngle::kSvgAngletypeGrad;
170 ptr += 4;
171 } else if (ptr[0] == 't' && ptr[1] == 'u' && ptr[2] == 'r' &&
172 ptr[3] == 'n') {
173 type = SVGAngle::kSvgAngletypeTurn;
174 ptr += 4;
175 }
176 }
177 }
178
179 if (!SkipOptionalSVGSpaces(ptr, end))
180 return type;
181
182 return SVGAngle::kSvgAngletypeUnknown;
183 }
184
ValueAsString() const185 String SVGAngle::ValueAsString() const {
186 switch (unit_type_) {
187 case kSvgAngletypeDeg: {
188 DEFINE_STATIC_LOCAL(String, deg_string, ("deg"));
189 return String::Number(value_in_specified_units_) + deg_string;
190 }
191 case kSvgAngletypeRad: {
192 DEFINE_STATIC_LOCAL(String, rad_string, ("rad"));
193 return String::Number(value_in_specified_units_) + rad_string;
194 }
195 case kSvgAngletypeGrad: {
196 DEFINE_STATIC_LOCAL(String, grad_string, ("grad"));
197 return String::Number(value_in_specified_units_) + grad_string;
198 }
199 case kSvgAngletypeTurn: {
200 DEFINE_STATIC_LOCAL(String, turn_string, ("turn"));
201 return String::Number(value_in_specified_units_) + turn_string;
202 }
203 case kSvgAngletypeUnspecified:
204 case kSvgAngletypeUnknown:
205 return String::Number(value_in_specified_units_);
206 }
207
208 NOTREACHED();
209 return String();
210 }
211
212 template <typename CharType>
ParseValue(const String & value,float & value_in_specified_units,SVGAngle::SVGAngleType & unit_type)213 static SVGParsingError ParseValue(const String& value,
214 float& value_in_specified_units,
215 SVGAngle::SVGAngleType& unit_type) {
216 const CharType* ptr = value.GetCharacters<CharType>();
217 const CharType* end = ptr + value.length();
218
219 if (!ParseNumber(ptr, end, value_in_specified_units, kAllowLeadingWhitespace))
220 return SVGParsingError(SVGParseStatus::kExpectedAngle,
221 ptr - value.GetCharacters<CharType>());
222
223 unit_type = StringToAngleType(ptr, end);
224 if (unit_type == SVGAngle::kSvgAngletypeUnknown)
225 return SVGParsingError(SVGParseStatus::kExpectedAngle,
226 ptr - value.GetCharacters<CharType>());
227
228 return SVGParseStatus::kNoError;
229 }
230
SetValueAsString(const String & value)231 SVGParsingError SVGAngle::SetValueAsString(const String& value) {
232 if (value.IsEmpty()) {
233 NewValueSpecifiedUnits(kSvgAngletypeUnspecified, 0);
234 return SVGParseStatus::kNoError;
235 }
236
237 if (value == "auto") {
238 NewValueSpecifiedUnits(kSvgAngletypeUnspecified, 0);
239 orient_type_->SetEnumValue(kSVGMarkerOrientAuto);
240 return SVGParseStatus::kNoError;
241 }
242 if (value == "auto-start-reverse") {
243 NewValueSpecifiedUnits(kSvgAngletypeUnspecified, 0);
244 orient_type_->SetEnumValue(kSVGMarkerOrientAutoStartReverse);
245 return SVGParseStatus::kNoError;
246 }
247
248 float value_in_specified_units = 0;
249 SVGAngleType unit_type = kSvgAngletypeUnknown;
250
251 SVGParsingError error;
252 if (value.Is8Bit())
253 error = ParseValue<LChar>(value, value_in_specified_units, unit_type);
254 else
255 error = ParseValue<UChar>(value, value_in_specified_units, unit_type);
256 if (error != SVGParseStatus::kNoError)
257 return error;
258
259 orient_type_->SetEnumValue(kSVGMarkerOrientAngle);
260 unit_type_ = unit_type;
261 value_in_specified_units_ = value_in_specified_units;
262 return SVGParseStatus::kNoError;
263 }
264
NewValueSpecifiedUnits(SVGAngleType unit_type,float value_in_specified_units)265 void SVGAngle::NewValueSpecifiedUnits(SVGAngleType unit_type,
266 float value_in_specified_units) {
267 orient_type_->SetEnumValue(kSVGMarkerOrientAngle);
268 unit_type_ = unit_type;
269 value_in_specified_units_ = value_in_specified_units;
270 }
271
ConvertToSpecifiedUnits(SVGAngleType unit_type)272 void SVGAngle::ConvertToSpecifiedUnits(SVGAngleType unit_type) {
273 if (unit_type == unit_type_)
274 return;
275
276 switch (unit_type_) {
277 case kSvgAngletypeTurn:
278 switch (unit_type) {
279 case kSvgAngletypeGrad:
280 value_in_specified_units_ = turn2grad(value_in_specified_units_);
281 break;
282 case kSvgAngletypeUnspecified:
283 case kSvgAngletypeDeg:
284 value_in_specified_units_ = turn2deg(value_in_specified_units_);
285 break;
286 case kSvgAngletypeRad:
287 value_in_specified_units_ =
288 deg2rad(turn2deg(value_in_specified_units_));
289 break;
290 case kSvgAngletypeTurn:
291 case kSvgAngletypeUnknown:
292 NOTREACHED();
293 break;
294 }
295 break;
296 case kSvgAngletypeRad:
297 switch (unit_type) {
298 case kSvgAngletypeGrad:
299 value_in_specified_units_ = rad2grad(value_in_specified_units_);
300 break;
301 case kSvgAngletypeUnspecified:
302 case kSvgAngletypeDeg:
303 value_in_specified_units_ = rad2deg(value_in_specified_units_);
304 break;
305 case kSvgAngletypeTurn:
306 value_in_specified_units_ =
307 deg2turn(rad2deg(value_in_specified_units_));
308 break;
309 case kSvgAngletypeRad:
310 case kSvgAngletypeUnknown:
311 NOTREACHED();
312 break;
313 }
314 break;
315 case kSvgAngletypeGrad:
316 switch (unit_type) {
317 case kSvgAngletypeRad:
318 value_in_specified_units_ = grad2rad(value_in_specified_units_);
319 break;
320 case kSvgAngletypeUnspecified:
321 case kSvgAngletypeDeg:
322 value_in_specified_units_ = grad2deg(value_in_specified_units_);
323 break;
324 case kSvgAngletypeTurn:
325 value_in_specified_units_ = grad2turn(value_in_specified_units_);
326 break;
327 case kSvgAngletypeGrad:
328 case kSvgAngletypeUnknown:
329 NOTREACHED();
330 break;
331 }
332 break;
333 case kSvgAngletypeUnspecified:
334 // Spec: For angles, a unitless value is treated the same as if degrees were
335 // specified.
336 case kSvgAngletypeDeg:
337 switch (unit_type) {
338 case kSvgAngletypeRad:
339 value_in_specified_units_ = deg2rad(value_in_specified_units_);
340 break;
341 case kSvgAngletypeGrad:
342 value_in_specified_units_ = deg2grad(value_in_specified_units_);
343 break;
344 case kSvgAngletypeTurn:
345 value_in_specified_units_ = deg2turn(value_in_specified_units_);
346 break;
347 case kSvgAngletypeUnspecified:
348 case kSvgAngletypeDeg:
349 break;
350 case kSvgAngletypeUnknown:
351 NOTREACHED();
352 break;
353 }
354 break;
355 case kSvgAngletypeUnknown:
356 NOTREACHED();
357 break;
358 }
359
360 unit_type_ = unit_type;
361 orient_type_->SetEnumValue(kSVGMarkerOrientAngle);
362 }
363
Add(SVGPropertyBase * other,SVGElement *)364 void SVGAngle::Add(SVGPropertyBase* other, SVGElement*) {
365 auto* other_angle = To<SVGAngle>(other);
366
367 // Only respect by animations, if from and by are both specified in angles
368 // (and not, for example, 'auto').
369 if (OrientType()->EnumValue() != kSVGMarkerOrientAngle ||
370 other_angle->OrientType()->EnumValue() != kSVGMarkerOrientAngle)
371 return;
372
373 SetValue(Value() + other_angle->Value());
374 }
375
Assign(const SVGAngle & other)376 void SVGAngle::Assign(const SVGAngle& other) {
377 SVGMarkerOrientType other_orient_type = other.OrientType()->EnumValue();
378 if (other_orient_type == kSVGMarkerOrientAngle) {
379 NewValueSpecifiedUnits(other.UnitType(), other.ValueInSpecifiedUnits());
380 return;
381 }
382 value_in_specified_units_ = 0;
383 orient_type_->SetEnumValue(other_orient_type);
384 }
385
CalculateAnimatedValue(const SVGAnimateElement & animation_element,float percentage,unsigned repeat_count,SVGPropertyBase * from,SVGPropertyBase * to,SVGPropertyBase * to_at_end_of_duration,SVGElement *)386 void SVGAngle::CalculateAnimatedValue(
387 const SVGAnimateElement& animation_element,
388 float percentage,
389 unsigned repeat_count,
390 SVGPropertyBase* from,
391 SVGPropertyBase* to,
392 SVGPropertyBase* to_at_end_of_duration,
393 SVGElement*) {
394 auto* from_angle = To<SVGAngle>(from);
395 auto* to_angle = To<SVGAngle>(to);
396 SVGMarkerOrientType from_orient_type = from_angle->OrientType()->EnumValue();
397 SVGMarkerOrientType to_orient_type = to_angle->OrientType()->EnumValue();
398
399 // We can only interpolate between two SVGAngles with orient-type 'angle',
400 // all other cases will use discrete animation.
401 if (from_orient_type != to_orient_type ||
402 from_orient_type != kSVGMarkerOrientAngle) {
403 Assign(percentage < 0.5f ? *from_angle : *to_angle);
404 return;
405 }
406
407 float animated_value = Value();
408 animation_element.AnimateAdditiveNumber(
409 percentage, repeat_count, from_angle->Value(), to_angle->Value(),
410 To<SVGAngle>(to_at_end_of_duration)->Value(), animated_value);
411 OrientType()->SetEnumValue(kSVGMarkerOrientAngle);
412 SetValue(animated_value);
413 }
414
CalculateDistance(SVGPropertyBase * other,SVGElement *)415 float SVGAngle::CalculateDistance(SVGPropertyBase* other, SVGElement*) {
416 return fabsf(Value() - To<SVGAngle>(other)->Value());
417 }
418
OrientTypeChanged()419 void SVGAngle::OrientTypeChanged() {
420 if (OrientType()->EnumValue() == kSVGMarkerOrientAuto ||
421 OrientType()->EnumValue() == kSVGMarkerOrientAutoStartReverse) {
422 unit_type_ = kSvgAngletypeUnspecified;
423 value_in_specified_units_ = 0;
424 }
425 }
426
427 } // namespace blink
428