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 file is part of the KDE project
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.
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.
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.
26 #include "SVGPatternElement.h"
28 #include "AffineTransform.h"
30 #include "FloatConversion.h"
31 #include "GraphicsContext.h"
32 #include "ImageBuffer.h"
33 #include "PatternAttributes.h"
34 #include "RenderSVGContainer.h"
35 #include "SVGLength.h"
37 #include "SVGPaintServerPattern.h"
38 #include "SVGRenderSupport.h"
39 #include "SVGStyledTransformableElement.h"
40 #include "SVGSVGElement.h"
41 #include "SVGTransformList.h"
42 #include "SVGTransformable.h"
43 #include "SVGUnitTypes.h"
46 #include <wtf/OwnPtr.h>
47 #include <wtf/MathExtras.h>
53 SVGPatternElement::SVGPatternElement(const QualifiedName
& tagName
, Document
* doc
)
54 : SVGStyledElement(tagName
, doc
)
58 , SVGExternalResourcesRequired()
60 , m_x(this, LengthModeWidth
)
61 , m_y(this, LengthModeHeight
)
62 , m_width(this, LengthModeWidth
)
63 , m_height(this, LengthModeHeight
)
64 , m_patternUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
)
65 , m_patternContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
)
66 , m_patternTransform(SVGTransformList::create(SVGNames::patternTransformAttr
))
70 SVGPatternElement::~SVGPatternElement()
74 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, int, Enumeration
, enumeration
, PatternUnits
, patternUnits
, SVGNames::patternUnitsAttr
, m_patternUnits
)
75 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, int, Enumeration
, enumeration
, PatternContentUnits
, patternContentUnits
, SVGNames::patternContentUnitsAttr
, m_patternContentUnits
)
76 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, SVGLength
, Length
, length
, X
, x
, SVGNames::xAttr
, m_x
)
77 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, SVGLength
, Length
, length
, Y
, y
, SVGNames::yAttr
, m_y
)
78 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, SVGLength
, Length
, length
, Width
, width
, SVGNames::widthAttr
, m_width
)
79 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, SVGLength
, Length
, length
, Height
, height
, SVGNames::heightAttr
, m_height
)
80 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement
, SVGTransformList
*, TransformList
, transformList
, PatternTransform
, patternTransform
, SVGNames::patternTransformAttr
, m_patternTransform
.get())
82 void SVGPatternElement::parseMappedAttribute(MappedAttribute
* attr
)
84 if (attr
->name() == SVGNames::patternUnitsAttr
) {
85 if (attr
->value() == "userSpaceOnUse")
86 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
);
87 else if (attr
->value() == "objectBoundingBox")
88 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
);
89 } else if (attr
->name() == SVGNames::patternContentUnitsAttr
) {
90 if (attr
->value() == "userSpaceOnUse")
91 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE
);
92 else if (attr
->value() == "objectBoundingBox")
93 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX
);
94 } else if (attr
->name() == SVGNames::patternTransformAttr
) {
95 SVGTransformList
* patternTransforms
= patternTransformBaseValue();
96 if (!SVGTransformable::parseTransformAttribute(patternTransforms
, attr
->value())) {
98 patternTransforms
->clear(ec
);
100 } else if (attr
->name() == SVGNames::xAttr
)
101 setXBaseValue(SVGLength(this, LengthModeWidth
, attr
->value()));
102 else if (attr
->name() == SVGNames::yAttr
)
103 setYBaseValue(SVGLength(this, LengthModeHeight
, attr
->value()));
104 else if (attr
->name() == SVGNames::widthAttr
) {
105 setWidthBaseValue(SVGLength(this, LengthModeWidth
, attr
->value()));
106 if (width().value() < 0.0)
107 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <width> is not allowed");
108 } else if (attr
->name() == SVGNames::heightAttr
) {
109 setHeightBaseValue(SVGLength(this, LengthModeHeight
, attr
->value()));
110 if (width().value() < 0.0)
111 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <height> is not allowed");
113 if (SVGURIReference::parseMappedAttribute(attr
))
115 if (SVGTests::parseMappedAttribute(attr
))
117 if (SVGLangSpace::parseMappedAttribute(attr
))
119 if (SVGExternalResourcesRequired::parseMappedAttribute(attr
))
121 if (SVGFitToViewBox::parseMappedAttribute(attr
))
124 SVGStyledElement::parseMappedAttribute(attr
);
128 void SVGPatternElement::svgAttributeChanged(const QualifiedName
& attrName
)
130 SVGStyledElement::svgAttributeChanged(attrName
);
135 if (attrName
== SVGNames::patternUnitsAttr
|| attrName
== SVGNames::patternContentUnitsAttr
||
136 attrName
== SVGNames::patternTransformAttr
|| attrName
== SVGNames::xAttr
|| attrName
== SVGNames::yAttr
||
137 attrName
== SVGNames::widthAttr
|| attrName
== SVGNames::heightAttr
||
138 SVGURIReference::isKnownAttribute(attrName
) ||
139 SVGTests::isKnownAttribute(attrName
) ||
140 SVGLangSpace::isKnownAttribute(attrName
) ||
141 SVGExternalResourcesRequired::isKnownAttribute(attrName
) ||
142 SVGFitToViewBox::isKnownAttribute(attrName
) ||
143 SVGStyledElement::isKnownAttribute(attrName
))
144 m_resource
->invalidate();
147 void SVGPatternElement::childrenChanged(bool changedByParser
, Node
* beforeChange
, Node
* afterChange
, int childCountDelta
)
149 SVGStyledElement::childrenChanged(changedByParser
, beforeChange
, afterChange
, childCountDelta
);
154 m_resource
->invalidate();
157 void SVGPatternElement::buildPattern(const FloatRect
& targetRect
) const
159 PatternAttributes attributes
= collectPatternProperties();
161 // If we didn't find any pattern content, ignore the request.
162 if (!attributes
.patternContentElement() || !renderer() || !renderer()->style())
165 FloatRect patternBoundaries
;
166 FloatRect patternContentBoundaries
;
168 // Determine specified pattern size
169 if (attributes
.boundingBoxMode())
170 patternBoundaries
= FloatRect(attributes
.x().valueAsPercentage() * targetRect
.width(),
171 attributes
.y().valueAsPercentage() * targetRect
.height(),
172 attributes
.width().valueAsPercentage() * targetRect
.width(),
173 attributes
.height().valueAsPercentage() * targetRect
.height());
175 patternBoundaries
= FloatRect(attributes
.x().value(),
176 attributes
.y().value(),
177 attributes
.width().value(),
178 attributes
.height().value());
180 // Clip pattern boundaries to target boundaries
181 if (patternBoundaries
.width() > targetRect
.width())
182 patternBoundaries
.setWidth(targetRect
.width());
184 if (patternBoundaries
.height() > targetRect
.height())
185 patternBoundaries
.setHeight(targetRect
.height());
187 IntSize
patternSize(patternBoundaries
.width(), patternBoundaries
.height());
188 clampImageBufferSizeToViewport(document()->renderer(), patternSize
);
190 if (patternSize
.width() < static_cast<int>(patternBoundaries
.width()))
191 patternBoundaries
.setWidth(patternSize
.width());
193 if (patternSize
.height() < static_cast<int>(patternBoundaries
.height()))
194 patternBoundaries
.setHeight(patternSize
.height());
196 // Eventually calculate the pattern content boundaries (only needed with overflow="visible").
197 RenderStyle
* style
= renderer()->style();
198 if (style
->overflowX() == OVISIBLE
&& style
->overflowY() == OVISIBLE
) {
199 for (Node
* n
= attributes
.patternContentElement()->firstChild(); n
; n
= n
->nextSibling()) {
200 if (!n
->isSVGElement() || !static_cast<SVGElement
*>(n
)->isStyledTransformable() || !n
->renderer())
202 patternContentBoundaries
.unite(n
->renderer()->relativeBBox(true));
206 AffineTransform viewBoxCTM
= viewBoxToViewTransform(patternBoundaries
.width(), patternBoundaries
.height());
207 FloatRect patternBoundariesIncludingOverflow
= patternBoundaries
;
209 // Apply objectBoundingBoxMode fixup for patternContentUnits, if viewBox is not set.
210 if (!patternContentBoundaries
.isEmpty()) {
211 if (!viewBoxCTM
.isIdentity())
212 patternContentBoundaries
= viewBoxCTM
.mapRect(patternContentBoundaries
);
213 else if (attributes
.boundingBoxModeContent())
214 patternContentBoundaries
= FloatRect(patternContentBoundaries
.x() * targetRect
.width(),
215 patternContentBoundaries
.y() * targetRect
.height(),
216 patternContentBoundaries
.width() * targetRect
.width(),
217 patternContentBoundaries
.height() * targetRect
.height());
219 patternBoundariesIncludingOverflow
.unite(patternContentBoundaries
);
222 IntSize
imageSize(lroundf(patternBoundariesIncludingOverflow
.width()), lroundf(patternBoundariesIncludingOverflow
.height()));
223 clampImageBufferSizeToViewport(document()->renderer(), imageSize
);
225 auto_ptr
<ImageBuffer
> patternImage
= ImageBuffer::create(imageSize
, false);
227 if (!patternImage
.get())
230 GraphicsContext
* context
= patternImage
->context();
235 // Move to pattern start origin
236 if (patternBoundariesIncludingOverflow
.location() != patternBoundaries
.location()) {
237 context
->translate(patternBoundaries
.x() - patternBoundariesIncludingOverflow
.x(),
238 patternBoundaries
.y() - patternBoundariesIncludingOverflow
.y());
240 patternBoundaries
.setLocation(patternBoundariesIncludingOverflow
.location());
243 // Process viewBox or boundingBoxModeContent correction
244 if (!viewBoxCTM
.isIdentity())
245 context
->concatCTM(viewBoxCTM
);
246 else if (attributes
.boundingBoxModeContent()) {
247 context
->translate(targetRect
.x(), targetRect
.y());
248 context
->scale(FloatSize(targetRect
.width(), targetRect
.height()));
251 // Render subtree into ImageBuffer
252 for (Node
* n
= attributes
.patternContentElement()->firstChild(); n
; n
= n
->nextSibling()) {
253 if (!n
->isSVGElement() || !static_cast<SVGElement
*>(n
)->isStyled() || !n
->renderer())
255 renderSubtreeToImage(patternImage
.get(), n
->renderer());
260 m_resource
->setPatternTransform(attributes
.patternTransform());
261 m_resource
->setPatternBoundaries(patternBoundaries
);
262 m_resource
->setTile(patternImage
);
265 RenderObject
* SVGPatternElement::createRenderer(RenderArena
* arena
, RenderStyle
*)
267 RenderSVGContainer
* patternContainer
= new (arena
) RenderSVGContainer(this);
268 patternContainer
->setDrawsContents(false);
269 return patternContainer
;
272 SVGResource
* SVGPatternElement::canvasResource()
275 m_resource
= SVGPaintServerPattern::create(this);
277 return m_resource
.get();
280 PatternAttributes
SVGPatternElement::collectPatternProperties() const
282 PatternAttributes attributes
;
283 HashSet
<const SVGPatternElement
*> processedPatterns
;
285 const SVGPatternElement
* current
= this;
287 if (!attributes
.hasX() && current
->hasAttribute(SVGNames::xAttr
))
288 attributes
.setX(current
->x());
290 if (!attributes
.hasY() && current
->hasAttribute(SVGNames::yAttr
))
291 attributes
.setY(current
->y());
293 if (!attributes
.hasWidth() && current
->hasAttribute(SVGNames::widthAttr
))
294 attributes
.setWidth(current
->width());
296 if (!attributes
.hasHeight() && current
->hasAttribute(SVGNames::heightAttr
))
297 attributes
.setHeight(current
->height());
299 if (!attributes
.hasBoundingBoxMode() && current
->hasAttribute(SVGNames::patternUnitsAttr
))
300 attributes
.setBoundingBoxMode(current
->getAttribute(SVGNames::patternUnitsAttr
) == "objectBoundingBox");
302 if (!attributes
.hasBoundingBoxModeContent() && current
->hasAttribute(SVGNames::patternContentUnitsAttr
))
303 attributes
.setBoundingBoxModeContent(current
->getAttribute(SVGNames::patternContentUnitsAttr
) == "objectBoundingBox");
305 if (!attributes
.hasPatternTransform() && current
->hasAttribute(SVGNames::patternTransformAttr
))
306 attributes
.setPatternTransform(current
->patternTransform()->consolidate().matrix());
308 if (!attributes
.hasPatternContentElement() && current
->hasChildNodes())
309 attributes
.setPatternContentElement(current
);
311 processedPatterns
.add(current
);
313 // Respect xlink:href, take attributes from referenced element
314 Node
* refNode
= ownerDocument()->getElementById(SVGURIReference::getTarget(current
->href()));
315 if (refNode
&& refNode
->hasTagName(SVGNames::patternTag
)) {
316 current
= static_cast<const SVGPatternElement
*>(const_cast<const Node
*>(refNode
));
319 if (processedPatterns
.contains(current
))
320 return PatternAttributes();
330 #endif // ENABLE(SVG)