SVGLinearGradientElement.cpp revision 635860845790a19bf50bbc51ba8fb66a96dde068
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 7 This library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Library General Public 9 License as published by the Free Software Foundation; either 10 version 2 of the License, or (at your option) any later version. 11 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Library General Public License for more details. 16 17 You should have received a copy of the GNU Library General Public License 18 along with this library; see the file COPYING.LIB. If not, write to 19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 Boston, MA 02110-1301, USA. 21*/ 22 23#include "config.h" 24 25#if ENABLE(SVG) 26#include "SVGLinearGradientElement.h" 27 28#include "Document.h" 29#include "FloatPoint.h" 30#include "LinearGradientAttributes.h" 31#include "SVGLength.h" 32#include "SVGNames.h" 33#include "SVGPaintServerLinearGradient.h" 34#include "SVGTransform.h" 35#include "SVGTransformList.h" 36#include "SVGUnitTypes.h" 37 38namespace WebCore { 39 40SVGLinearGradientElement::SVGLinearGradientElement(const QualifiedName& tagName, Document* doc) 41 : SVGGradientElement(tagName, doc) 42 , m_x1(this, SVGNames::x1Attr, LengthModeWidth) 43 , m_y1(this, SVGNames::y1Attr, LengthModeHeight) 44 , m_x2(this, SVGNames::x2Attr, LengthModeWidth, "100%") 45 , m_y2(this, SVGNames::y2Attr, LengthModeHeight) 46{ 47 // Spec: If the x2 attribute is not specified, the effect is as if a value of "100%" were specified. 48} 49 50SVGLinearGradientElement::~SVGLinearGradientElement() 51{ 52} 53 54void SVGLinearGradientElement::parseMappedAttribute(MappedAttribute* attr) 55{ 56 if (attr->name() == SVGNames::x1Attr) 57 setX1BaseValue(SVGLength(LengthModeWidth, attr->value())); 58 else if (attr->name() == SVGNames::y1Attr) 59 setY1BaseValue(SVGLength(LengthModeHeight, attr->value())); 60 else if (attr->name() == SVGNames::x2Attr) 61 setX2BaseValue(SVGLength(LengthModeWidth, attr->value())); 62 else if (attr->name() == SVGNames::y2Attr) 63 setY2BaseValue(SVGLength(LengthModeHeight, attr->value())); 64 else 65 SVGGradientElement::parseMappedAttribute(attr); 66} 67 68void SVGLinearGradientElement::svgAttributeChanged(const QualifiedName& attrName) 69{ 70 SVGGradientElement::svgAttributeChanged(attrName); 71 72 if (!m_resource) 73 return; 74 75 if (attrName == SVGNames::x1Attr || attrName == SVGNames::y1Attr || 76 attrName == SVGNames::x2Attr || attrName == SVGNames::y2Attr) 77 m_resource->invalidate(); 78} 79 80void SVGLinearGradientElement::buildGradient() const 81{ 82 LinearGradientAttributes attributes = collectGradientProperties(); 83 84 RefPtr<SVGPaintServerLinearGradient> linearGradient = WTF::static_pointer_cast<SVGPaintServerLinearGradient>(m_resource); 85 86 FloatPoint startPoint = FloatPoint::narrowPrecision(attributes.x1(), attributes.y1()); 87 FloatPoint endPoint = FloatPoint::narrowPrecision(attributes.x2(), attributes.y2()); 88 89 RefPtr<Gradient> gradient = Gradient::create(startPoint, endPoint); 90 91 Vector<SVGGradientStop> m_stops = attributes.stops(); 92 float previousOffset = 0.0f; 93 for (unsigned i = 0; i < m_stops.size(); ++i) { 94 float offset = std::min(std::max(previousOffset, m_stops[i].first), 1.0f); 95 previousOffset = offset; 96 gradient->addColorStop(offset, m_stops[i].second); 97 } 98 99 linearGradient->setGradient(gradient); 100 101 if (attributes.stops().isEmpty()) 102 return; 103 104 // This code should go away. PaintServers should go away too. 105 // Only this code should care about bounding boxes 106 linearGradient->setBoundingBoxMode(attributes.boundingBoxMode()); 107 linearGradient->setGradientStops(attributes.stops()); 108 109 // These should possibly be supported on Gradient 110 linearGradient->setGradientSpreadMethod(attributes.spreadMethod()); 111 linearGradient->setGradientTransform(attributes.gradientTransform()); 112 linearGradient->setGradientStart(startPoint); 113 linearGradient->setGradientEnd(endPoint); 114} 115 116LinearGradientAttributes SVGLinearGradientElement::collectGradientProperties() const 117{ 118 LinearGradientAttributes attributes; 119 HashSet<const SVGGradientElement*> processedGradients; 120 121 bool isLinear = true; 122 const SVGGradientElement* current = this; 123 124 while (current) { 125 if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr)) 126 attributes.setSpreadMethod((GradientSpreadMethod) current->spreadMethod()); 127 128 if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr)) 129 attributes.setBoundingBoxMode(current->gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 130 131 if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) 132 attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix()); 133 134 if (!attributes.hasStops()) { 135 const Vector<SVGGradientStop>& stops(current->buildStops()); 136 if (!stops.isEmpty()) 137 attributes.setStops(stops); 138 } 139 140 if (isLinear) { 141 const SVGLinearGradientElement* linear = static_cast<const SVGLinearGradientElement*>(current); 142 143 if (!attributes.hasX1() && current->hasAttribute(SVGNames::x1Attr)) 144 attributes.setX1(linear->x1().valueAsPercentage()); 145 146 if (!attributes.hasY1() && current->hasAttribute(SVGNames::y1Attr)) 147 attributes.setY1(linear->y1().valueAsPercentage()); 148 149 if (!attributes.hasX2() && current->hasAttribute(SVGNames::x2Attr)) 150 attributes.setX2(linear->x2().valueAsPercentage()); 151 152 if (!attributes.hasY2() && current->hasAttribute(SVGNames::y2Attr)) 153 attributes.setY2(linear->y2().valueAsPercentage()); 154 } 155 156 processedGradients.add(current); 157 158 // Respect xlink:href, take attributes from referenced element 159 Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href())); 160 if (refNode && (refNode->hasTagName(SVGNames::linearGradientTag) || refNode->hasTagName(SVGNames::radialGradientTag))) { 161 current = static_cast<const SVGGradientElement*>(const_cast<const Node*>(refNode)); 162 163 // Cycle detection 164 if (processedGradients.contains(current)) 165 return LinearGradientAttributes(); 166 167 isLinear = current->gradientType() == LinearGradientPaintServer; 168 } else 169 current = 0; 170 } 171 172 return attributes; 173} 174 175} 176 177#endif // ENABLE(SVG) 178