1 /* 2 Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann (at) kde.org> 3 2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org> 4 2008 Eric Seidel <eric (at) webkit.org> 5 2008 Dirk Schulze <krit (at) 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 "MappedAttribute.h" 32 #include "SVGLength.h" 33 #include "SVGNames.h" 34 #include "SVGPaintServerLinearGradient.h" 35 #include "SVGTransform.h" 36 #include "SVGTransformList.h" 37 #include "SVGUnitTypes.h" 38 39 namespace WebCore { 40 41 SVGLinearGradientElement::SVGLinearGradientElement(const QualifiedName& tagName, Document* doc) 42 : SVGGradientElement(tagName, doc) 43 , m_x1(LengthModeWidth) 44 , m_y1(LengthModeHeight) 45 , m_x2(LengthModeWidth, "100%") 46 , m_y2(LengthModeHeight) 47 { 48 // Spec: If the x2 attribute is not specified, the effect is as if a value of "100%" were specified. 49 } 50 51 SVGLinearGradientElement::~SVGLinearGradientElement() 52 { 53 } 54 55 void SVGLinearGradientElement::parseMappedAttribute(MappedAttribute* attr) 56 { 57 if (attr->name() == SVGNames::x1Attr) 58 setX1BaseValue(SVGLength(LengthModeWidth, attr->value())); 59 else if (attr->name() == SVGNames::y1Attr) 60 setY1BaseValue(SVGLength(LengthModeHeight, attr->value())); 61 else if (attr->name() == SVGNames::x2Attr) 62 setX2BaseValue(SVGLength(LengthModeWidth, attr->value())); 63 else if (attr->name() == SVGNames::y2Attr) 64 setY2BaseValue(SVGLength(LengthModeHeight, attr->value())); 65 else 66 SVGGradientElement::parseMappedAttribute(attr); 67 } 68 69 void SVGLinearGradientElement::svgAttributeChanged(const QualifiedName& attrName) 70 { 71 SVGGradientElement::svgAttributeChanged(attrName); 72 73 if (!m_resource) 74 return; 75 76 if (attrName == SVGNames::x1Attr || attrName == SVGNames::y1Attr || 77 attrName == SVGNames::x2Attr || attrName == SVGNames::y2Attr) 78 m_resource->invalidate(); 79 } 80 81 void SVGLinearGradientElement::synchronizeProperty(const QualifiedName& attrName) 82 { 83 SVGGradientElement::synchronizeProperty(attrName); 84 85 if (attrName == anyQName()) { 86 synchronizeX1(); 87 synchronizeY1(); 88 synchronizeX2(); 89 synchronizeY2(); 90 return; 91 } 92 93 if (attrName == SVGNames::x1Attr) 94 synchronizeX1(); 95 else if (attrName == SVGNames::y1Attr) 96 synchronizeY1(); 97 else if (attrName == SVGNames::x2Attr) 98 synchronizeX2(); 99 else if (attrName == SVGNames::y2Attr) 100 synchronizeY2(); 101 } 102 103 void SVGLinearGradientElement::buildGradient() const 104 { 105 LinearGradientAttributes attributes = collectGradientProperties(); 106 107 RefPtr<SVGPaintServerLinearGradient> linearGradient = WTF::static_pointer_cast<SVGPaintServerLinearGradient>(m_resource); 108 109 FloatPoint startPoint; 110 FloatPoint endPoint; 111 if (attributes.boundingBoxMode()) { 112 startPoint = FloatPoint(attributes.x1().valueAsPercentage(), attributes.y1().valueAsPercentage()); 113 endPoint = FloatPoint(attributes.x2().valueAsPercentage(), attributes.y2().valueAsPercentage()); 114 } else { 115 startPoint = FloatPoint(attributes.x1().value(this), attributes.y1().value(this)); 116 endPoint = FloatPoint(attributes.x2().value(this), attributes.y2().value(this)); 117 } 118 119 RefPtr<Gradient> gradient = Gradient::create(startPoint, endPoint); 120 gradient->setSpreadMethod(attributes.spreadMethod()); 121 122 Vector<SVGGradientStop> m_stops = attributes.stops(); 123 float previousOffset = 0.0f; 124 for (unsigned i = 0; i < m_stops.size(); ++i) { 125 float offset = std::min(std::max(previousOffset, m_stops[i].first), 1.0f); 126 previousOffset = offset; 127 gradient->addColorStop(offset, m_stops[i].second); 128 } 129 130 linearGradient->setGradient(gradient); 131 132 if (attributes.stops().isEmpty()) 133 return; 134 135 // This code should go away. PaintServers should go away too. 136 // Only this code should care about bounding boxes 137 linearGradient->setBoundingBoxMode(attributes.boundingBoxMode()); 138 linearGradient->setGradientStops(attributes.stops()); 139 140 // These should possibly be supported on Gradient 141 linearGradient->setGradientTransform(attributes.gradientTransform()); 142 linearGradient->setGradientStart(startPoint); 143 linearGradient->setGradientEnd(endPoint); 144 } 145 146 LinearGradientAttributes SVGLinearGradientElement::collectGradientProperties() const 147 { 148 LinearGradientAttributes attributes; 149 HashSet<const SVGGradientElement*> processedGradients; 150 151 bool isLinear = true; 152 const SVGGradientElement* current = this; 153 154 while (current) { 155 if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr)) 156 attributes.setSpreadMethod((GradientSpreadMethod) current->spreadMethod()); 157 158 if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr)) 159 attributes.setBoundingBoxMode(current->gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 160 161 if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) 162 attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix()); 163 164 if (!attributes.hasStops()) { 165 const Vector<SVGGradientStop>& stops(current->buildStops()); 166 if (!stops.isEmpty()) 167 attributes.setStops(stops); 168 } 169 170 if (isLinear) { 171 const SVGLinearGradientElement* linear = static_cast<const SVGLinearGradientElement*>(current); 172 173 if (!attributes.hasX1() && current->hasAttribute(SVGNames::x1Attr)) 174 attributes.setX1(linear->x1()); 175 176 if (!attributes.hasY1() && current->hasAttribute(SVGNames::y1Attr)) 177 attributes.setY1(linear->y1()); 178 179 if (!attributes.hasX2() && current->hasAttribute(SVGNames::x2Attr)) 180 attributes.setX2(linear->x2()); 181 182 if (!attributes.hasY2() && current->hasAttribute(SVGNames::y2Attr)) 183 attributes.setY2(linear->y2()); 184 } 185 186 processedGradients.add(current); 187 188 // Respect xlink:href, take attributes from referenced element 189 Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href())); 190 if (refNode && (refNode->hasTagName(SVGNames::linearGradientTag) || refNode->hasTagName(SVGNames::radialGradientTag))) { 191 current = static_cast<const SVGGradientElement*>(const_cast<const Node*>(refNode)); 192 193 // Cycle detection 194 if (processedGradients.contains(current)) 195 return LinearGradientAttributes(); 196 197 isLinear = current->gradientType() == LinearGradientPaintServer; 198 } else 199 current = 0; 200 } 201 202 return attributes; 203 } 204 205 } 206 207 #endif // ENABLE(SVG) 208