Home | History | Annotate | Download | only in svg
      1 /*
      2  * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org>
      4  * Copyright (C) 2008 Eric Seidel <eric (at) webkit.org>
      5  * Copyright (C) 2008 Dirk Schulze <krit (at) webkit.org>
      6  * Copyright (C) Research In Motion Limited 2010. 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/SVGRadialGradientElement.h"
     27 
     28 #include "SVGNames.h"
     29 #include "core/rendering/svg/RenderSVGResourceRadialGradient.h"
     30 #include "core/svg/RadialGradientAttributes.h"
     31 #include "core/svg/SVGElementInstance.h"
     32 #include "core/svg/SVGTransformList.h"
     33 
     34 namespace WebCore {
     35 
     36 // Animated property definitions
     37 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx)
     38 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy)
     39 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r)
     40 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx)
     41 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy)
     42 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::frAttr, Fr, fr)
     43 
     44 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGRadialGradientElement)
     45     REGISTER_LOCAL_ANIMATED_PROPERTY(cx)
     46     REGISTER_LOCAL_ANIMATED_PROPERTY(cy)
     47     REGISTER_LOCAL_ANIMATED_PROPERTY(r)
     48     REGISTER_LOCAL_ANIMATED_PROPERTY(fx)
     49     REGISTER_LOCAL_ANIMATED_PROPERTY(fy)
     50     REGISTER_LOCAL_ANIMATED_PROPERTY(fr)
     51     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement)
     52 END_REGISTER_ANIMATED_PROPERTIES
     53 
     54 inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* document)
     55     : SVGGradientElement(tagName, document)
     56     , m_cx(LengthModeWidth, "50%")
     57     , m_cy(LengthModeHeight, "50%")
     58     , m_r(LengthModeOther, "50%")
     59     , m_fx(LengthModeWidth)
     60     , m_fy(LengthModeHeight)
     61     , m_fr(LengthModeOther, "0%")
     62 {
     63     // Spec: If the cx/cy/r/fr attribute is not specified, the effect is as if a value of "50%" were specified.
     64     ASSERT(hasTagName(SVGNames::radialGradientTag));
     65     ScriptWrappable::init(this);
     66     registerAnimatedPropertiesForSVGRadialGradientElement();
     67 }
     68 
     69 PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document)
     70 {
     71     return adoptRef(new SVGRadialGradientElement(tagName, document));
     72 }
     73 
     74 bool SVGRadialGradientElement::isSupportedAttribute(const QualifiedName& attrName)
     75 {
     76     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
     77     if (supportedAttributes.isEmpty()) {
     78         supportedAttributes.add(SVGNames::cxAttr);
     79         supportedAttributes.add(SVGNames::cyAttr);
     80         supportedAttributes.add(SVGNames::fxAttr);
     81         supportedAttributes.add(SVGNames::fyAttr);
     82         supportedAttributes.add(SVGNames::rAttr);
     83         supportedAttributes.add(SVGNames::frAttr);
     84     }
     85     return supportedAttributes.contains<SVGAttributeHashTranslator>(attrName);
     86 }
     87 
     88 void SVGRadialGradientElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
     89 {
     90     SVGParsingError parseError = NoError;
     91 
     92     if (!isSupportedAttribute(name))
     93         SVGGradientElement::parseAttribute(name, value);
     94     else if (name == SVGNames::cxAttr)
     95         setCxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
     96     else if (name == SVGNames::cyAttr)
     97         setCyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
     98     else if (name == SVGNames::rAttr)
     99         setRBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
    100     else if (name == SVGNames::fxAttr)
    101         setFxBaseValue(SVGLength::construct(LengthModeWidth, value, parseError));
    102     else if (name == SVGNames::fyAttr)
    103         setFyBaseValue(SVGLength::construct(LengthModeHeight, value, parseError));
    104     else if (name == SVGNames::frAttr)
    105         setFrBaseValue(SVGLength::construct(LengthModeOther, value, parseError, ForbidNegativeLengths));
    106     else
    107         ASSERT_NOT_REACHED();
    108 
    109     reportAttributeParsingError(parseError, name, value);
    110 }
    111 
    112 void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
    113 {
    114     if (!isSupportedAttribute(attrName)) {
    115         SVGGradientElement::svgAttributeChanged(attrName);
    116         return;
    117     }
    118 
    119     SVGElementInstance::InvalidationGuard invalidationGuard(this);
    120 
    121     updateRelativeLengthsInformation();
    122 
    123     if (RenderObject* object = renderer())
    124         object->setNeedsLayout();
    125 }
    126 
    127 RenderObject* SVGRadialGradientElement::createRenderer(RenderStyle*)
    128 {
    129     return new RenderSVGResourceRadialGradient(this);
    130 }
    131 
    132 bool SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
    133 {
    134     HashSet<SVGGradientElement*> processedGradients;
    135 
    136     bool isRadial = true;
    137     SVGGradientElement* current = this;
    138 
    139     while (current) {
    140         if (!current->renderer())
    141             return false;
    142 
    143         if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
    144             attributes.setSpreadMethod(current->spreadMethodCurrentValue());
    145 
    146         if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr))
    147             attributes.setGradientUnits(current->gradientUnitsCurrentValue());
    148 
    149         if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
    150             AffineTransform transform;
    151             current->gradientTransformCurrentValue().concatenate(transform);
    152             attributes.setGradientTransform(transform);
    153         }
    154 
    155         if (!attributes.hasStops()) {
    156             const Vector<Gradient::ColorStop>& stops(current->buildStops());
    157             if (!stops.isEmpty())
    158                 attributes.setStops(stops);
    159         }
    160 
    161         if (isRadial) {
    162             SVGRadialGradientElement* radial = static_cast<SVGRadialGradientElement*>(current);
    163 
    164             if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
    165                 attributes.setCx(radial->cxCurrentValue());
    166 
    167             if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
    168                 attributes.setCy(radial->cyCurrentValue());
    169 
    170             if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
    171                 attributes.setR(radial->rCurrentValue());
    172 
    173             if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
    174                 attributes.setFx(radial->fxCurrentValue());
    175 
    176             if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
    177                 attributes.setFy(radial->fyCurrentValue());
    178 
    179             if (!attributes.hasFr() && current->hasAttribute(SVGNames::frAttr))
    180                 attributes.setFr(radial->frCurrentValue());
    181         }
    182 
    183         processedGradients.add(current);
    184 
    185         // Respect xlink:href, take attributes from referenced element
    186         Node* refNode = SVGURIReference::targetElementFromIRIString(current->hrefCurrentValue(), document());
    187         if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
    188             current = toSVGGradientElement(refNode);
    189 
    190             // Cycle detection
    191             if (processedGradients.contains(current)) {
    192                 current = 0;
    193                 break;
    194             }
    195 
    196             isRadial = current->hasTagName(SVGNames::radialGradientTag);
    197         } else
    198             current = 0;
    199     }
    200 
    201     // Handle default values for fx/fy
    202     if (!attributes.hasFx())
    203         attributes.setFx(attributes.cx());
    204 
    205     if (!attributes.hasFy())
    206         attributes.setFy(attributes.cy());
    207     return true;
    208 }
    209 
    210 bool SVGRadialGradientElement::selfHasRelativeLengths() const
    211 {
    212     return cxCurrentValue().isRelative()
    213         || cyCurrentValue().isRelative()
    214         || rCurrentValue().isRelative()
    215         || fxCurrentValue().isRelative()
    216         || fyCurrentValue().isRelative()
    217         || frCurrentValue().isRelative();
    218 }
    219 
    220 }
    221