File indexing completed on 2024-04-28 11:39:20

0001 /*
0002     Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
0003                   2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
0004 
0005     This file is part of the KDE project
0006 
0007     This library is free software; you can redistribute it and/or
0008     modify it under the terms of the GNU Library General Public
0009     License as published by the Free Software Foundation; either
0010     version 2 of the License, or (at your option) any later version.
0011 
0012     This library is distributed in the hope that it will be useful,
0013     but WITHOUT ANY WARRANTY; without even the implied warranty of
0014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0015     Library General Public License for more details.
0016 
0017     You should have received a copy of the GNU Library General Public License
0018     along with this library; see the file COPYING.LIB.  If not, write to
0019     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0020     Boston, MA 02110-1301, USA.
0021 */
0022 
0023 #include "wtf/Platform.h"
0024 
0025 #if ENABLE(SVG)
0026 #include "SVGRadialGradientElement.h"
0027 
0028 #include "FloatConversion.h"
0029 #include "FloatPoint.h"
0030 #include "RadialGradientAttributes.h"
0031 #include "RenderObject.h"
0032 #include "SVGLength.h"
0033 #include "SVGNames.h"
0034 #include "SVGPaintServerRadialGradient.h"
0035 #include "SVGStopElement.h"
0036 #include "SVGTransform.h"
0037 #include "SVGTransformList.h"
0038 #include "SVGUnitTypes.h"
0039 
0040 namespace WebCore
0041 {
0042 
0043 SVGRadialGradientElement::SVGRadialGradientElement(const QualifiedName &tagName, Document *doc)
0044     : SVGGradientElement(tagName, doc)
0045     , m_cx(this, LengthModeWidth)
0046     , m_cy(this, LengthModeHeight)
0047     , m_r(this, LengthModeOther)
0048     , m_fx(this, LengthModeWidth)
0049     , m_fy(this, LengthModeHeight)
0050 {
0051     // Spec: If the attribute is not specified, the effect is as if a value of "50%" were specified.
0052     setCxBaseValue(SVGLength(this, LengthModeWidth, "50%"));
0053     setCyBaseValue(SVGLength(this, LengthModeHeight, "50%"));
0054     setRBaseValue(SVGLength(this, LengthModeOther, "50%"));
0055 }
0056 
0057 SVGRadialGradientElement::~SVGRadialGradientElement()
0058 {
0059 }
0060 
0061 ANIMATED_PROPERTY_DEFINITIONS(SVGRadialGradientElement, SVGLength, Length, length, Cx, cx, SVGNames::cxAttr, m_cx)
0062 ANIMATED_PROPERTY_DEFINITIONS(SVGRadialGradientElement, SVGLength, Length, length, Cy, cy, SVGNames::cyAttr, m_cy)
0063 ANIMATED_PROPERTY_DEFINITIONS(SVGRadialGradientElement, SVGLength, Length, length, Fx, fx, SVGNames::fxAttr, m_fx)
0064 ANIMATED_PROPERTY_DEFINITIONS(SVGRadialGradientElement, SVGLength, Length, length, Fy, fy, SVGNames::fyAttr, m_fy)
0065 ANIMATED_PROPERTY_DEFINITIONS(SVGRadialGradientElement, SVGLength, Length, length, R, r, SVGNames::rAttr, m_r)
0066 
0067 void SVGRadialGradientElement::parseMappedAttribute(MappedAttribute *attr)
0068 {
0069     if (attr->name() == SVGNames::cxAttr) {
0070         setCxBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
0071     } else if (attr->name() == SVGNames::cyAttr) {
0072         setCyBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
0073     } else if (attr->name() == SVGNames::rAttr) {
0074         setRBaseValue(SVGLength(this, LengthModeOther, attr->value()));
0075         if (r().value() < 0.0) {
0076             document()->accessSVGExtensions()->reportError("A negative value for radial gradient radius <r> is not allowed");
0077         }
0078     } else if (attr->name() == SVGNames::fxAttr) {
0079         setFxBaseValue(SVGLength(this, LengthModeWidth, attr->value()));
0080     } else if (attr->name() == SVGNames::fyAttr) {
0081         setFyBaseValue(SVGLength(this, LengthModeHeight, attr->value()));
0082     } else {
0083         SVGGradientElement::parseMappedAttribute(attr);
0084     }
0085 }
0086 
0087 void SVGRadialGradientElement::svgAttributeChanged(const QualifiedName &attrName)
0088 {
0089     SVGGradientElement::svgAttributeChanged(attrName);
0090 
0091     if (!m_resource) {
0092         return;
0093     }
0094 
0095     if (attrName == SVGNames::cxAttr || attrName == SVGNames::cyAttr ||
0096             attrName == SVGNames::fxAttr || attrName == SVGNames::fyAttr ||
0097             attrName == SVGNames::rAttr) {
0098         m_resource->invalidate();
0099     }
0100 }
0101 
0102 void SVGRadialGradientElement::buildGradient() const
0103 {
0104     RadialGradientAttributes attributes = collectGradientProperties();
0105 
0106     // If we didn't find any gradient containing stop elements, ignore the request.
0107     if (attributes.stops().isEmpty()) {
0108         return;
0109     }
0110 
0111     RefPtr<SVGPaintServerRadialGradient> radialGradient = WTF::static_pointer_cast<SVGPaintServerRadialGradient>(m_resource);
0112 
0113     radialGradient->setGradientStops(attributes.stops());
0114     radialGradient->setBoundingBoxMode(attributes.boundingBoxMode());
0115     radialGradient->setGradientSpreadMethod(attributes.spreadMethod());
0116     radialGradient->setGradientTransform(attributes.gradientTransform());
0117     radialGradient->setGradientCenter(FloatPoint::narrowPrecision(attributes.cx(), attributes.cy()));
0118     radialGradient->setGradientFocal(FloatPoint::narrowPrecision(attributes.fx(), attributes.fy()));
0119     radialGradient->setGradientRadius(narrowPrecisionToFloat(attributes.r()));
0120 }
0121 
0122 RadialGradientAttributes SVGRadialGradientElement::collectGradientProperties() const
0123 {
0124     RadialGradientAttributes attributes;
0125     HashSet<const SVGGradientElement *> processedGradients;
0126 
0127     bool isRadial = true;
0128     const SVGGradientElement *current = this;
0129 
0130     while (current) {
0131         if (!attributes.hasSpreadMethod() && current->hasAttribute(SVGNames::spreadMethodAttr)) {
0132             attributes.setSpreadMethod((SVGGradientSpreadMethod) current->spreadMethod());
0133         }
0134 
0135         if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::gradientUnitsAttr)) {
0136             attributes.setBoundingBoxMode(current->getAttribute(SVGNames::gradientUnitsAttr) == "objectBoundingBox");
0137         }
0138 
0139         if (!attributes.hasGradientTransform() && current->hasAttribute(SVGNames::gradientTransformAttr)) {
0140             attributes.setGradientTransform(current->gradientTransform()->consolidate().matrix());
0141         }
0142 
0143         if (!attributes.hasStops()) {
0144             const Vector<SVGGradientStop> &stops(current->buildStops());
0145             if (!stops.isEmpty()) {
0146                 attributes.setStops(stops);
0147             }
0148         }
0149 
0150         if (isRadial) {
0151             const SVGRadialGradientElement *radial = static_cast<const SVGRadialGradientElement *>(current);
0152 
0153             if (!attributes.hasCx() && current->hasAttribute(SVGNames::cxAttr)) {
0154                 attributes.setCx(radial->cx().valueAsPercentage());
0155             }
0156 
0157             if (!attributes.hasCy() && current->hasAttribute(SVGNames::cyAttr)) {
0158                 attributes.setCy(radial->cy().valueAsPercentage());
0159             }
0160 
0161             if (!attributes.hasR() && current->hasAttribute(SVGNames::rAttr)) {
0162                 attributes.setR(radial->r().valueAsPercentage());
0163             }
0164 
0165             if (!attributes.hasFx() && current->hasAttribute(SVGNames::fxAttr)) {
0166                 attributes.setFx(radial->fx().valueAsPercentage());
0167             }
0168 
0169             if (!attributes.hasFy() && current->hasAttribute(SVGNames::fyAttr)) {
0170                 attributes.setFy(radial->fy().valueAsPercentage());
0171             }
0172         }
0173 
0174         processedGradients.add(current);
0175 
0176         // Respect xlink:href, take attributes from referenced element
0177         Node *refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
0178         if (refNode && (refNode->hasTagName(SVGNames::radialGradientTag) || refNode->hasTagName(SVGNames::linearGradientTag))) {
0179             current = static_cast<const SVGGradientElement *>(const_cast<const Node *>(refNode));
0180 
0181             // Cycle detection
0182             if (processedGradients.contains(current)) {
0183                 return RadialGradientAttributes();
0184             }
0185 
0186             isRadial = current->gradientType() == RadialGradientPaintServer;
0187         } else {
0188             current = nullptr;
0189         }
0190     }
0191 
0192     // Handle default values for fx/fy
0193     if (!attributes.hasFx()) {
0194         attributes.setFx(attributes.cx());
0195     }
0196 
0197     if (!attributes.hasFy()) {
0198         attributes.setFy(attributes.cy());
0199     }
0200 
0201     return attributes;
0202 }
0203 
0204 // KHTML ElementImpl pure virtual method
0205 quint32 SVGRadialGradientElement::id() const
0206 {
0207     return SVGNames::radialGradientTag.id();
0208 }
0209 
0210 }
0211 
0212 #endif // ENABLE(SVG)