KHtml

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

KDE's Doxygen guidelines are available online.