1 /*
2 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org>
4
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.
9
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.
14
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.
19 */
20
21 #include "config.h"
22
23 #if ENABLE(SVG)
24 #include "SVGPatternElement.h"
25
26 #include "AffineTransform.h"
27 #include "Document.h"
28 #include "FloatConversion.h"
29 #include "GraphicsContext.h"
30 #include "ImageBuffer.h"
31 #include "MappedAttribute.h"
32 #include "PatternAttributes.h"
33 #include "RenderSVGContainer.h"
34 #include "SVGLength.h"
35 #include "SVGNames.h"
36 #include "SVGPaintServerPattern.h"
37 #include "SVGRenderSupport.h"
38 #include "SVGSVGElement.h"
39 #include "SVGStyledTransformableElement.h"
40 #include "SVGTransformList.h"
41 #include "SVGTransformable.h"
42 #include "SVGUnitTypes.h"
43 #include <math.h>
44 #include <wtf/MathExtras.h>
45 #include <wtf/OwnPtr.h>
46
47 using namespace std;
48
49 namespace WebCore {
50
SVGPatternElement(const QualifiedName & tagName,Document * doc)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(LengthModeWidth)
59 , m_y(LengthModeHeight)
60 , m_width(LengthModeWidth)
61 , m_height(LengthModeHeight)
62 , m_patternUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
63 , m_patternContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
64 , m_patternTransform(SVGTransformList::create(SVGNames::patternTransformAttr))
65 {
66 }
67
~SVGPatternElement()68 SVGPatternElement::~SVGPatternElement()
69 {
70 }
71
parseMappedAttribute(MappedAttribute * attr)72 void SVGPatternElement::parseMappedAttribute(MappedAttribute* attr)
73 {
74 if (attr->name() == SVGNames::patternUnitsAttr) {
75 if (attr->value() == "userSpaceOnUse")
76 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
77 else if (attr->value() == "objectBoundingBox")
78 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
79 } else if (attr->name() == SVGNames::patternContentUnitsAttr) {
80 if (attr->value() == "userSpaceOnUse")
81 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
82 else if (attr->value() == "objectBoundingBox")
83 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
84 } else if (attr->name() == SVGNames::patternTransformAttr) {
85 SVGTransformList* patternTransforms = patternTransformBaseValue();
86 if (!SVGTransformable::parseTransformAttribute(patternTransforms, attr->value())) {
87 ExceptionCode ec = 0;
88 patternTransforms->clear(ec);
89 }
90 } else if (attr->name() == SVGNames::xAttr)
91 setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
92 else if (attr->name() == SVGNames::yAttr)
93 setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
94 else if (attr->name() == SVGNames::widthAttr) {
95 setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
96 if (widthBaseValue().value(this) < 0.0)
97 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <width> is not allowed");
98 } else if (attr->name() == SVGNames::heightAttr) {
99 setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
100 if (heightBaseValue().value(this) < 0.0)
101 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <height> is not allowed");
102 } else {
103 if (SVGURIReference::parseMappedAttribute(attr))
104 return;
105 if (SVGTests::parseMappedAttribute(attr))
106 return;
107 if (SVGLangSpace::parseMappedAttribute(attr))
108 return;
109 if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
110 return;
111 if (SVGFitToViewBox::parseMappedAttribute(document(), attr))
112 return;
113
114 SVGStyledElement::parseMappedAttribute(attr);
115 }
116 }
117
svgAttributeChanged(const QualifiedName & attrName)118 void SVGPatternElement::svgAttributeChanged(const QualifiedName& attrName)
119 {
120 SVGStyledElement::svgAttributeChanged(attrName);
121
122 if (!m_resource)
123 return;
124
125 if (attrName == SVGNames::patternUnitsAttr || attrName == SVGNames::patternContentUnitsAttr ||
126 attrName == SVGNames::patternTransformAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
127 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
128 SVGURIReference::isKnownAttribute(attrName) ||
129 SVGTests::isKnownAttribute(attrName) ||
130 SVGLangSpace::isKnownAttribute(attrName) ||
131 SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
132 SVGFitToViewBox::isKnownAttribute(attrName) ||
133 SVGStyledElement::isKnownAttribute(attrName))
134 m_resource->invalidate();
135 }
136
synchronizeProperty(const QualifiedName & attrName)137 void SVGPatternElement::synchronizeProperty(const QualifiedName& attrName)
138 {
139 SVGStyledElement::synchronizeProperty(attrName);
140
141 if (attrName == anyQName()) {
142 synchronizePatternUnits();
143 synchronizePatternContentUnits();
144 synchronizePatternTransform();
145 synchronizeX();
146 synchronizeY();
147 synchronizeWidth();
148 synchronizeHeight();
149 synchronizeExternalResourcesRequired();
150 synchronizeViewBox();
151 synchronizePreserveAspectRatio();
152 synchronizeHref();
153 return;
154 }
155
156 if (attrName == SVGNames::patternUnitsAttr)
157 synchronizePatternUnits();
158 else if (attrName == SVGNames::patternContentUnitsAttr)
159 synchronizePatternContentUnits();
160 else if (attrName == SVGNames::patternTransformAttr)
161 synchronizePatternTransform();
162 else if (attrName == SVGNames::xAttr)
163 synchronizeX();
164 else if (attrName == SVGNames::yAttr)
165 synchronizeY();
166 else if (attrName == SVGNames::widthAttr)
167 synchronizeWidth();
168 else if (attrName == SVGNames::heightAttr)
169 synchronizeHeight();
170 else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
171 synchronizeExternalResourcesRequired();
172 else if (SVGFitToViewBox::isKnownAttribute(attrName)) {
173 synchronizeViewBox();
174 synchronizePreserveAspectRatio();
175 } else if (SVGURIReference::isKnownAttribute(attrName))
176 synchronizeHref();
177 }
178
childrenChanged(bool changedByParser,Node * beforeChange,Node * afterChange,int childCountDelta)179 void SVGPatternElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
180 {
181 SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
182
183 if (!m_resource)
184 return;
185
186 m_resource->invalidate();
187 }
188
buildPattern(const FloatRect & targetRect) const189 void SVGPatternElement::buildPattern(const FloatRect& targetRect) const
190 {
191 PatternAttributes attributes = collectPatternProperties();
192
193 // If we didn't find any pattern content, ignore the request.
194 if (!attributes.patternContentElement() || !renderer() || !renderer()->style())
195 return;
196
197 FloatRect patternBoundaries;
198 FloatRect patternContentBoundaries;
199
200 // Determine specified pattern size
201 if (attributes.boundingBoxMode())
202 patternBoundaries = FloatRect(attributes.x().valueAsPercentage() * targetRect.width(),
203 attributes.y().valueAsPercentage() * targetRect.height(),
204 attributes.width().valueAsPercentage() * targetRect.width(),
205 attributes.height().valueAsPercentage() * targetRect.height());
206 else
207 patternBoundaries = FloatRect(attributes.x().value(this),
208 attributes.y().value(this),
209 attributes.width().value(this),
210 attributes.height().value(this));
211
212 IntSize patternSize(patternBoundaries.width(), patternBoundaries.height());
213 clampImageBufferSizeToViewport(document()->view(), patternSize);
214
215 if (patternSize.width() < static_cast<int>(patternBoundaries.width()))
216 patternBoundaries.setWidth(patternSize.width());
217
218 if (patternSize.height() < static_cast<int>(patternBoundaries.height()))
219 patternBoundaries.setHeight(patternSize.height());
220
221 // Eventually calculate the pattern content boundaries (only needed with overflow="visible").
222 RenderStyle* style = renderer()->style();
223 if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) {
224 for (Node* n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) {
225 if (!n->isSVGElement() || !static_cast<SVGElement*>(n)->isStyledTransformable() || !n->renderer())
226 continue;
227 patternContentBoundaries.unite(n->renderer()->repaintRectInLocalCoordinates());
228 }
229 }
230
231 AffineTransform viewBoxCTM = viewBoxToViewTransform(viewBox(), preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height());
232 FloatRect patternBoundariesIncludingOverflow = patternBoundaries;
233
234 // Apply objectBoundingBoxMode fixup for patternContentUnits, if viewBox is not set.
235 if (!patternContentBoundaries.isEmpty()) {
236 if (!viewBoxCTM.isIdentity())
237 patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries);
238 else if (attributes.boundingBoxModeContent())
239 patternContentBoundaries = FloatRect(patternContentBoundaries.x() * targetRect.width(),
240 patternContentBoundaries.y() * targetRect.height(),
241 patternContentBoundaries.width() * targetRect.width(),
242 patternContentBoundaries.height() * targetRect.height());
243
244 patternBoundariesIncludingOverflow.unite(patternContentBoundaries);
245 }
246
247 IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height()));
248 clampImageBufferSizeToViewport(document()->view(), imageSize);
249
250 OwnPtr<ImageBuffer> patternImage = ImageBuffer::create(imageSize);
251
252 if (!patternImage)
253 return;
254
255 GraphicsContext* context = patternImage->context();
256 ASSERT(context);
257
258 context->save();
259
260 // Move to pattern start origin
261 if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) {
262 context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(),
263 patternBoundaries.y() - patternBoundariesIncludingOverflow.y());
264
265 patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location());
266 }
267
268 // Process viewBox or boundingBoxModeContent correction
269 if (!viewBoxCTM.isIdentity())
270 context->concatCTM(viewBoxCTM);
271 else if (attributes.boundingBoxModeContent()) {
272 context->translate(targetRect.x(), targetRect.y());
273 context->scale(FloatSize(targetRect.width(), targetRect.height()));
274 }
275
276 // Render subtree into ImageBuffer
277 for (Node* n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) {
278 if (!n->isSVGElement() || !static_cast<SVGElement*>(n)->isStyled() || !n->renderer())
279 continue;
280 renderSubtreeToImage(patternImage.get(), n->renderer());
281 }
282
283 context->restore();
284
285 m_resource->setPatternTransform(attributes.patternTransform());
286 m_resource->setPatternBoundaries(patternBoundaries);
287 m_resource->setTile(patternImage.release());
288 }
289
createRenderer(RenderArena * arena,RenderStyle *)290 RenderObject* SVGPatternElement::createRenderer(RenderArena* arena, RenderStyle*)
291 {
292 RenderSVGContainer* patternContainer = new (arena) RenderSVGContainer(this);
293 patternContainer->setDrawsContents(false);
294 return patternContainer;
295 }
296
canvasResource(const RenderObject *)297 SVGResource* SVGPatternElement::canvasResource(const RenderObject*)
298 {
299 if (!m_resource)
300 m_resource = SVGPaintServerPattern::create(this);
301
302 return m_resource.get();
303 }
304
collectPatternProperties() const305 PatternAttributes SVGPatternElement::collectPatternProperties() const
306 {
307 PatternAttributes attributes;
308 HashSet<const SVGPatternElement*> processedPatterns;
309
310 const SVGPatternElement* current = this;
311 while (current) {
312 if (!attributes.hasX() && current->hasAttribute(SVGNames::xAttr))
313 attributes.setX(current->x());
314
315 if (!attributes.hasY() && current->hasAttribute(SVGNames::yAttr))
316 attributes.setY(current->y());
317
318 if (!attributes.hasWidth() && current->hasAttribute(SVGNames::widthAttr))
319 attributes.setWidth(current->width());
320
321 if (!attributes.hasHeight() && current->hasAttribute(SVGNames::heightAttr))
322 attributes.setHeight(current->height());
323
324 if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::patternUnitsAttr))
325 attributes.setBoundingBoxMode(current->patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
326
327 if (!attributes.hasBoundingBoxModeContent() && current->hasAttribute(SVGNames::patternContentUnitsAttr))
328 attributes.setBoundingBoxModeContent(current->patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
329
330 if (!attributes.hasPatternTransform() && current->hasAttribute(SVGNames::patternTransformAttr))
331 attributes.setPatternTransform(current->patternTransform()->consolidate().matrix());
332
333 if (!attributes.hasPatternContentElement() && current->hasChildNodes())
334 attributes.setPatternContentElement(current);
335
336 processedPatterns.add(current);
337
338 // Respect xlink:href, take attributes from referenced element
339 Node* refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href()));
340 if (refNode && refNode->hasTagName(SVGNames::patternTag)) {
341 current = static_cast<const SVGPatternElement*>(const_cast<const Node*>(refNode));
342
343 // Cycle detection
344 if (processedPatterns.contains(current))
345 return PatternAttributes();
346 } else
347 current = 0;
348 }
349
350 return attributes;
351 }
352
353 }
354
355 #endif // ENABLE(SVG)
356