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.
24 #include "SVGPatternElement.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"
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"
44 #include <wtf/MathExtras.h>
45 #include <wtf/OwnPtr.h>
51 SVGPatternElement::SVGPatternElement(const QualifiedName
& tagName
, Document
* doc
)
52 : SVGStyledElement(tagName
, doc
)
56 , SVGExternalResourcesRequired()
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())) {
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");
107 if (SVGURIReference::parseMappedAttribute(attr
))
109 if (SVGTests::parseMappedAttribute(attr
))
111 if (SVGLangSpace::parseMappedAttribute(attr
))
113 if (SVGExternalResourcesRequired::parseMappedAttribute(attr
))
115 if (SVGFitToViewBox::parseMappedAttribute(document(), attr
))
118 SVGStyledElement::parseMappedAttribute(attr
);
122 void SVGPatternElement::svgAttributeChanged(const QualifiedName
& attrName
)
124 SVGStyledElement::svgAttributeChanged(attrName
);
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
);
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())
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());
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())
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
);
224 GraphicsContext
* context
= patternImage
->context();
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())
249 renderSubtreeToImage(patternImage
.get(), n
->renderer());
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()
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;
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
));
313 if (processedPatterns
.contains(current
))
314 return PatternAttributes();
324 #endif // ENABLE(SVG)