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