Home | History | Annotate | Download | only in svg
      1 /*
      2     Copyright (C) 2004, 2005, 2006, 2007, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005, 2006, 2007 Rob Buis <buis (at) 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 
     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 
     68 SVGPatternElement::~SVGPatternElement()
     69 {
     70 }
     71 
     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 
    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 
    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 
    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 
    189 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 
    290 RenderObject* SVGPatternElement::createRenderer(RenderArena* arena, RenderStyle*)
    291 {
    292     RenderSVGContainer* patternContainer = new (arena) RenderSVGContainer(this);
    293     patternContainer->setDrawsContents(false);
    294     return patternContainer;
    295 }
    296 
    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 
    305 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