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