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