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