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 #if ENABLE(SVG)
     27 #include "SVGRadialGradientElement.h"
     28 
     29 #include "Attribute.h"
     30 #include "FloatConversion.h"
     31 #include "FloatPoint.h"
     32 #include "RadialGradientAttributes.h"
     33 #include "RenderSVGResourceRadialGradient.h"
     34 #include "SVGNames.h"
     35 #include "SVGStopElement.h"
     36 #include "SVGTransform.h"
     37 #include "SVGTransformList.h"
     38 #include "SVGUnitTypes.h"
     39 
     40 namespace WebCore {
     41 
     42 // Animated property definitions
     43 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx)
     44 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy)
     45 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r)
     46 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx)
     47 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy)
     48 
     49 inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* document)
     50     : SVGGradientElement(tagName, document)
     51     , m_cx(LengthModeWidth, "50%")
     52     , m_cy(LengthModeHeight, "50%")
     53     , m_r(LengthModeOther, "50%")
     54     , m_fx(LengthModeWidth)
     55     , m_fy(LengthModeHeight)
     56 {
     57     // Spec: If the cx/cy/r attribute is not specified, the effect is as if a value of "50%" were specified.
     58 }
     59 
     60 PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document)
     61 {
     62     return adoptRef(new SVGRadialGradientElement(tagName, document));
     63 }
     64 
     65 void SVGRadialGradientElement::parseMappedAttribute(Attribute* attr)
     66 {
     67     if (attr->name() == SVGNames::cxAttr)
     68         setCxBaseValue(SVGLength(LengthModeWidth, attr->value()));
     69     else if (attr->name() == SVGNames::cyAttr)
     70         setCyBaseValue(SVGLength(LengthModeHeight, attr->value()));
     71     else if (attr->name() == SVGNames::rAttr) {
     72         setRBaseValue(SVGLength(LengthModeOther, attr->value()));
     73         if (rBaseValue().value(this) < 0.0)
     74             document()->accessSVGExtensions()->reportError("A negative value for radial gradient radius <r> is not allowed");
     75     } else if (attr->name() == SVGNames::fxAttr)
     76         setFxBaseValue(SVGLength(LengthModeWidth, attr->value()));
     77     else if (attr->name() == SVGNames::fyAttr)
     78         setFyBaseValue(SVGLength(LengthModeHeight, attr->value()));
     79     else
     80         SVGGradientElement::parseMappedAttribute(attr);
     81 }
     82 
     83 void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
     84 {
     85     SVGGradientElement::svgAttributeChanged(attrName);
     86 
     87     if (attrName == SVGNames::cxAttr
     88         || attrName == SVGNames::cyAttr
     89         || attrName == SVGNames::fxAttr
     90         || attrName == SVGNames::fyAttr
     91         || attrName == SVGNames::rAttr) {
     92         updateRelativeLengthsInformation();
     93 
     94         RenderObject* object = renderer();
     95         if (!object)
     96             return;
     97 
     98         object->setNeedsLayout(true);
     99     }
    100 }
    101 
    102 void SVGRadialGradientElement::synchronizeProperty(const QualifiedName& attrName)
    103 {
    104     SVGGradientElement::synchronizeProperty(attrName);
    105 
    106     if (attrName == anyQName()) {
    107         synchronizeCx();
    108         synchronizeCy();
    109         synchronizeFx();
    110         synchronizeFy();
    111         synchronizeR();
    112         return;
    113     }
    114 
    115     if (attrName == SVGNames::cxAttr)
    116         synchronizeCx();
    117     else if (attrName == SVGNames::cyAttr)
    118         synchronizeCy();
    119     else if (attrName == SVGNames::fxAttr)
    120         synchronizeFx();
    121     else if (attrName == SVGNames::fyAttr)
    122         synchronizeFy();
    123     else if (attrName == SVGNames::rAttr)
    124         synchronizeR();
    125 }
    126 
    127 AttributeToPropertyTypeMap& SVGRadialGradientElement::attributeToPropertyTypeMap()
    128 {
    129     DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
    130     return s_attributeToPropertyTypeMap;
    131 }
    132 
    133 void SVGRadialGradientElement::fillAttributeToPropertyTypeMap()
    134 {
    135     AttributeToPropertyTypeMap& attributeToPropertyTypeMap = this->attributeToPropertyTypeMap();
    136 
    137     SVGGradientElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
    138     attributeToPropertyTypeMap.set(SVGNames::cxAttr, AnimatedLength);
    139     attributeToPropertyTypeMap.set(SVGNames::cyAttr, AnimatedLength);
    140     attributeToPropertyTypeMap.set(SVGNames::rAttr, AnimatedLength);
    141     attributeToPropertyTypeMap.set(SVGNames::fxAttr, AnimatedLength);
    142     attributeToPropertyTypeMap.set(SVGNames::fyAttr, AnimatedLength);
    143 }
    144 
    145 RenderObject* SVGRadialGradientElement::createRenderer(RenderArena* arena, RenderStyle*)
    146 {
    147     return new (arena) RenderSVGResourceRadialGradient(this);
    148 }
    149 
    150 void SVGRadialGradientElement::collectGradientAttributes(RadialGradientAttributes& attributes)
    151 {
    152     HashSet<SVGGradientElement*> processedGradients;
    153 
    154     bool isRadial = true;
    155     SVGGradientElement* current = this;
    156 
    157     while (current) {
    158         if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
    159             attributes.setSpreadMethod((GradientSpreadMethod) current->spreadMethod());
    160 
    161         if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr))
    162             attributes.setBoundingBoxMode(current->gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
    163 
    164         if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
    165             AffineTransform transform;
    166             current->gradientTransform().concatenate(transform);
    167             attributes.setGradientTransform(transform);
    168         }
    169 
    170         if (!attributes.hasStops()) {
    171             const Vector<Gradient::ColorStop>& stops(current->buildStops());
    172             if (!stops.isEmpty())
    173                 attributes.setStops(stops);
    174         }
    175 
    176         if (isRadial) {
    177             SVGRadialGradientElement* radial = static_cast<SVGRadialGradientElement*>(current);
    178 
    179             if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
    180                 attributes.setCx(radial->cx());
    181 
    182             if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
    183                 attributes.setCy(radial->cy());
    184 
    185             if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
    186                 attributes.setR(radial->r());
    187 
    188             if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
    189                 attributes.setFx(radial->fx());
    190 
    191             if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
    192                 attributes.setFy(radial->fy());
    193         }
    194 
    195         processedGradients.add(current);
    196 
    197         // Respect xlink:href, take attributes from referenced element
    198         Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
    199         if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
    200             current = static_cast<SVGGradientElement*>(refNode);
    201 
    202             // Cycle detection
    203             if (processedGradients.contains(current)) {
    204                 current = 0;
    205                 break;
    206             }
    207 
    208             isRadial = current->hasTagName(SVGNames::radialGradientTag);
    209         } else
    210             current = 0;
    211     }
    212 
    213     // Handle default values for fx/fy
    214     if (!attributes.hasFx())
    215         attributes.setFx(attributes.cx());
    216 
    217     if (!attributes.hasFy())
    218         attributes.setFy(attributes.cy());
    219 }
    220 
    221 void SVGRadialGradientElement::calculateFocalCenterPointsAndRadius(const RadialGradientAttributes& attributes, FloatPoint& focalPoint, FloatPoint& centerPoint, float& radius)
    222 {
    223     // Determine gradient focal/center points and radius
    224     if (attributes.boundingBoxMode()) {
    225         focalPoint = FloatPoint(attributes.fx().valueAsPercentage(), attributes.fy().valueAsPercentage());
    226         centerPoint = FloatPoint(attributes.cx().valueAsPercentage(), attributes.cy().valueAsPercentage());
    227         radius = attributes.r().valueAsPercentage();
    228     } else {
    229         focalPoint = FloatPoint(attributes.fx().value(this), attributes.fy().value(this));
    230         centerPoint = FloatPoint(attributes.cx().value(this), attributes.cy().value(this));
    231         radius = attributes.r().value(this);
    232     }
    233 
    234     // Eventually adjust focal points, as described below
    235     float deltaX = focalPoint.x() - centerPoint.x();
    236     float deltaY = focalPoint.y() - centerPoint.y();
    237     float radiusMax = 0.99f * radius;
    238 
    239     // Spec: If (fx, fy) lies outside the circle defined by (cx, cy) and r, set
    240     // (fx, fy) to the point of intersection of the line through (fx, fy) and the circle.
    241     // We scale the radius by 0.99 to match the behavior of FireFox.
    242     if (sqrt(deltaX * deltaX + deltaY * deltaY) > radiusMax) {
    243         float angle = atan2f(deltaY, deltaX);
    244 
    245         deltaX = cosf(angle) * radiusMax;
    246         deltaY = sinf(angle) * radiusMax;
    247         focalPoint = FloatPoint(deltaX + centerPoint.x(), deltaY + centerPoint.y());
    248     }
    249 }
    250 
    251 bool SVGRadialGradientElement::selfHasRelativeLengths() const
    252 {
    253     return cy().isRelative()
    254         || cy().isRelative()
    255         || r().isRelative()
    256         || fx().isRelative()
    257         || fy().isRelative();
    258 }
    259 
    260 }
    261 
    262 #endif // ENABLE(SVG)
    263