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