6e9b816819f8db8049f2b6e65674865cb1c1b4a9
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / svg / SVGAngle.cpp
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 "config.h"
23 #include "core/svg/SVGAngle.h"
24
25 #include "bindings/core/v8/ExceptionState.h"
26 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
27 #include "core/dom/ExceptionCode.h"
28 #include "core/svg/SVGAnimationElement.h"
29 #include "core/svg/SVGParserUtilities.h"
30 #include "wtf/MathExtras.h"
31 #include "wtf/text/WTFString.h"
32
33 namespace blink {
34
35 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGMarkerOrientType>()
36 {
37     DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
38     if (entries.isEmpty()) {
39         entries.append(std::make_pair(SVGMarkerOrientAuto, "auto"));
40         entries.append(std::make_pair(SVGMarkerOrientAngle, "angle"));
41     }
42     return entries;
43 }
44
45 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
46     : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
47     , m_angle(angle)
48 {
49 }
50
51 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
52 {
53 }
54
55 void SVGMarkerOrientEnumeration::notifyChange()
56 {
57     ASSERT(m_angle);
58     m_angle->orientTypeChanged();
59 }
60
61 void SVGMarkerOrientEnumeration::add(PassRefPtrWillBeRawPtr<SVGPropertyBase>, SVGElement*)
62 {
63     // SVGMarkerOrientEnumeration is only animated via SVGAngle
64     ASSERT_NOT_REACHED();
65 }
66
67 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
68 {
69     // SVGMarkerOrientEnumeration is only animated via SVGAngle
70     ASSERT_NOT_REACHED();
71 }
72
73 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
74 {
75     // SVGMarkerOrientEnumeration is only animated via SVGAngle
76     ASSERT_NOT_REACHED();
77     return -1.0;
78 }
79
80 SVGAngle::SVGAngle()
81     : m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
82     , m_valueInSpecifiedUnits(0)
83     , m_orientType(SVGMarkerOrientEnumeration::create(this))
84 {
85 }
86
87 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
88     : m_unitType(unitType)
89     , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
90     , m_orientType(SVGMarkerOrientEnumeration::create(this))
91 {
92     m_orientType->setEnumValue(orientType);
93 }
94
95 SVGAngle::~SVGAngle()
96 {
97 }
98
99 PassRefPtr<SVGAngle> SVGAngle::clone() const
100 {
101     return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
102 }
103
104 float SVGAngle::value() const
105 {
106     switch (m_unitType) {
107     case SVG_ANGLETYPE_GRAD:
108         return grad2deg(m_valueInSpecifiedUnits);
109     case SVG_ANGLETYPE_RAD:
110         return rad2deg(m_valueInSpecifiedUnits);
111     case SVG_ANGLETYPE_TURN:
112         return turn2deg(m_valueInSpecifiedUnits);
113     case SVG_ANGLETYPE_UNSPECIFIED:
114     case SVG_ANGLETYPE_UNKNOWN:
115     case SVG_ANGLETYPE_DEG:
116         return m_valueInSpecifiedUnits;
117     }
118
119     ASSERT_NOT_REACHED();
120     return 0;
121 }
122
123 void SVGAngle::setValue(float value)
124 {
125     switch (m_unitType) {
126     case SVG_ANGLETYPE_GRAD:
127         m_valueInSpecifiedUnits = deg2grad(value);
128         break;
129     case SVG_ANGLETYPE_RAD:
130         m_valueInSpecifiedUnits = deg2rad(value);
131         break;
132     case SVG_ANGLETYPE_TURN:
133         m_valueInSpecifiedUnits = deg2turn(value);
134         break;
135     case SVG_ANGLETYPE_UNSPECIFIED:
136     case SVG_ANGLETYPE_UNKNOWN:
137     case SVG_ANGLETYPE_DEG:
138         m_valueInSpecifiedUnits = value;
139         break;
140     }
141     m_orientType->setEnumValue(SVGMarkerOrientAngle);
142 }
143
144 template<typename CharType>
145 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
146 {
147     // If there's no unit given, the angle type is unspecified.
148     if (ptr == end)
149         return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
150
151     SVGAngle::SVGAngleType type = SVGAngle::SVG_ANGLETYPE_UNKNOWN;
152     const CharType firstChar = *ptr++;
153
154     if (isHTMLSpace<CharType>(firstChar)) {
155         type = SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
156     } else if (end - ptr >= 2) {
157         const CharType secondChar = *ptr++;
158         const CharType thirdChar = *ptr++;
159         if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g') {
160             type = SVGAngle::SVG_ANGLETYPE_DEG;
161         } else if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd') {
162             type = SVGAngle::SVG_ANGLETYPE_RAD;
163         } else if (ptr != end) {
164             const CharType fourthChar = *ptr++;
165             if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
166                 type = SVGAngle::SVG_ANGLETYPE_GRAD;
167             else if (firstChar == 't' && secondChar == 'u' && thirdChar == 'r' && fourthChar == 'n')
168                 type = SVGAngle::SVG_ANGLETYPE_TURN;
169         }
170     }
171
172     if (!skipOptionalSVGSpaces(ptr, end))
173         return type;
174
175     return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
176 }
177
178 String SVGAngle::valueAsString() const
179 {
180     switch (m_unitType) {
181     case SVG_ANGLETYPE_DEG: {
182         DEFINE_STATIC_LOCAL(String, degString, ("deg"));
183         return String::number(m_valueInSpecifiedUnits) + degString;
184     }
185     case SVG_ANGLETYPE_RAD: {
186         DEFINE_STATIC_LOCAL(String, radString, ("rad"));
187         return String::number(m_valueInSpecifiedUnits) + radString;
188     }
189     case SVG_ANGLETYPE_GRAD: {
190         DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
191         return String::number(m_valueInSpecifiedUnits) + gradString;
192     }
193     case SVG_ANGLETYPE_TURN: {
194         DEFINE_STATIC_LOCAL(String, turnString, ("turn"));
195         return String::number(m_valueInSpecifiedUnits) + turnString;
196     }
197     case SVG_ANGLETYPE_UNSPECIFIED:
198     case SVG_ANGLETYPE_UNKNOWN:
199         return String::number(m_valueInSpecifiedUnits);
200     }
201
202     ASSERT_NOT_REACHED();
203     return String();
204 }
205
206 template<typename CharType>
207 static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAngle::SVGAngleType& unitType)
208 {
209     const CharType* ptr = value.getCharacters<CharType>();
210     const CharType* end = ptr + value.length();
211
212     if (!parseNumber(ptr, end, valueInSpecifiedUnits, AllowLeadingWhitespace))
213         return false;
214
215     unitType = stringToAngleType(ptr, end);
216     if (unitType == SVGAngle::SVG_ANGLETYPE_UNKNOWN)
217         return false;
218
219     return true;
220 }
221
222 void SVGAngle::setValueAsString(const String& value, ExceptionState& exceptionState)
223 {
224     if (value.isEmpty()) {
225         newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
226         return;
227     }
228
229     if (value == "auto") {
230         newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
231         m_orientType->setEnumValue(SVGMarkerOrientAuto);
232         return;
233     }
234
235     float valueInSpecifiedUnits = 0;
236     SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
237
238     bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
239                                   : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
240     if (!success) {
241         exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
242         return;
243     }
244
245     m_orientType->setEnumValue(SVGMarkerOrientAngle);
246     m_unitType = unitType;
247     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
248 }
249
250 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
251 {
252     m_orientType->setEnumValue(SVGMarkerOrientAngle);
253     m_unitType = unitType;
254     m_valueInSpecifiedUnits = valueInSpecifiedUnits;
255 }
256
257 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
258 {
259     if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
260         exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
261         return;
262     }
263
264     if (unitType == m_unitType)
265         return;
266
267     switch (m_unitType) {
268     case SVG_ANGLETYPE_TURN:
269         switch (unitType) {
270         case SVG_ANGLETYPE_GRAD:
271             m_valueInSpecifiedUnits = turn2grad(m_valueInSpecifiedUnits);
272             break;
273         case SVG_ANGLETYPE_UNSPECIFIED:
274         case SVG_ANGLETYPE_DEG:
275             m_valueInSpecifiedUnits = turn2deg(m_valueInSpecifiedUnits);
276             break;
277         case SVG_ANGLETYPE_RAD:
278             m_valueInSpecifiedUnits = deg2rad(turn2deg(m_valueInSpecifiedUnits));
279             break;
280         case SVG_ANGLETYPE_TURN:
281         case SVG_ANGLETYPE_UNKNOWN:
282             ASSERT_NOT_REACHED();
283             break;
284         }
285         break;
286     case SVG_ANGLETYPE_RAD:
287         switch (unitType) {
288         case SVG_ANGLETYPE_GRAD:
289             m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
290             break;
291         case SVG_ANGLETYPE_UNSPECIFIED:
292         case SVG_ANGLETYPE_DEG:
293             m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
294             break;
295         case SVG_ANGLETYPE_TURN:
296             m_valueInSpecifiedUnits = deg2turn(rad2deg(m_valueInSpecifiedUnits));
297             break;
298         case SVG_ANGLETYPE_RAD:
299         case SVG_ANGLETYPE_UNKNOWN:
300             ASSERT_NOT_REACHED();
301             break;
302         }
303         break;
304     case SVG_ANGLETYPE_GRAD:
305         switch (unitType) {
306         case SVG_ANGLETYPE_RAD:
307             m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
308             break;
309         case SVG_ANGLETYPE_UNSPECIFIED:
310         case SVG_ANGLETYPE_DEG:
311             m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
312             break;
313         case SVG_ANGLETYPE_TURN:
314             m_valueInSpecifiedUnits = grad2turn(m_valueInSpecifiedUnits);
315             break;
316         case SVG_ANGLETYPE_GRAD:
317         case SVG_ANGLETYPE_UNKNOWN:
318             ASSERT_NOT_REACHED();
319             break;
320         }
321         break;
322     case SVG_ANGLETYPE_UNSPECIFIED:
323         // Spec: For angles, a unitless value is treated the same as if degrees were specified.
324     case SVG_ANGLETYPE_DEG:
325         switch (unitType) {
326         case SVG_ANGLETYPE_RAD:
327             m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
328             break;
329         case SVG_ANGLETYPE_GRAD:
330             m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
331             break;
332         case SVG_ANGLETYPE_TURN:
333             m_valueInSpecifiedUnits = deg2turn(m_valueInSpecifiedUnits);
334             break;
335         case SVG_ANGLETYPE_UNSPECIFIED:
336         case SVG_ANGLETYPE_DEG:
337             break;
338         case SVG_ANGLETYPE_UNKNOWN:
339             ASSERT_NOT_REACHED();
340             break;
341         }
342         break;
343     case SVG_ANGLETYPE_UNKNOWN:
344         ASSERT_NOT_REACHED();
345         break;
346     }
347
348     m_unitType = unitType;
349     m_orientType->setEnumValue(SVGMarkerOrientAngle);
350 }
351
352 void SVGAngle::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*)
353 {
354     RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
355
356     // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
357     if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
358         return;
359
360     setValue(value() + otherAngle->value());
361 }
362
363 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
364 {
365     ASSERT(animationElement);
366     bool isToAnimation = animationElement->animationMode() == ToAnimation;
367
368     RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
369     RefPtr<SVGAngle> toAngle = toSVGAngle(to);
370     RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
371
372     SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
373     SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
374
375     if (fromOrientType != toOrientType) {
376         // Animating from eg. auto to 90deg, or auto to 90deg.
377         if (fromOrientType == SVGMarkerOrientAngle) {
378             // Animating from an angle value to eg. 'auto' - this disabled additive as 'auto' is a keyword..
379             if (toOrientType == SVGMarkerOrientAuto) {
380                 if (percentage < 0.5f) {
381                     newValueSpecifiedUnits(fromAngle->unitType(), fromAngle->valueInSpecifiedUnits());
382                     return;
383                 }
384                 orientType()->setEnumValue(SVGMarkerOrientAuto);
385                 return;
386             }
387             m_valueInSpecifiedUnits = 0;
388             orientType()->setEnumValue(SVGMarkerOrientUnknown);
389             return;
390         }
391     }
392
393     // From 'auto' to 'auto'.
394     if (fromOrientType == SVGMarkerOrientAuto) {
395         m_valueInSpecifiedUnits = 0;
396         orientType()->setEnumValue(SVGMarkerOrientAuto);
397         return;
398     }
399
400     // If the enumeration value is not angle or auto, its unknown.
401     if (fromOrientType != SVGMarkerOrientAngle) {
402         m_valueInSpecifiedUnits = 0;
403         orientType()->setEnumValue(SVGMarkerOrientUnknown);
404         return;
405     }
406
407     // Regular from angle to angle animation, with all features like additive etc.
408     float animatedValue = value();
409     animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
410     orientType()->setEnumValue(SVGMarkerOrientAngle);
411     setValue(animatedValue);
412 }
413
414 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
415 {
416     return fabsf(value() - toSVGAngle(other)->value());
417 }
418
419 void SVGAngle::orientTypeChanged()
420 {
421     if (orientType()->enumValue() == SVGMarkerOrientAuto) {
422         m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
423         m_valueInSpecifiedUnits = 0;
424     }
425 }
426
427 }