• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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