2011-11-10 Nikolas Zimmermann <nzimmermann@rim.com>
[WebKit-https.git] / Source / WebCore / svg / SVGRadialGradientElement.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3  * Copyright (C) 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4  * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
5  * Copyright (C) 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 "SVGElementInstance.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 // Animated property definitions
44 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cxAttr, Cx, cx)
45 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::cyAttr, Cy, cy)
46 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::rAttr, R, r)
47 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fxAttr, Fx, fx)
48 DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement, SVGNames::fyAttr, Fy, fy)
49
50 BEGIN_REGISTER_ANIMATED_PROPERTIES(SVGRadialGradientElement)
51     REGISTER_LOCAL_ANIMATED_PROPERTY(cx)
52     REGISTER_LOCAL_ANIMATED_PROPERTY(cy)
53     REGISTER_LOCAL_ANIMATED_PROPERTY(r)
54     REGISTER_LOCAL_ANIMATED_PROPERTY(fx)
55     REGISTER_LOCAL_ANIMATED_PROPERTY(fy)
56     REGISTER_PARENT_ANIMATED_PROPERTIES(SVGGradientElement)
57 END_REGISTER_ANIMATED_PROPERTIES
58
59 inline SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName& tagName, Document* document)
60     : SVGGradientElement(tagName, document)
61     , m_cx(LengthModeWidth, "50%")
62     , m_cy(LengthModeHeight, "50%")
63     , m_r(LengthModeOther, "50%")
64     , m_fx(LengthModeWidth)
65     , m_fy(LengthModeHeight)
66 {
67     // Spec: If the cx/cy/r attribute is not specified, the effect is as if a value of "50%" were specified.
68     ASSERT(hasTagName(SVGNames::radialGradientTag));
69     registerAnimatedPropertiesForSVGRadialGradientElement();
70 }
71
72 PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document)
73 {
74     return adoptRef(new SVGRadialGradientElement(tagName, document));
75 }
76
77 bool SVGRadialGradientElement::isSupportedAttribute(const QualifiedName& attrName)
78 {
79     DEFINE_STATIC_LOCAL(HashSet<QualifiedName>, supportedAttributes, ());
80     if (supportedAttributes.isEmpty()) {
81         supportedAttributes.add(SVGNames::cxAttr);
82         supportedAttributes.add(SVGNames::cyAttr);
83         supportedAttributes.add(SVGNames::fxAttr);
84         supportedAttributes.add(SVGNames::fyAttr);
85         supportedAttributes.add(SVGNames::rAttr);
86     }
87     return supportedAttributes.contains<QualifiedName, SVGAttributeHashTranslator>(attrName);
88 }
89
90 void SVGRadialGradientElement::parseMappedAttribute(Attribute* attr)
91 {
92     SVGParsingError parseError = NoError;
93
94     if (!isSupportedAttribute(attr->name()))
95         SVGGradientElement::parseMappedAttribute(attr);
96     else if (attr->name() == SVGNames::cxAttr)
97         setCxBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError));
98     else if (attr->name() == SVGNames::cyAttr)
99         setCyBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError));
100     else if (attr->name() == SVGNames::rAttr)
101         setRBaseValue(SVGLength::construct(LengthModeOther, attr->value(), parseError, ForbidNegativeLengths));
102     else if (attr->name() == SVGNames::fxAttr)
103         setFxBaseValue(SVGLength::construct(LengthModeWidth, attr->value(), parseError));
104     else if (attr->name() == SVGNames::fyAttr)
105         setFyBaseValue(SVGLength::construct(LengthModeHeight, attr->value(), parseError));
106     else
107         ASSERT_NOT_REACHED();
108
109     reportAttributeParsingError(parseError, attr);
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(true);
125 }
126
127 RenderObject* SVGRadialGradientElement::createRenderer(RenderArena* arena, RenderStyle*)
128 {
129     return new (arena) 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->spreadMethod());
145
146         if (!attributes.hasGradientUnits() && current->hasAttribute(SVGNames::gradientUnitsAttr))
147             attributes.setGradientUnits(current->gradientUnits());
148
149         if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
150             AffineTransform transform;
151             current->gradientTransform().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->cx());
166
167             if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr))
168                 attributes.setCy(radial->cy());
169
170             if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr))
171                 attributes.setR(radial->r());
172
173             if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr))
174                 attributes.setFx(radial->fx());
175
176             if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr))
177                 attributes.setFy(radial->fy());
178         }
179
180         processedGradients.add(current);
181
182         // Respect xlink:href, take attributes from referenced element
183         Node* refNode = SVGURIReference::targetElementFromIRIString(current->href(), document());
184         if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
185             current = static_cast<SVGGradientElement*>(refNode);
186
187             // Cycle detection
188             if (processedGradients.contains(current)) {
189                 current = 0;
190                 break;
191             }
192
193             isRadial = current->hasTagName(SVGNames::radialGradientTag);
194         } else
195             current = 0;
196     }
197
198     // Handle default values for fx/fy
199     if (!attributes.hasFx())
200         attributes.setFx(attributes.cx());
201
202     if (!attributes.hasFy())
203         attributes.setFy(attributes.cy());
204
205     return true;
206 }
207
208 bool SVGRadialGradientElement::selfHasRelativeLengths() const
209 {
210     return cx().isRelative()
211         || cy().isRelative()
212         || r().isRelative()
213         || fx().isRelative()
214         || fy().isRelative();
215 }
216
217 }
218
219 #endif // ENABLE(SVG)