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