WebCore/svg/SVGRadialGradientElement.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2     Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
       
     3                   2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
       
     4                   2008 Eric Seidel <eric@webkit.org>
       
     5                   2008 Dirk Schulze <krit@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 "SVGLength.h"
       
    35 #include "SVGNames.h"
       
    36 #include "SVGStopElement.h"
       
    37 #include "SVGTransform.h"
       
    38 #include "SVGTransformList.h"
       
    39 #include "SVGUnitTypes.h"
       
    40 
       
    41 namespace WebCore {
       
    42 
       
    43 SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* doc)
       
    44     : SVGGradientElement(tagName, doc)
       
    45     , m_cx(LengthModeWidth, "50%")
       
    46     , m_cy(LengthModeHeight, "50%")
       
    47     , m_r(LengthModeOther, "50%")
       
    48     , m_fx(LengthModeWidth)
       
    49     , m_fy(LengthModeHeight)
       
    50 {
       
    51     // Spec: If the cx/cy/r attribute is not specified, the effect is as if a value of "50%" were specified.
       
    52 }
       
    53 
       
    54 SVGRadialGradientElement::~SVGRadialGradientElement()
       
    55 {
       
    56 }
       
    57 
       
    58 void SVGRadialGradientElement::parseMappedAttribute(Attribute* attr)
       
    59 {
       
    60     if (attr->name() == SVGNames::cxAttr)
       
    61         setCxBaseValue(SVGLength(LengthModeWidth, attr->value()));
       
    62     else if (attr->name() == SVGNames::cyAttr)
       
    63         setCyBaseValue(SVGLength(LengthModeHeight, attr->value()));
       
    64     else if (attr->name() == SVGNames::rAttr) {
       
    65         setRBaseValue(SVGLength(LengthModeOther, attr->value()));
       
    66         if (rBaseValue().value(this) < 0.0)
       
    67             document()->accessSVGExtensions()->reportError("A negative value for radial gradient radius <r> is not allowed");
       
    68     } else if (attr->name() == SVGNames::fxAttr)
       
    69         setFxBaseValue(SVGLength(LengthModeWidth, attr->value()));
       
    70     else if (attr->name() == SVGNames::fyAttr)
       
    71         setFyBaseValue(SVGLength(LengthModeHeight, attr->value()));
       
    72     else
       
    73         SVGGradientElement::parseMappedAttribute(attr);
       
    74 }
       
    75 
       
    76 void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName& attrName)
       
    77 {
       
    78     SVGGradientElement::svgAttributeChanged(attrName);
       
    79 
       
    80     if (attrName == SVGNames::cxAttr
       
    81         || attrName == SVGNames::cyAttr
       
    82         || attrName == SVGNames::fxAttr
       
    83         || attrName == SVGNames::fyAttr
       
    84         || attrName == SVGNames::rAttr) {
       
    85         updateRelativeLengthsInformation();
       
    86         invalidateResourceClients();
       
    87     }
       
    88 }
       
    89 
       
    90 void SVGRadialGradientElement::synchronizeProperty(const QualifiedName& attrName)
       
    91 {
       
    92     SVGGradientElement::synchronizeProperty(attrName);
       
    93 
       
    94     if (attrName == anyQName()) {
       
    95         synchronizeCx();
       
    96         synchronizeCy();
       
    97         synchronizeFx();
       
    98         synchronizeFy();
       
    99         synchronizeR();
       
   100         return;
       
   101     }
       
   102 
       
   103     if (attrName == SVGNames::cxAttr)
       
   104         synchronizeCx();
       
   105     else if (attrName == SVGNames::cyAttr)
       
   106         synchronizeCy();
       
   107     else if (attrName == SVGNames::fxAttr)
       
   108         synchronizeFx();
       
   109     else if (attrName == SVGNames::fyAttr)
       
   110         synchronizeFy();
       
   111     else if (attrName == SVGNames::rAttr)
       
   112         synchronizeR();
       
   113 }
       
   114 
       
   115 RenderObject* SVGRadialGradientElement::createRenderer(RenderArena* arena, RenderStyle*)
       
   116 {
       
   117     return new (arena) RenderSVGResourceRadialGradient(this);
       
   118 }
       
   119 
       
   120 RadialGradientAttributes SVGRadialGradientElement::collectGradientProperties()
       
   121 {
       
   122     RadialGradientAttributes attributes;
       
   123     HashSet<SVGGradientElement*> processedGradients;
       
   124 
       
   125     bool isRadial = true;
       
   126     SVGGradientElement* current = this;
       
   127 
       
   128     while (current) {
       
   129         if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr))
       
   130             attributes.setSpreadMethod((GradientSpreadMethod) current->spreadMethod());
       
   131 
       
   132         if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr))
       
   133             attributes.setBoundingBoxMode(current->gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
       
   134 
       
   135         if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr))
       
   136             attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix());
       
   137 
       
   138         if (!attributes.hasStops()) {
       
   139             const Vector<Gradient::ColorStop>& stops(current->buildStops());
       
   140             if (!stops.isEmpty())
       
   141                 attributes.setStops(stops);
       
   142         }
       
   143 
       
   144         if (isRadial) {
       
   145             SVGRadialGradientElement* radial = static_cast<SVGRadialGradientElement*>(current);
       
   146 
       
   147             if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr))
       
   148                 attributes.setCx(radial->cx());
       
   149 
       
   150             if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
       
   151                 attributes.setCy(radial->cy());
       
   152 
       
   153             if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
       
   154                 attributes.setR(radial->r());
       
   155 
       
   156             if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
       
   157                 attributes.setFx(radial->fx());
       
   158 
       
   159             if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
       
   160                 attributes.setFy(radial->fy());
       
   161         }
       
   162 
       
   163         processedGradients.add(current);
       
   164 
       
   165         // Respect xlink:href, take attributes from referenced element
       
   166         Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
       
   167         if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
       
   168             current = static_cast<SVGGradientElement*>(refNode);
       
   169 
       
   170             // Cycle detection
       
   171             if (processedGradients.contains(current))
       
   172                 return RadialGradientAttributes();
       
   173 
       
   174             isRadial = current->hasTagName(SVGNames::radialGradientTag);
       
   175         } else
       
   176             current = 0;
       
   177     }
       
   178 
       
   179     // Handle default values for fx/fy
       
   180     if (!attributes.hasFx())
       
   181         attributes.setFx(attributes.cx());
       
   182 
       
   183     if (!attributes.hasFy())
       
   184         attributes.setFy(attributes.cy());
       
   185 
       
   186     return attributes;
       
   187 }
       
   188 
       
   189 void SVGRadialGradientElement::calculateFocalCenterPointsAndRadius(const RadialGradientAttributes& attributes, FloatPoint& focalPoint, FloatPoint& centerPoint, float& radius)
       
   190 {
       
   191     // Determine gradient focal/center points and radius
       
   192     if (attributes.boundingBoxMode()) {
       
   193         focalPoint = FloatPoint(attributes.fx().valueAsPercentage(), attributes.fy().valueAsPercentage());
       
   194         centerPoint = FloatPoint(attributes.cx().valueAsPercentage(), attributes.cy().valueAsPercentage());
       
   195         radius = attributes.r().valueAsPercentage();
       
   196     } else {
       
   197         focalPoint = FloatPoint(attributes.fx().value(this), attributes.fy().value(this));
       
   198         centerPoint = FloatPoint(attributes.cx().value(this), attributes.cy().value(this));
       
   199         radius = attributes.r().value(this);
       
   200     }
       
   201 
       
   202     // Eventually adjust focal points, as described below
       
   203     float deltaX = focalPoint.x() - centerPoint.x();
       
   204     float deltaY = focalPoint.y() - centerPoint.y();
       
   205     float radiusMax = 0.99f * radius;
       
   206 
       
   207     // Spec: If (fx, fy) lies outside the circle defined by (cx, cy) and r, set
       
   208     // (fx, fy) to the point of intersection of the line through (fx, fy) and the circle.
       
   209     // We scale the radius by 0.99 to match the behavior of FireFox.
       
   210     if (sqrt(deltaX * deltaX + deltaY * deltaY) > radiusMax) {
       
   211         float angle = atan2f(deltaY, deltaX);
       
   212 
       
   213         deltaX = cosf(angle) * radiusMax;
       
   214         deltaY = sinf(angle) * radiusMax;
       
   215         focalPoint = FloatPoint(deltaX + centerPoint.x(), deltaY + centerPoint.y());
       
   216     }
       
   217 }
       
   218 
       
   219 bool SVGRadialGradientElement::selfHasRelativeLengths() const
       
   220 {
       
   221     return cy().isRelative()
       
   222         || cy().isRelative()
       
   223         || r().isRelative()
       
   224         || fx().isRelative()
       
   225         || fy().isRelative();
       
   226 }
       
   227 
       
   228 }
       
   229 
       
   230 #endif // ENABLE(SVG)