Home | History | Annotate | Download | only in svg
      1 /*
      2     Copyright (C) 2004, 2005, 2006, 2008 Nikolas Zimmermann <zimmermann (at) kde.org>
      3                   2004, 2005, 2006, 2007 Rob Buis <buis (at) kde.org>
      4                   2005 Alexander Kellett <lypanov (at) kde.org>
      5                   2009 Dirk Schulze <krit (at) webkit.org>
      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 #include "config.h"
     24 
     25 #if ENABLE(SVG)
     26 #include "SVGMaskElement.h"
     27 
     28 #include "CanvasPixelArray.h"
     29 #include "CSSStyleSelector.h"
     30 #include "GraphicsContext.h"
     31 #include "Image.h"
     32 #include "ImageBuffer.h"
     33 #include "ImageData.h"
     34 #include "MappedAttribute.h"
     35 #include "RenderObject.h"
     36 #include "RenderSVGContainer.h"
     37 #include "SVGLength.h"
     38 #include "SVGNames.h"
     39 #include "SVGRenderSupport.h"
     40 #include "SVGUnitTypes.h"
     41 #include <math.h>
     42 #include <wtf/MathExtras.h>
     43 #include <wtf/OwnPtr.h>
     44 #include <wtf/Vector.h>
     45 
     46 using namespace std;
     47 
     48 namespace WebCore {
     49 
     50 SVGMaskElement::SVGMaskElement(const QualifiedName& tagName, Document* doc)
     51     : SVGStyledLocatableElement(tagName, doc)
     52     , SVGURIReference()
     53     , SVGTests()
     54     , SVGLangSpace()
     55     , SVGExternalResourcesRequired()
     56     , m_maskUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
     57     , m_maskContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
     58     , m_x(LengthModeWidth, "-10%")
     59     , m_y(LengthModeHeight, "-10%")
     60     , m_width(LengthModeWidth, "120%")
     61     , m_height(LengthModeHeight, "120%")
     62 {
     63     // Spec: If the x/y attribute is not specified, the effect is as if a value of "-10%" were specified.
     64     // Spec: If the width/height attribute is not specified, the effect is as if a value of "120%" were specified.
     65 }
     66 
     67 SVGMaskElement::~SVGMaskElement()
     68 {
     69 }
     70 
     71 void SVGMaskElement::parseMappedAttribute(MappedAttribute* attr)
     72 {
     73     if (attr->name() == SVGNames::maskUnitsAttr) {
     74         if (attr->value() == "userSpaceOnUse")
     75             setMaskUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
     76         else if (attr->value() == "objectBoundingBox")
     77             setMaskUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
     78     } else if (attr->name() == SVGNames::maskContentUnitsAttr) {
     79         if (attr->value() == "userSpaceOnUse")
     80             setMaskContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE);
     81         else if (attr->value() == "objectBoundingBox")
     82             setMaskContentUnitsBaseValue(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX);
     83     } else if (attr->name() == SVGNames::xAttr)
     84         setXBaseValue(SVGLength(LengthModeWidth, attr->value()));
     85     else if (attr->name() == SVGNames::yAttr)
     86         setYBaseValue(SVGLength(LengthModeHeight, attr->value()));
     87     else if (attr->name() == SVGNames::widthAttr)
     88         setWidthBaseValue(SVGLength(LengthModeWidth, attr->value()));
     89     else if (attr->name() == SVGNames::heightAttr)
     90         setHeightBaseValue(SVGLength(LengthModeHeight, attr->value()));
     91     else {
     92         if (SVGURIReference::parseMappedAttribute(attr))
     93             return;
     94         if (SVGTests::parseMappedAttribute(attr))
     95             return;
     96         if (SVGLangSpace::parseMappedAttribute(attr))
     97             return;
     98         if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
     99             return;
    100         SVGStyledElement::parseMappedAttribute(attr);
    101     }
    102 }
    103 
    104 void SVGMaskElement::svgAttributeChanged(const QualifiedName& attrName)
    105 {
    106     SVGStyledElement::svgAttributeChanged(attrName);
    107 
    108     if (attrName == SVGNames::maskUnitsAttr || attrName == SVGNames::maskContentUnitsAttr ||
    109         attrName == SVGNames::xAttr || attrName == SVGNames::yAttr ||
    110         attrName == SVGNames::widthAttr || attrName == SVGNames::heightAttr ||
    111         SVGURIReference::isKnownAttribute(attrName) ||
    112         SVGTests::isKnownAttribute(attrName) ||
    113         SVGLangSpace::isKnownAttribute(attrName) ||
    114         SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
    115         SVGStyledElement::isKnownAttribute(attrName))
    116         invalidateCanvasResources();
    117 }
    118 
    119 void SVGMaskElement::synchronizeProperty(const QualifiedName& attrName)
    120 {
    121     SVGStyledElement::synchronizeProperty(attrName);
    122 
    123     if (attrName == anyQName()) {
    124         synchronizeMaskUnits();
    125         synchronizeMaskContentUnits();
    126         synchronizeX();
    127         synchronizeY();
    128         synchronizeExternalResourcesRequired();
    129         synchronizeHref();
    130         return;
    131     }
    132 
    133     if (attrName == SVGNames::maskUnitsAttr)
    134         synchronizeMaskUnits();
    135     else if (attrName == SVGNames::maskContentUnitsAttr)
    136         synchronizeMaskContentUnits();
    137     else if (attrName == SVGNames::xAttr)
    138         synchronizeX();
    139     else if (attrName == SVGNames::yAttr)
    140         synchronizeY();
    141     else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
    142         synchronizeExternalResourcesRequired();
    143     else if (SVGURIReference::isKnownAttribute(attrName))
    144         synchronizeHref();
    145 }
    146 
    147 void SVGMaskElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
    148 {
    149     SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
    150     invalidateCanvasResources();
    151 }
    152 
    153 FloatRect SVGMaskElement::maskBoundingBox(const FloatRect& objectBoundingBox) const
    154 {
    155     FloatRect maskBBox;
    156     if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
    157         maskBBox = FloatRect(x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
    158                              y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
    159                              width().valueAsPercentage() * objectBoundingBox.width(),
    160                              height().valueAsPercentage() * objectBoundingBox.height());
    161     else
    162         maskBBox = FloatRect(x().value(this),
    163                              y().value(this),
    164                              width().value(this),
    165                              height().value(this));
    166 
    167     return maskBBox;
    168 }
    169 
    170 PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const RenderObject* object, FloatRect& maskDestRect, bool& emptyMask) const
    171 {
    172     FloatRect objectBoundingBox = object->objectBoundingBox();
    173 
    174     // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present.
    175     maskDestRect = object->repaintRectInLocalCoordinates();
    176     if (maskDestRect.isEmpty()) {
    177         emptyMask = true;
    178         return 0;
    179     }
    180 
    181     // Calculate the smallest rect for the mask ImageBuffer.
    182     FloatRect repaintRect;
    183     Vector<RenderObject*> rendererList;
    184     for (Node* node = firstChild(); node; node = node->nextSibling()) {
    185         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
    186             continue;
    187 
    188         rendererList.append(node->renderer());
    189         repaintRect.unite(node->renderer()->localToParentTransform().mapRect(node->renderer()->repaintRectInLocalCoordinates()));
    190     }
    191 
    192     AffineTransform contextTransform;
    193     // We need to scale repaintRect for objectBoundingBox to get the drawing area.
    194     if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    195         contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    196         FloatPoint contextAdjustment = repaintRect.location();
    197         repaintRect = contextTransform.mapRect(repaintRect);
    198         repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y());
    199         contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y());
    200     }
    201     repaintRect.intersect(maskDestRect);
    202     maskDestRect = repaintRect;
    203     IntRect maskImageRect = enclosingIntRect(maskDestRect);
    204 
    205     maskImageRect.setLocation(IntPoint());
    206 
    207     // Don't create ImageBuffers with image size of 0
    208     if (!maskImageRect.width() || !maskImageRect.height()) {
    209         emptyMask = true;
    210         return 0;
    211     }
    212 
    213     // FIXME: This changes color space to linearRGB, the default color space
    214     // for masking operations in SVG. We need a switch for the other color-space
    215     // attribute values sRGB, inherit and auto.
    216     OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB);
    217     if (!maskImage)
    218         return 0;
    219 
    220     GraphicsContext* maskImageContext = maskImage->context();
    221     ASSERT(maskImageContext);
    222 
    223     maskImageContext->save();
    224 
    225     if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
    226         maskImageContext->translate(-maskDestRect.x(), -maskDestRect.y());
    227     maskImageContext->concatCTM(contextTransform);
    228 
    229     // draw the content into the ImageBuffer
    230     Vector<RenderObject*>::iterator end = rendererList.end();
    231     for (Vector<RenderObject*>::iterator it = rendererList.begin(); it != end; it++)
    232         renderSubtreeToImage(maskImage.get(), *it);
    233 
    234 
    235     maskImageContext->restore();
    236 
    237     // create the luminance mask
    238     RefPtr<ImageData> imageData(maskImage->getUnmultipliedImageData(maskImageRect));
    239     CanvasPixelArray* srcPixelArray(imageData->data());
    240 
    241     for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset += 4) {
    242         unsigned char a = srcPixelArray->get(pixelOffset + 3);
    243         if (!a)
    244             continue;
    245         unsigned char r = srcPixelArray->get(pixelOffset);
    246         unsigned char g = srcPixelArray->get(pixelOffset + 1);
    247         unsigned char b = srcPixelArray->get(pixelOffset + 2);
    248 
    249         double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
    250         srcPixelArray->set(pixelOffset + 3, luma);
    251     }
    252 
    253     maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, IntPoint());
    254 
    255     return maskImage.release();
    256 }
    257 
    258 RenderObject* SVGMaskElement::createRenderer(RenderArena* arena, RenderStyle*)
    259 {
    260     RenderSVGContainer* maskContainer = new (arena) RenderSVGContainer(this);
    261     maskContainer->setDrawsContents(false);
    262     return maskContainer;
    263 }
    264 
    265 SVGResource* SVGMaskElement::canvasResource(const RenderObject* object)
    266 {
    267     ASSERT(object);
    268 
    269     if (m_masker.contains(object))
    270         return m_masker.get(object).get();
    271 
    272     RefPtr<SVGResourceMasker> masker = SVGResourceMasker::create(this);
    273     SVGResourceMasker* maskerPtr = masker.get();
    274     m_masker.set(object, masker.release());
    275 
    276     return maskerPtr;
    277 }
    278 
    279 void SVGMaskElement::invalidateCanvasResources()
    280 {
    281     // Don't call through to the base class since the base class will just
    282     // invalidate one item in the HashMap.
    283     HashMap<const RenderObject*, RefPtr<SVGResourceMasker> >::const_iterator end = m_masker.end();
    284     for (HashMap<const RenderObject*, RefPtr<SVGResourceMasker> >::const_iterator it = m_masker.begin(); it != end; ++it)
    285         it->second->invalidate();
    286 }
    287 
    288 }
    289 
    290 #endif // ENABLE(SVG)
    291