Rubber-stamped by Brady Eidson.
[webbrowser.git] / WebCore / svg / SVGPatternElement.cpp
bloba10c2c22971a436f5f3d9e7724bd394b16dcc285
1 /*
2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
21 #include "config.h"
23 #if ENABLE(SVG)
24 #include "SVGPatternElement.h"
26 #include "Document.h"
27 #include "FloatConversion.h"
28 #include "GraphicsContext.h"
29 #include "ImageBuffer.h"
30 #include "MappedAttribute.h"
31 #include "PatternAttributes.h"
32 #include "RenderSVGContainer.h"
33 #include "SVGLength.h"
34 #include "SVGNames.h"
35 #include "SVGPaintServerPattern.h"
36 #include "SVGRenderSupport.h"
37 #include "SVGSVGElement.h"
38 #include "SVGStyledTransformableElement.h"
39 #include "SVGTransformList.h"
40 #include "SVGTransformable.h"
41 #include "SVGUnitTypes.h"
42 #include "TransformationMatrix.h"
43 #include <math.h>
44 #include <wtf/MathExtras.h>
45 #include <wtf/OwnPtr.h>
47 using namespace std;
49 namespace WebCore {
51 SVGPatternElement::SVGPatternElement(const QualifiedName& tagName, Document* doc)
52 : SVGStyledElement(tagName, doc)
53 , SVGURIReference()
54 , SVGTests()
55 , SVGLangSpace()
56 , SVGExternalResourcesRequired()
57 , SVGFitToViewBox()
58 , m_x(this, SVGNames::xAttr, LengthModeWidth)
59 , m_y(this, SVGNames::yAttr, LengthModeHeight)
60 , m_width(this, SVGNames::widthAttr, LengthModeWidth)
61 , m_height(this, SVGNames::heightAttr, LengthModeHeight)
62 , m_patternUnits(this, SVGNames::patternUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
63 , m_patternContentUnits(this, SVGNames::patternContentUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
64 , m_patternTransform(this, SVGNames::patternTransformAttr, SVGTransformList::create(SVGNames::patternTransformAttr))
65 , m_href(this, XLinkNames::hrefAttr)
66 , m_externalResourcesRequired(this, SVGNames::externalResourcesRequiredAttr, false)
67 , m_viewBox(this, SVGNames::viewBoxAttr)
68 , m_preserveAspectRatio(this, SVGNames::preserveAspectRatioAttr, SVGPreserveAspectRatio::create())
72 SVGPatternElement::~SVGPatternElement()
76 void SVGPatternElement::parseMappedAttribute(MappedAttribute* attr)
78 if (attr->name() == SVGNames::patternUnitsAttr) {
79 if (attr->value() == "userSpaceOnUse")
80 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
81 else if (attr->value() == "objectBoundingBox")
82 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
83 } else if (attr->name() == SVGNames::patternContentUnitsAttr) {
84 if (attr->value() == "userSpaceOnUse")
85 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
86 else if (attr->value() == "objectBoundingBox")
87 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
88 } else if (attr->name() == SVGNames::patternTransformAttr) {
89 SVGTransformList* patternTransforms = patternTransformBaseValue();
90 if (!SVGTransformable::parseTransformAttribute(patternTransforms, attr->value())) {
91 ExceptionCode ec = 0;
92 patternTransforms->clear(ec);
94 } else if (attr->name() == SVGNames::xAttr)
95 setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
96 else if (attr->name() == SVGNames::yAttr)
97 setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
98 else if (attr->name() == SVGNames::widthAttr) {
99 setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
100 if (widthBaseValue().value(this) < 0.0)
101 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <width> is not allowed");
102 } else if (attr->name() == SVGNames::heightAttr) {
103 setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
104 if (heightBaseValue().value(this) < 0.0)
105 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <height> is not allowed");
106 } else {
107 if (SVGURIReference::parseMappedAttribute(attr))
108 return;
109 if (SVGTests::parseMappedAttribute(attr))
110 return;
111 if (SVGLangSpace::parseMappedAttribute(attr))
112 return;
113 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
114 return;
115 if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
116 return;
118 SVGStyledElement::parseMappedAttribute(attr);
122 void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName)
124 SVGStyledElement::svgAttributeChanged(attrName);
126 if (!m_resource)
127 return;
129 if (attrName == SVGNames::patternUnitsAttr || attrName == SVGNames::patternContentUnitsAttr ||
130 attrName == SVGNames::patternTransformAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
131 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
132 SVGURIReference::isKnownAttribute(attrName) ||
133 SVGTests::isKnownAttribute(attrName) ||
134 SVGLangSpace::isKnownAttribute(attrName) ||
135 SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
136 SVGFitToViewBox::isKnownAttribute(attrName) ||
137 SVGStyledElement::isKnownAttribute(attrName))
138 m_resource->invalidate();
141 void SVGPatternElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
143 SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
145 if (!m_resource)
146 return;
148 m_resource->invalidate();
151 void SVGPatternElement::buildPattern(const FloatRect& targetRect) const
153 PatternAttributes attributes = collectPatternProperties();
155 // If we didn't find any pattern content, ignore the request.
156 if (!attributes.patternContentElement() || !renderer() || !renderer()->style())
157 return;
159 FloatRect patternBoundaries;
160 FloatRect patternContentBoundaries;
162 // Determine specified pattern size
163 if (attributes.boundingBoxMode())
164 patternBoundaries = FloatRect(attributes.x().valueAsPercentage() * targetRect.width(),
165 attributes.y().valueAsPercentage() * targetRect.height(),
166 attributes.width().valueAsPercentage() * targetRect.width(),
167 attributes.height().valueAsPercentage() * targetRect.height());
168 else
169 patternBoundaries = FloatRect(attributes.x().value(this),
170 attributes.y().value(this),
171 attributes.width().value(this),
172 attributes.height().value(this));
174 // Clip pattern boundaries to target boundaries
175 if (patternBoundaries.width() > targetRect.width())
176 patternBoundaries.setWidth(targetRect.width());
178 if (patternBoundaries.height() > targetRect.height())
179 patternBoundaries.setHeight(targetRect.height());
181 IntSize patternSize(patternBoundaries.width(), patternBoundaries.height());
182 clampImageBufferSizeToViewport(document()->view(), patternSize);
184 if (patternSize.width() < static_cast<int>(patternBoundaries.width()))
185 patternBoundaries.setWidth(patternSize.width());
187 if (patternSize.height() < static_cast<int>(patternBoundaries.height()))
188 patternBoundaries.setHeight(patternSize.height());
190 // Eventually calculate the pattern content boundaries (only needed with overflow="visible").
191 RenderStyle* style = renderer()->style();
192 if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) {
193 for (Node* n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) {
194 if (!n->isSVGElement() || !static_cast<SVGElement*>(n)->isStyledTransformable() || !n->renderer())
195 continue;
196 patternContentBoundaries.unite(n->renderer()->repaintRectInLocalCoordinates());
200 TransformationMatrix viewBoxCTM = viewBoxToViewTransform(viewBox(), preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
201 FloatRect patternBoundariesIncludingOverflow = patternBoundaries;
203 // Apply objectBoundingBoxMode fixup for patternContentUnits, if viewBox is not set.
204 if (!patternContentBoundaries.isEmpty()) {
205 if (!viewBoxCTM.isIdentity())
206 patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries);
207 else if (attributes.boundingBoxModeContent())
208 patternContentBoundaries = FloatRect(patternContentBoundaries.x() * targetRect.width(),
209 patternContentBoundaries.y() * targetRect.height(),
210 patternContentBoundaries.width() * targetRect.width(),
211 patternContentBoundaries.height() * targetRect.height());
213 patternBoundariesIncludingOverflow.unite(patternContentBoundaries);
216 IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height()));
217 clampImageBufferSizeToViewport(document()->view(), imageSize);
219 OwnPtr<ImageBuffer> patternImage = ImageBuffer::create(imageSize);
221 if (!patternImage)
222 return;
224 GraphicsContext* context = patternImage->context();
225 ASSERT(context);
227 context->save();
229 // Move to pattern start origin
230 if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) {
231 context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(),
232 patternBoundaries.y() - patternBoundariesIncludingOverflow.y());
234 patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location());
237 // Process viewBox or boundingBoxModeContent correction
238 if (!viewBoxCTM.isIdentity())
239 context->concatCTM(viewBoxCTM);
240 else if (attributes.boundingBoxModeContent()) {
241 context->translate(targetRect.x(), targetRect.y());
242 context->scale(FloatSize(targetRect.width(), targetRect.height()));
245 // Render subtree into ImageBuffer
246 for (Node* n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) {
247 if (!n->isSVGElement() || !static_cast<SVGElement*>(n)->isStyled() || !n->renderer())
248 continue;
249 renderSubtreeToImage(patternImage.get(), n->renderer());
252 context->restore();
254 m_resource->setPatternTransform(attributes.patternTransform());
255 m_resource->setPatternBoundaries(patternBoundaries);
256 m_resource->setTile(patternImage.release());
259 RenderObject* SVGPatternElement::createRenderer(RenderArena* arena, RenderStyle*)
261 RenderSVGContainer* patternContainer = new (arena) RenderSVGContainer(this);
262 patternContainer->setDrawsContents(false);
263 return patternContainer;
266 SVGResource* SVGPatternElement::canvasResource()
268 if (!m_resource)
269 m_resource = SVGPaintServerPattern::create(this);
271 return m_resource.get();
274 PatternAttributes SVGPatternElement::collectPatternProperties() const
276 PatternAttributes attributes;
277 HashSet<const SVGPatternElement*> processedPatterns;
279 const SVGPatternElement* current = this;
280 while (current) {
281 if (!attributes.hasX() && current->hasAttribute(SVGNames::xAttr))
282 attributes.setX(current->x());
284 if (!attributes.hasY() && current->hasAttribute(SVGNames::yAttr))
285 attributes.setY(current->y());
287 if (!attributes.hasWidth() && current->hasAttribute(SVGNames::widthAttr))
288 attributes.setWidth(current->width());
290 if (!attributes.hasHeight() && current->hasAttribute(SVGNames::heightAttr))
291 attributes.setHeight(current->height());
293 if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::patternUnitsAttr))
294 attributes.setBoundingBoxMode(current->patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
296 if (!attributes.hasBoundingBoxModeContent() && current->hasAttribute(SVGNames::patternContentUnitsAttr))
297 attributes.setBoundingBoxModeContent(current->patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
299 if (!attributes.hasPatternTransform() && current->hasAttribute(SVGNames::patternTransformAttr))
300 attributes.setPatternTransform(current->patternTransform()->consolidate().matrix());
302 if (!attributes.hasPatternContentElement() && current->hasChildNodes())
303 attributes.setPatternContentElement(current);
305 processedPatterns.add(current);
307 // Respect xlink:href, take attributes from referenced element
308 Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
309 if (refNode && refNode->hasTagName(SVGNames::patternTag)) {
310 current = static_cast<const SVGPatternElement*>(const_cast<const Node*>(refNode));
312 // Cycle detection
313 if (processedPatterns.contains(current))
314 return PatternAttributes();
315 } else
316 current = 0;
319 return attributes;
324 #endif // ENABLE(SVG)