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) Research In Motion Limited 2010. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#if ENABLE(SVG)
25#include "SVGGradientElement.h"
26
27#include "Attribute.h"
28#include "CSSStyleSelector.h"
29#include "RenderSVGHiddenContainer.h"
30#include "RenderSVGPath.h"
31#include "RenderSVGResourceLinearGradient.h"
32#include "RenderSVGResourceRadialGradient.h"
33#include "SVGNames.h"
34#include "SVGStopElement.h"
35#include "SVGTransformList.h"
36#include "SVGTransformable.h"
37#include "SVGUnitTypes.h"
38
39namespace WebCore {
40
41// Animated property definitions
42DEFINE_ANIMATED_ENUMERATION(SVGGradientElement, SVGNames::spreadMethodAttr, SpreadMethod, spreadMethod)
43DEFINE_ANIMATED_ENUMERATION(SVGGradientElement, SVGNames::gradientUnitsAttr, GradientUnits, gradientUnits)
44DEFINE_ANIMATED_TRANSFORM_LIST(SVGGradientElement, SVGNames::gradientTransformAttr, GradientTransform, gradientTransform)
45DEFINE_ANIMATED_STRING(SVGGradientElement, XLinkNames::hrefAttr, Href, href)
46DEFINE_ANIMATED_BOOLEAN(SVGGradientElement, SVGNames::externalResourcesRequiredAttr, ExternalResourcesRequired, externalResourcesRequired)
47
48SVGGradientElement::SVGGradientElement(const QualifiedName& tagName, Document* document)
49    : SVGStyledElement(tagName, document)
50    , m_gradientUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
51{
52}
53
54void SVGGradientElement::parseMappedAttribute(Attribute* attr)
55{
56    if (attr->name() == SVGNames::gradientUnitsAttr) {
57        if (attr->value() == "userSpaceOnUse")
58            setGradientUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
59        else if (attr->value() == "objectBoundingBox")
60            setGradientUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
61    } else if (attr->name() == SVGNames::gradientTransformAttr) {
62        SVGTransformList newList;
63        if (!SVGTransformable::parseTransformAttribute(newList, attr->value()))
64            newList.clear();
65
66        detachAnimatedGradientTransformListWrappers(newList.size());
67        setGradientTransformBaseValue(newList);
68    } else if (attr->name() == SVGNames::spreadMethodAttr) {
69        if (attr->value() == "reflect")
70            setSpreadMethodBaseValue(SpreadMethodReflect);
71        else if (attr->value() == "repeat")
72            setSpreadMethodBaseValue(SpreadMethodRepeat);
73        else if (attr->value() == "pad")
74            setSpreadMethodBaseValue(SpreadMethodPad);
75    } else {
76        if (SVGURIReference::parseMappedAttribute(attr))
77            return;
78        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
79            return;
80
81        SVGStyledElement::parseMappedAttribute(attr);
82    }
83}
84
85void SVGGradientElement::svgAttributeChanged(const QualifiedName& attrName)
86{
87    SVGStyledElement::svgAttributeChanged(attrName);
88
89    RenderObject* object = renderer();
90    if (!object)
91        return;
92
93    if (attrName == SVGNames::gradientUnitsAttr
94        || attrName == SVGNames::gradientTransformAttr
95        || attrName == SVGNames::spreadMethodAttr
96        || SVGURIReference::isKnownAttribute(attrName)
97        || SVGExternalResourcesRequired::isKnownAttribute(attrName)
98        || SVGStyledElement::isKnownAttribute(attrName))
99        object->setNeedsLayout(true);
100}
101
102void SVGGradientElement::synchronizeProperty(const QualifiedName& attrName)
103{
104    SVGStyledElement::synchronizeProperty(attrName);
105
106    if (attrName == anyQName()) {
107        synchronizeGradientUnits();
108        synchronizeGradientTransform();
109        synchronizeSpreadMethod();
110        synchronizeExternalResourcesRequired();
111        synchronizeHref();
112        return;
113    }
114
115    if (attrName == SVGNames::gradientUnitsAttr)
116        synchronizeGradientUnits();
117    else if (attrName == SVGNames::gradientTransformAttr)
118        synchronizeGradientTransform();
119    else if (attrName == SVGNames::spreadMethodAttr)
120        synchronizeSpreadMethod();
121    else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
122        synchronizeExternalResourcesRequired();
123    else if (SVGURIReference::isKnownAttribute(attrName))
124        synchronizeHref();
125}
126
127void SVGGradientElement::fillPassedAttributeToPropertyTypeMap(AttributeToPropertyTypeMap& attributeToPropertyTypeMap)
128{
129    SVGStyledElement::fillPassedAttributeToPropertyTypeMap(attributeToPropertyTypeMap);
130
131    attributeToPropertyTypeMap.set(SVGNames::spreadMethodAttr, AnimatedEnumeration);
132    attributeToPropertyTypeMap.set(SVGNames::gradientUnitsAttr, AnimatedEnumeration);
133    attributeToPropertyTypeMap.set(SVGNames::gradientTransformAttr, AnimatedTransformList);
134    attributeToPropertyTypeMap.set(XLinkNames::hrefAttr, AnimatedString);
135}
136
137void SVGGradientElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
138{
139    SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
140
141    if (changedByParser)
142        return;
143
144    if (RenderObject* object = renderer())
145        object->setNeedsLayout(true);
146}
147
148Vector<Gradient::ColorStop> SVGGradientElement::buildStops()
149{
150    Vector<Gradient::ColorStop> stops;
151
152    float previousOffset = 0.0f;
153    for (Node* n = firstChild(); n; n = n->nextSibling()) {
154        SVGElement* element = n->isSVGElement() ? static_cast<SVGElement*>(n) : 0;
155        if (!element || !element->isGradientStop())
156            continue;
157
158        SVGStopElement* stop = static_cast<SVGStopElement*>(element);
159        Color color = stop->stopColorIncludingOpacity();
160
161        // Figure out right monotonic offset
162        float offset = stop->offset();
163        offset = std::min(std::max(previousOffset, offset), 1.0f);
164        previousOffset = offset;
165
166        // Extract individual channel values
167        // FIXME: Why doesn't ColorStop take a Color and an offset??
168        float r, g, b, a;
169        color.getRGBA(r, g, b, a);
170
171        stops.append(Gradient::ColorStop(offset, r, g, b, a));
172    }
173
174    return stops;
175}
176
177}
178
179#endif // ENABLE(SVG)
180