Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2007 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2008 Apple Inc. All rights reserved.
      6  * Copyright (C) Research In Motion Limited 2012. All rights reserved.
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 
     26 #include "core/svg/SVGTransformList.h"
     27 
     28 #include "core/SVGNames.h"
     29 #include "core/svg/SVGAnimateTransformElement.h"
     30 #include "core/svg/SVGAnimatedNumber.h"
     31 #include "core/svg/SVGParserUtilities.h"
     32 #include "core/svg/SVGTransformDistance.h"
     33 #include "wtf/text/StringBuilder.h"
     34 #include "wtf/text/WTFString.h"
     35 
     36 namespace WebCore {
     37 
     38 inline PassRefPtr<SVGTransformList> toSVGTransformList(PassRefPtr<SVGPropertyBase> passBase)
     39 {
     40     RefPtr<SVGPropertyBase> base = passBase;
     41     ASSERT(base->type() == SVGTransformList::classType());
     42     return static_pointer_cast<SVGTransformList>(base.release());
     43 }
     44 
     45 SVGTransformList::SVGTransformList()
     46 {
     47 }
     48 
     49 SVGTransformList::~SVGTransformList()
     50 {
     51 }
     52 
     53 PassRefPtr<SVGTransform> SVGTransformList::consolidate()
     54 {
     55     AffineTransform matrix;
     56     if (!concatenate(matrix))
     57         return SVGTransform::create();
     58 
     59     RefPtr<SVGTransform> transform = SVGTransform::create(matrix);
     60     clear();
     61     return appendItem(transform);
     62 }
     63 
     64 bool SVGTransformList::concatenate(AffineTransform& result) const
     65 {
     66     if (isEmpty())
     67         return false;
     68 
     69     ConstIterator it = begin();
     70     ConstIterator itEnd = end();
     71     for (; it != itEnd; ++it)
     72         result *= it->matrix();
     73 
     74     return true;
     75 }
     76 
     77 PassRefPtr<SVGTransformList> SVGTransformList::clone()
     78 {
     79     RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create();
     80     svgTransformList->deepCopy(this);
     81     return svgTransformList.release();
     82 }
     83 
     84 namespace {
     85 
     86 template<typename CharType>
     87 int parseTransformParamList(const CharType*& ptr, const CharType* end, float* values, int required, int optional)
     88 {
     89     int parsedParams = 0;
     90     int maxPossibleParams = required + optional;
     91 
     92     bool trailingDelimiter = false;
     93 
     94     skipOptionalSVGSpaces(ptr, end);
     95     while (parsedParams < maxPossibleParams) {
     96         if (!parseNumber(ptr, end, values[parsedParams], DisallowWhitespace))
     97             break;
     98 
     99         ++parsedParams;
    100 
    101         if (skipOptionalSVGSpaces(ptr, end) && *ptr == ',') {
    102             ++ptr;
    103             skipOptionalSVGSpaces(ptr, end);
    104 
    105             trailingDelimiter = true;
    106         } else {
    107             trailingDelimiter = false;
    108         }
    109     }
    110 
    111     if (trailingDelimiter || !(parsedParams == required || parsedParams == maxPossibleParams))
    112         return -1;
    113 
    114     return parsedParams;
    115 }
    116 
    117 // These should be kept in sync with enum SVGTransformType
    118 static const int requiredValuesForType[] =  {0, 6, 1, 1, 1, 1, 1};
    119 static const int optionalValuesForType[] =  {0, 0, 1, 1, 2, 0, 0};
    120 
    121 template<typename CharType>
    122 PassRefPtr<SVGTransform> parseTransformOfType(unsigned type, const CharType*& ptr, const CharType* end)
    123 {
    124     if (type == SVG_TRANSFORM_UNKNOWN)
    125         return nullptr;
    126 
    127     int valueCount = 0;
    128     float values[] = {0, 0, 0, 0, 0, 0};
    129     if ((valueCount = parseTransformParamList(ptr, end, values, requiredValuesForType[type], optionalValuesForType[type])) < 0) {
    130         return nullptr;
    131     }
    132 
    133     RefPtr<SVGTransform> transform = SVGTransform::create();
    134 
    135     switch (type) {
    136     case SVG_TRANSFORM_SKEWX:
    137         transform->setSkewX(values[0]);
    138         break;
    139     case SVG_TRANSFORM_SKEWY:
    140         transform->setSkewY(values[0]);
    141         break;
    142     case SVG_TRANSFORM_SCALE:
    143         if (valueCount == 1) // Spec: if only one param given, assume uniform scaling
    144             transform->setScale(values[0], values[0]);
    145         else
    146             transform->setScale(values[0], values[1]);
    147         break;
    148     case SVG_TRANSFORM_TRANSLATE:
    149         if (valueCount == 1) // Spec: if only one param given, assume 2nd param to be 0
    150             transform->setTranslate(values[0], 0);
    151         else
    152             transform->setTranslate(values[0], values[1]);
    153         break;
    154     case SVG_TRANSFORM_ROTATE:
    155         if (valueCount == 1)
    156             transform->setRotate(values[0], 0, 0);
    157         else
    158             transform->setRotate(values[0], values[1], values[2]);
    159         break;
    160     case SVG_TRANSFORM_MATRIX:
    161         transform->setMatrix(AffineTransform(values[0], values[1], values[2], values[3], values[4], values[5]));
    162         break;
    163     }
    164 
    165     return transform.release();
    166 }
    167 
    168 }
    169 
    170 template<typename CharType>
    171 bool SVGTransformList::parseInternal(const CharType*& ptr, const CharType* end)
    172 {
    173     clear();
    174 
    175     bool delimParsed = false;
    176     while (ptr < end) {
    177         delimParsed = false;
    178         SVGTransformType transformType = SVG_TRANSFORM_UNKNOWN;
    179         skipOptionalSVGSpaces(ptr, end);
    180 
    181         if (!parseAndSkipTransformType(ptr, end, transformType))
    182             return false;
    183 
    184         if (!skipOptionalSVGSpaces(ptr, end) || *ptr != '(')
    185             return false;
    186         ptr++;
    187 
    188         RefPtr<SVGTransform> transform = parseTransformOfType(transformType, ptr, end);
    189         if (!transform)
    190             return false;
    191 
    192         if (!skipOptionalSVGSpaces(ptr, end) || *ptr != ')')
    193             return false;
    194         ptr++;
    195 
    196         append(transform.release());
    197 
    198         skipOptionalSVGSpaces(ptr, end);
    199         if (ptr < end && *ptr == ',') {
    200             delimParsed = true;
    201             ++ptr;
    202             skipOptionalSVGSpaces(ptr, end);
    203         }
    204     }
    205 
    206     return !delimParsed;
    207 }
    208 
    209 bool SVGTransformList::parse(const UChar*& ptr, const UChar* end)
    210 {
    211     return parseInternal(ptr, end);
    212 }
    213 
    214 bool SVGTransformList::parse(const LChar*& ptr, const LChar* end)
    215 {
    216     return parseInternal(ptr, end);
    217 }
    218 
    219 String SVGTransformList::valueAsString() const
    220 {
    221     StringBuilder builder;
    222 
    223     ConstIterator it = begin();
    224     ConstIterator itEnd = end();
    225     while (it != itEnd) {
    226         builder.append(it->valueAsString());
    227         ++it;
    228         if (it != itEnd)
    229             builder.append(' ');
    230     }
    231 
    232     return builder.toString();
    233 }
    234 
    235 void SVGTransformList::setValueAsString(const String& value, ExceptionState& exceptionState)
    236 {
    237     if (value.isEmpty()) {
    238         clear();
    239         return;
    240     }
    241 
    242     bool valid = false;
    243     if (value.is8Bit()) {
    244         const LChar* ptr = value.characters8();
    245         const LChar* end = ptr + value.length();
    246         valid = parse(ptr, end);
    247     } else {
    248         const UChar* ptr = value.characters16();
    249         const UChar* end = ptr + value.length();
    250         valid = parse(ptr, end);
    251     }
    252 
    253     if (!valid) {
    254         clear();
    255         exceptionState.throwDOMException(SyntaxError, "Problem parsing transform list=\""+value+"\"");
    256     }
    257 }
    258 
    259 PassRefPtr<SVGPropertyBase> SVGTransformList::cloneForAnimation(const String& value) const
    260 {
    261     ASSERT_NOT_REACHED();
    262     return nullptr;
    263 }
    264 
    265 PassRefPtr<SVGTransformList> SVGTransformList::create(SVGTransformType transformType, const String& value)
    266 {
    267     RefPtr<SVGTransform> transform;
    268     if (value.isEmpty()) {
    269     } else if (value.is8Bit()) {
    270         const LChar* ptr = value.characters8();
    271         const LChar* end = ptr + value.length();
    272         transform = parseTransformOfType(transformType, ptr, end);
    273     } else {
    274         const UChar* ptr = value.characters16();
    275         const UChar* end = ptr + value.length();
    276         transform = parseTransformOfType(transformType, ptr, end);
    277     }
    278 
    279     RefPtr<SVGTransformList> svgTransformList = SVGTransformList::create();
    280     if (transform)
    281         svgTransformList->append(transform);
    282     return svgTransformList.release();
    283 }
    284 
    285 void SVGTransformList::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement* contextElement)
    286 {
    287     if (isEmpty())
    288         return;
    289 
    290     RefPtr<SVGTransformList> otherList = toSVGTransformList(other);
    291     if (length() != otherList->length())
    292         return;
    293 
    294     ASSERT(length() == 1);
    295     RefPtr<SVGTransform> fromTransform = at(0);
    296     RefPtr<SVGTransform> toTransform = otherList->at(0);
    297 
    298     ASSERT(fromTransform->transformType() == toTransform->transformType());
    299     clear();
    300     append(SVGTransformDistance::addSVGTransforms(fromTransform, toTransform));
    301 }
    302 
    303 void SVGTransformList::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> fromValue, PassRefPtr<SVGPropertyBase> toValue, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
    304 {
    305     ASSERT(animationElement);
    306     bool isToAnimation = animationElement->animationMode() == ToAnimation;
    307 
    308     // Spec: To animations provide specific functionality to get a smooth change from the underlying value to the
    309     // to attribute value, which conflicts mathematically with the requirement for additive transform animations
    310     // to be post-multiplied. As a consequence, in SVG 1.1 the behavior of to animations for animateTransform is undefined
    311     // FIXME: This is not taken into account yet.
    312     RefPtr<SVGTransformList> fromList = isToAnimation ? this : toSVGTransformList(fromValue);
    313     RefPtr<SVGTransformList> toList = toSVGTransformList(toValue);
    314     RefPtr<SVGTransformList> toAtEndOfDurationList = toSVGTransformList(toAtEndOfDurationValue);
    315 
    316     size_t toListSize = toList->length();
    317     if (!toListSize)
    318         return;
    319 
    320     // Get a reference to the from value before potentially cleaning it out (in the case of a To animation.)
    321     RefPtr<SVGTransform> toTransform = toList->at(0);
    322     RefPtr<SVGTransform> effectiveFrom;
    323     // If there's an existing 'from'/underlying value of the same type use that, else use a "zero transform".
    324     if (fromList->length() && fromList->at(0)->transformType() == toTransform->transformType())
    325         effectiveFrom = fromList->at(0);
    326     else
    327         effectiveFrom = SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform);
    328 
    329     // Never resize the animatedTransformList to the toList size, instead either clear the list or append to it.
    330     if (!isEmpty() && (!animationElement->isAdditive() || isToAnimation))
    331         clear();
    332 
    333     RefPtr<SVGTransform> currentTransform = SVGTransformDistance(effectiveFrom, toTransform).scaledDistance(percentage).addToSVGTransform(effectiveFrom);
    334     if (animationElement->isAccumulated() && repeatCount) {
    335         RefPtr<SVGTransform> effectiveToAtEnd = !toAtEndOfDurationList->isEmpty() ? toAtEndOfDurationList->at(0) : SVGTransform::create(toTransform->transformType(), SVGTransform::ConstructZeroTransform);
    336         append(SVGTransformDistance::addSVGTransforms(currentTransform, effectiveToAtEnd, repeatCount));
    337     } else {
    338         append(currentTransform);
    339     }
    340 }
    341 
    342 float SVGTransformList::calculateDistance(PassRefPtr<SVGPropertyBase> toValue, SVGElement*)
    343 {
    344     // FIXME: This is not correct in all cases. The spec demands that each component (translate x and y for example)
    345     // is paced separately. To implement this we need to treat each component as individual animation everywhere.
    346 
    347     RefPtr<SVGTransformList> toList = toSVGTransformList(toValue);
    348     if (isEmpty() || length() != toList->length())
    349         return -1;
    350 
    351     ASSERT(length() == 1);
    352     if (at(0)->transformType() == toList->at(0)->transformType())
    353         return -1;
    354 
    355     // Spec: http://www.w3.org/TR/SVG/animate.html#complexDistances
    356     // Paced animations assume a notion of distance between the various animation values defined by the to, from, by and values attributes.
    357     // Distance is defined only for scalar types (such as <length>), colors and the subset of transformation types that are supported by animateTransform.
    358     return SVGTransformDistance(at(0), toList->at(0)).distance();
    359 }
    360 
    361 }
    362