File indexing completed on 2024-04-28 15:24:40
0001 /* 0002 Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org> 0003 2004, 2005, 2006, 2007 Rob Buis <buis@kde.org> 0004 0005 This file is part of the KDE project 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #if ENABLE(SVG) 0024 #include "SVGPatternElement.h" 0025 0026 #include "AffineTransform.h" 0027 #include "Document.h" 0028 #include "FloatConversion.h" 0029 #include "GraphicsContext.h" 0030 #include "ImageBuffer.h" 0031 #include "PatternAttributes.h" 0032 #include "RenderSVGContainer.h" 0033 #include "SVGLength.h" 0034 #include "SVGNames.h" 0035 #include "SVGRenderSupport.h" 0036 #include "SVGStyledTransformableElement.h" 0037 #include "SVGSVGElement.h" 0038 #include "SVGTransformList.h" 0039 #include "SVGTransformable.h" 0040 #include "SVGUnitTypes.h" 0041 0042 #include <math.h> 0043 #include <wtf/OwnPtr.h> 0044 #include <wtf/MathExtras.h> 0045 0046 using namespace std; 0047 0048 namespace WebCore 0049 { 0050 0051 SVGPatternElement::SVGPatternElement(const QualifiedName &tagName, Document *doc) 0052 : SVGStyledElement(tagName, doc) 0053 , SVGURIReference() 0054 , SVGTests() 0055 , SVGLangSpace() 0056 , SVGExternalResourcesRequired() 0057 , SVGFitToViewBox() 0058 , m_x(this, LengthModeWidth) 0059 , m_y(this, LengthModeHeight) 0060 , m_width(this, LengthModeWidth) 0061 , m_height(this, LengthModeHeight) 0062 , m_patternUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) 0063 , m_patternContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) 0064 , m_patternTransform(SVGTransformList::create(SVGNames::patternTransformAttr)) 0065 { 0066 } 0067 0068 SVGPatternElement::~SVGPatternElement() 0069 { 0070 } 0071 0072 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, int, Enumeration, enumeration, PatternUnits, patternUnits, SVGNames::patternUnitsAttr, m_patternUnits) 0073 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, int, Enumeration, enumeration, PatternContentUnits, patternContentUnits, SVGNames::patternContentUnitsAttr, m_patternContentUnits) 0074 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, SVGLength, Length, length, X, x, SVGNames::xAttr, m_x) 0075 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, SVGLength, Length, length, Y, y, SVGNames::yAttr, m_y) 0076 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, SVGLength, Length, length, Width, width, SVGNames::widthAttr, m_width) 0077 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, SVGLength, Length, length, Height, height, SVGNames::heightAttr, m_height) 0078 ANIMATED_PROPERTY_DEFINITIONS(SVGPatternElement, SVGTransformList *, TransformList, transformList, PatternTransform, patternTransform, SVGNames::patternTransformAttr, m_patternTransform.get()) 0079 0080 void SVGPatternElement::parseMappedAttribute(MappedAttribute *attr) 0081 { 0082 if (attr->name() == SVGNames::patternUnitsAttr) { 0083 if (attr->value() == "userSpaceOnUse") { 0084 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE); 0085 } else if (attr->value() == "objectBoundingBox") { 0086 setPatternUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 0087 } 0088 } else if (attr->name() == SVGNames::patternContentUnitsAttr) { 0089 if (attr->value() == "userSpaceOnUse") { 0090 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE); 0091 } else if (attr->value() == "objectBoundingBox") { 0092 setPatternContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX); 0093 } 0094 } else if (attr->name() == SVGNames::patternTransformAttr) { 0095 SVGTransformList *patternTransforms = patternTransformBaseValue(); 0096 if (!SVGTransformable::parseTransformAttribute(patternTransforms, attr->value())) { 0097 ExceptionCode ec = 0; 0098 patternTransforms->clear(ec); 0099 } 0100 } else if (attr->name() == SVGNames::xAttr) { 0101 setXBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 0102 } else if (attr->name() == SVGNames::yAttr) { 0103 setYBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 0104 } else if (attr->name() == SVGNames::widthAttr) { 0105 setWidthBaseValue(SVGLength(this, LengthModeWidth, attr->value())); 0106 if (width().value() < 0.0) { 0107 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <width> is not allowed"); 0108 } 0109 } else if (attr->name() == SVGNames::heightAttr) { 0110 setHeightBaseValue(SVGLength(this, LengthModeHeight, attr->value())); 0111 if (width().value() < 0.0) { 0112 document()->accessSVGExtensions()->reportError("A negative value for pattern attribute <height> is not allowed"); 0113 } 0114 } else { 0115 if (SVGURIReference::parseMappedAttribute(attr)) { 0116 return; 0117 } 0118 if (SVGTests::parseMappedAttribute(attr)) { 0119 return; 0120 } 0121 if (SVGLangSpace::parseMappedAttribute(attr)) { 0122 return; 0123 } 0124 if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) { 0125 return; 0126 } 0127 if (SVGFitToViewBox::parseMappedAttribute(attr)) { 0128 return; 0129 } 0130 0131 SVGStyledElement::parseMappedAttribute(attr); 0132 } 0133 } 0134 0135 void SVGPatternElement::svgAttributeChanged(const QualifiedName &attrName) 0136 { 0137 SVGStyledElement::svgAttributeChanged(attrName); 0138 0139 if (!m_resource) { 0140 return; 0141 } 0142 0143 if (attrName == SVGNames::patternUnitsAttr || attrName == SVGNames::patternContentUnitsAttr || 0144 attrName == SVGNames::patternTransformAttr || attrName == SVGNames::xAttr || attrName == SVGNames::yAttr || 0145 attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr || 0146 SVGURIReference::isKnownAttribute(attrName) || 0147 SVGTests::isKnownAttribute(attrName) || 0148 SVGLangSpace::isKnownAttribute(attrName) || 0149 SVGExternalResourcesRequired::isKnownAttribute(attrName) || 0150 SVGFitToViewBox::isKnownAttribute(attrName) || 0151 SVGStyledElement::isKnownAttribute(attrName)) { 0152 m_resource->invalidate(); 0153 } 0154 } 0155 0156 void SVGPatternElement::childrenChanged(bool changedByParser, Node *beforeChange, Node *afterChange, int childCountDelta) 0157 { 0158 SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); 0159 0160 if (!m_resource) { 0161 return; 0162 } 0163 0164 m_resource->invalidate(); 0165 } 0166 0167 void SVGPatternElement::buildPattern(const FloatRect &targetRect) const 0168 { 0169 PatternAttributes attributes = collectPatternProperties(); 0170 0171 // If we didn't find any pattern content, ignore the request. 0172 if (!attributes.patternContentElement() || !renderer() || !renderer()->style()) { 0173 return; 0174 } 0175 0176 FloatRect patternBoundaries; 0177 FloatRect patternContentBoundaries; 0178 0179 // Determine specified pattern size 0180 if (attributes.boundingBoxMode()) 0181 patternBoundaries = FloatRect(attributes.x().valueAsPercentage() * targetRect.width(), 0182 attributes.y().valueAsPercentage() * targetRect.height(), 0183 attributes.width().valueAsPercentage() * targetRect.width(), 0184 attributes.height().valueAsPercentage() * targetRect.height()); 0185 else 0186 patternBoundaries = FloatRect(attributes.x().value(), 0187 attributes.y().value(), 0188 attributes.width().value(), 0189 attributes.height().value()); 0190 0191 // Clip pattern boundaries to target boundaries 0192 if (patternBoundaries.width() > targetRect.width()) { 0193 patternBoundaries.setWidth(targetRect.width()); 0194 } 0195 0196 if (patternBoundaries.height() > targetRect.height()) { 0197 patternBoundaries.setHeight(targetRect.height()); 0198 } 0199 0200 IntSize patternSize(patternBoundaries.width(), patternBoundaries.height()); 0201 clampImageBufferSizeToViewport(document()->renderer(), patternSize); 0202 0203 if (patternSize.width() < static_cast<int>(patternBoundaries.width())) { 0204 patternBoundaries.setWidth(patternSize.width()); 0205 } 0206 0207 if (patternSize.height() < static_cast<int>(patternBoundaries.height())) { 0208 patternBoundaries.setHeight(patternSize.height()); 0209 } 0210 0211 // Eventually calculate the pattern content boundaries (only needed with overflow="visible"). 0212 RenderStyle *style = renderer()->style(); 0213 if (style->overflowX() == OVISIBLE && style->overflowY() == OVISIBLE) { 0214 for (Node *n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) { 0215 if (!n->isSVGElement() || !static_cast<SVGElement *>(n)->isStyledTransformable() || !n->renderer()) { 0216 continue; 0217 } 0218 patternContentBoundaries.unite(n->renderer()->relativeBBox(true)); 0219 } 0220 } 0221 0222 AffineTransform viewBoxCTM = viewBoxToViewTransform(patternBoundaries.width(), patternBoundaries.height()); 0223 FloatRect patternBoundariesIncludingOverflow = patternBoundaries; 0224 0225 // Apply objectBoundingBoxMode fixup for patternContentUnits, if viewBox is not set. 0226 if (!patternContentBoundaries.isEmpty()) { 0227 if (!viewBoxCTM.isIdentity()) { 0228 patternContentBoundaries = viewBoxCTM.mapRect(patternContentBoundaries); 0229 } else if (attributes.boundingBoxModeContent()) 0230 patternContentBoundaries = FloatRect(patternContentBoundaries.x() * targetRect.width(), 0231 patternContentBoundaries.y() * targetRect.height(), 0232 patternContentBoundaries.width() * targetRect.width(), 0233 patternContentBoundaries.height() * targetRect.height()); 0234 0235 patternBoundariesIncludingOverflow.unite(patternContentBoundaries); 0236 } 0237 0238 IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height())); 0239 clampImageBufferSizeToViewport(document()->renderer(), imageSize); 0240 0241 unique_ptr<ImageBuffer> patternImage = ImageBuffer::create(imageSize, false); 0242 0243 if (!patternImage.get()) { 0244 return; 0245 } 0246 0247 GraphicsContext *context = patternImage->context(); 0248 ASSERT(context); 0249 0250 context->save(); 0251 0252 // Move to pattern start origin 0253 if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) { 0254 context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(), 0255 patternBoundaries.y() - patternBoundariesIncludingOverflow.y()); 0256 0257 patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location()); 0258 } 0259 0260 // Process viewBox or boundingBoxModeContent correction 0261 if (!viewBoxCTM.isIdentity()) { 0262 context->concatCTM(viewBoxCTM); 0263 } else if (attributes.boundingBoxModeContent()) { 0264 context->translate(targetRect.x(), targetRect.y()); 0265 context->scale(FloatSize(targetRect.width(), targetRect.height())); 0266 } 0267 0268 // Render subtree into ImageBuffer 0269 for (Node *n = attributes.patternContentElement()->firstChild(); n; n = n->nextSibling()) { 0270 if (!n->isSVGElement() || !static_cast<SVGElement *>(n)->isStyled() || !n->renderer()) { 0271 continue; 0272 } 0273 renderSubtreeToImage(patternImage.get(), n->renderer()); 0274 } 0275 0276 context->restore(); 0277 0278 m_resource->setPatternTransform(attributes.patternTransform()); 0279 m_resource->setPatternBoundaries(patternBoundaries); 0280 m_resource->setTile(patternImage); 0281 } 0282 0283 RenderObject *SVGPatternElement::createRenderer(RenderArena *arena, RenderStyle *) 0284 { 0285 RenderSVGContainer *patternContainer = new(arena) RenderSVGContainer(this); 0286 patternContainer->setDrawsContents(false); 0287 return patternContainer; 0288 } 0289 0290 SVGResource *SVGPatternElement::canvasResource() 0291 { 0292 if (!m_resource) { 0293 m_resource = SVGPaintServerPattern::create(this); 0294 } 0295 0296 return m_resource.get(); 0297 } 0298 0299 PatternAttributes SVGPatternElement::collectPatternProperties() const 0300 { 0301 PatternAttributes attributes; 0302 HashSet<const SVGPatternElement *> processedPatterns; 0303 0304 const SVGPatternElement *current = this; 0305 while (current) { 0306 if (!attributes.hasX() && current->hasAttribute(SVGNames::xAttr)) { 0307 attributes.setX(current->x()); 0308 } 0309 0310 if (!attributes.hasY() && current->hasAttribute(SVGNames::yAttr)) { 0311 attributes.setY(current->y()); 0312 } 0313 0314 if (!attributes.hasWidth() && current->hasAttribute(SVGNames::widthAttr)) { 0315 attributes.setWidth(current->width()); 0316 } 0317 0318 if (!attributes.hasHeight() && current->hasAttribute(SVGNames::heightAttr)) { 0319 attributes.setHeight(current->height()); 0320 } 0321 0322 if (!attributes.hasBoundingBoxMode() && current->hasAttribute(SVGNames::patternUnitsAttr)) { 0323 attributes.setBoundingBoxMode(current->getAttribute(SVGNames::patternUnitsAttr) == "objectBoundingBox"); 0324 } 0325 0326 if (!attributes.hasBoundingBoxModeContent() && current->hasAttribute(SVGNames::patternContentUnitsAttr)) { 0327 attributes.setBoundingBoxModeContent(current->getAttribute(SVGNames::patternContentUnitsAttr) == "objectBoundingBox"); 0328 } 0329 0330 if (!attributes.hasPatternTransform() && current->hasAttribute(SVGNames::patternTransformAttr)) { 0331 attributes.setPatternTransform(current->patternTransform()->consolidate().matrix()); 0332 } 0333 0334 if (!attributes.hasPatternContentElement() && current->hasChildNodes()) { 0335 attributes.setPatternContentElement(current); 0336 } 0337 0338 processedPatterns.add(current); 0339 0340 // Respect xlink:href, take attributes from referenced element 0341 Node *refNode = ownerDocument()->getElementById(SVGURIReference::getTarget(current->href())); 0342 if (refNode && refNode->hasTagName(SVGNames::patternTag)) { 0343 current = static_cast<const SVGPatternElement *>(const_cast<const Node *>(refNode)); 0344 0345 // Cycle detection 0346 if (processedPatterns.contains(current)) { 0347 return PatternAttributes(); 0348 } 0349 } else { 0350 current = 0; 0351 } 0352 } 0353 0354 return attributes; 0355 } 0356 0357 } 0358 0359 #endif // ENABLE(SVG)