Home | History | Annotate | Download | only in svg
      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