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 "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
DEFINE_ANIMATED_LENGTH(SVGRadialGradientElement,SVGNames::cxAttr,Cx,cx)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
create(const QualifiedName & tagName,Document * document)60 PassRefPtr<SVGRadialGradientElement> SVGRadialGradientElement::create(const QualifiedName& tagName, Document* document)
61 {
62 return adoptRef(new SVGRadialGradientElement(tagName, document));
63 }
64
parseMappedAttribute(Attribute * attr)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
svgAttributeChanged(const QualifiedName & attrName)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
synchronizeProperty(const QualifiedName & attrName)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
attributeToPropertyTypeMap()127 AttributeToPropertyTypeMap& SVGRadialGradientElement::attributeToPropertyTypeMap()
128 {
129 DEFINE_STATIC_LOCAL(AttributeToPropertyTypeMap, s_attributeToPropertyTypeMap, ());
130 return s_attributeToPropertyTypeMap;
131 }
132
fillAttributeToPropertyTypeMap()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
createRenderer(RenderArena * arena,RenderStyle *)145 RenderObject* SVGRadialGradientElement::createRenderer(RenderArena* arena, RenderStyle*)
146 {
147 return new (arena) RenderSVGResourceRadialGradient(this);
148 }
149
collectGradientAttributes(RadialGradientAttributes & attributes)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
calculateFocalCenterPointsAndRadius(const RadialGradientAttributes & attributes,FloatPoint & focalPoint,FloatPoint & centerPoint,float & radius)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
selfHasRelativeLengths() const251 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