Home | History | Annotate | Download | only in svg
      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