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