1 /*
2 Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4 2005 Alexander Kellett <lypanov@kde.org>
5
6 This file is part of the KDE project
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25
26 #if ENABLE(SVG)
27 #include "SVGMaskElement.h"
28
29 #include "CSSStyleSelector.h"
30 #include "GraphicsContext.h"
31 #include "ImageBuffer.h"
32 #include "MappedAttribute.h"
33 #include "RenderSVGContainer.h"
34 #include "SVGLength.h"
35 #include "SVGNames.h"
36 #include "SVGRenderSupport.h"
37 #include "SVGUnitTypes.h"
38 #include <math.h>
39 #include <wtf/MathExtras.h>
40 #include <wtf/OwnPtr.h>
41
42 using namespace std;
43
44 namespace WebCore {
45
SVGMaskElement(const QualifiedName & tagName,Document * doc)46 SVGMaskElement::SVGMaskElement(const QualifiedName& tagName, Document* doc)
47 : SVGStyledLocatableElement(tagName, doc)
48 , SVGURIReference()
49 , SVGTests()
50 , SVGLangSpace()
51 , SVGExternalResourcesRequired()
52 , m_maskUnits(this, SVGNames::maskUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
53 , m_maskContentUnits(this, SVGNames::maskContentUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
54 , m_x(this, SVGNames::xAttr, LengthModeWidth, "-10%")
55 , m_y(this, SVGNames::yAttr, LengthModeHeight, "-10%")
56 , m_width(this, SVGNames::widthAttr, LengthModeWidth, "120%")
57 , m_height(this, SVGNames::heightAttr, LengthModeHeight, "120%")
58 {
59 // Spec: If the x/y attribute is not specified, the effect is as if a value of "-10%" were specified.
60 // Spec: If the width/height attribute is not specified, the effect is as if a value of "120%" were specified.
61 }
62
~SVGMaskElement()63 SVGMaskElement::~SVGMaskElement()
64 {
65 }
66
parseMappedAttribute(MappedAttribute * attr)67 void SVGMaskElement::parseMappedAttribute(MappedAttribute* attr)
68 {
69 if (attr->name() == SVGNames::maskUnitsAttr) {
70 if (attr->value() == "userSpaceOnUse")
71 setMaskUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
72 else if (attr->value() == "objectBoundingBox")
73 setMaskUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
74 } else if (attr->name() == SVGNames::maskContentUnitsAttr) {
75 if (attr->value() == "userSpaceOnUse")
76 setMaskContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
77 else if (attr->value() == "objectBoundingBox")
78 setMaskContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
79 } else if (attr->name() == SVGNames::xAttr)
80 setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
81 else if (attr->name() == SVGNames::yAttr)
82 setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
83 else if (attr->name() == SVGNames::widthAttr)
84 setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
85 else if (attr->name() == SVGNames::heightAttr)
86 setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
87 else {
88 if (SVGURIReference::parseMappedAttribute(attr))
89 return;
90 if (SVGTests::parseMappedAttribute(attr))
91 return;
92 if (SVGLangSpace::parseMappedAttribute(attr))
93 return;
94 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
95 return;
96 SVGStyledElement::parseMappedAttribute(attr);
97 }
98 }
99
svgAttributeChanged(const QualifiedName & attrName)100 void SVGMaskElement::svgAttributeChanged(const QualifiedName& attrName)
101 {
102 SVGStyledElement::svgAttributeChanged(attrName);
103
104 if (!m_masker)
105 return;
106
107 if (attrName == SVGNames::maskUnitsAttr || attrName == SVGNames::maskContentUnitsAttr ||
108 attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
109 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
110 SVGURIReference::isKnownAttribute(attrName) ||
111 SVGTests::isKnownAttribute(attrName) ||
112 SVGLangSpace::isKnownAttribute(attrName) ||
113 SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
114 SVGStyledElement::isKnownAttribute(attrName))
115 m_masker->invalidate();
116 }
117
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)118 void SVGMaskElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
119 {
120 SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
121
122 if (!m_masker)
123 return;
124
125 m_masker->invalidate();
126 }
127
drawMaskerContent(const FloatRect & targetRect,FloatRect & maskDestRect) const128 PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const FloatRect& targetRect, FloatRect& maskDestRect) const
129 {
130 // Determine specified mask size
131 float xValue;
132 float yValue;
133 float widthValue;
134 float heightValue;
135
136 if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
137 xValue = x().valueAsPercentage() * targetRect.width();
138 yValue = y().valueAsPercentage() * targetRect.height();
139 widthValue = width().valueAsPercentage() * targetRect.width();
140 heightValue = height().valueAsPercentage() * targetRect.height();
141 } else {
142 xValue = x().value(this);
143 yValue = y().value(this);
144 widthValue = width().value(this);
145 heightValue = height().value(this);
146 }
147
148 IntSize imageSize(lroundf(widthValue), lroundf(heightValue));
149 clampImageBufferSizeToViewport(document()->view(), imageSize);
150
151 if (imageSize.width() < static_cast<int>(widthValue))
152 widthValue = imageSize.width();
153
154 if (imageSize.height() < static_cast<int>(heightValue))
155 heightValue = imageSize.height();
156
157 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(imageSize);
158 if (!maskImage)
159 return 0;
160
161 maskDestRect = FloatRect(xValue, yValue, widthValue, heightValue);
162 if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
163 maskDestRect.move(targetRect.x(), targetRect.y());
164
165 GraphicsContext* maskImageContext = maskImage->context();
166 ASSERT(maskImageContext);
167
168 maskImageContext->save();
169 maskImageContext->translate(-xValue, -yValue);
170
171 if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
172 maskImageContext->save();
173 maskImageContext->scale(FloatSize(targetRect.width(), targetRect.height()));
174 }
175
176 // Render subtree into ImageBuffer
177 for (Node* n = firstChild(); n; n = n->nextSibling()) {
178 SVGElement* elem = 0;
179 if (n->isSVGElement())
180 elem = static_cast<SVGElement*>(n);
181 if (!elem || !elem->isStyled())
182 continue;
183
184 SVGStyledElement* e = static_cast<SVGStyledElement*>(elem);
185 RenderObject* item = e->renderer();
186 if (!item)
187 continue;
188
189 renderSubtreeToImage(maskImage.get(), item);
190 }
191
192 if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
193 maskImageContext->restore();
194
195 maskImageContext->restore();
196 return maskImage.release();
197 }
198
createRenderer(RenderArena * arena,RenderStyle *)199 RenderObject* SVGMaskElement::createRenderer(RenderArena* arena, RenderStyle*)
200 {
201 RenderSVGContainer* maskContainer = new (arena) RenderSVGContainer(this);
202 maskContainer->setDrawsContents(false);
203 return maskContainer;
204 }
205
canvasResource()206 SVGResource* SVGMaskElement::canvasResource()
207 {
208 if (!m_masker)
209 m_masker = SVGResourceMasker::create(this);
210 return m_masker.get();
211 }
212
213 }
214
215 #endif // ENABLE(SVG)
216