summaryrefslogtreecommitdiffstats
path: root/WebCore/svg/SVGMaskElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/svg/SVGMaskElement.cpp')
-rw-r--r--WebCore/svg/SVGMaskElement.cpp197
1 files changed, 134 insertions, 63 deletions
diff --git a/WebCore/svg/SVGMaskElement.cpp b/WebCore/svg/SVGMaskElement.cpp
index e131d34..75f3dcd 100644
--- a/WebCore/svg/SVGMaskElement.cpp
+++ b/WebCore/svg/SVGMaskElement.cpp
@@ -25,10 +25,14 @@
#if ENABLE(SVG)
#include "SVGMaskElement.h"
+#include "CanvasPixelArray.h"
#include "CSSStyleSelector.h"
#include "GraphicsContext.h"
+#include "Image.h"
#include "ImageBuffer.h"
+#include "ImageData.h"
#include "MappedAttribute.h"
+#include "RenderObject.h"
#include "RenderSVGContainer.h"
#include "SVGLength.h"
#include "SVGNames.h"
@@ -37,6 +41,7 @@
#include <math.h>
#include <wtf/MathExtras.h>
#include <wtf/OwnPtr.h>
+#include <wtf/Vector.h>
using namespace std;
@@ -48,14 +53,12 @@ SVGMaskElement::SVGMaskElement(const QualifiedName& tagName, Document* doc)
, SVGTests()
, SVGLangSpace()
, SVGExternalResourcesRequired()
- , m_maskUnits(this, SVGNames::maskUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
- , m_maskContentUnits(this, SVGNames::maskContentUnitsAttr, SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
- , m_x(this, SVGNames::xAttr, LengthModeWidth, "-10%")
- , m_y(this, SVGNames::yAttr, LengthModeHeight, "-10%")
- , m_width(this, SVGNames::widthAttr, LengthModeWidth, "120%")
- , m_height(this, SVGNames::heightAttr, LengthModeHeight, "120%")
- , m_href(this, XLinkNames::hrefAttr)
- , m_externalResourcesRequired(this, SVGNames::externalResourcesRequiredAttr, false)
+ , m_maskUnits(SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
+ , m_maskContentUnits(SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
+ , m_x(LengthModeWidth, "-10%")
+ , m_y(LengthModeHeight, "-10%")
+ , m_width(LengthModeWidth, "120%")
+ , m_height(LengthModeHeight, "120%")
{
// Spec: If the x/y attribute is not specified, the effect is as if a value of "-10%" were specified.
// Spec: If the width/height attribute is not specified, the effect is as if a value of "120%" were specified.
@@ -102,7 +105,7 @@ void SVGMaskElement::svgAttributeChanged(const QualifiedName& attrName)
{
SVGStyledElement::svgAttributeChanged(attrName);
- if (!m_masker)
+ if (m_masker.isEmpty())
return;
if (attrName == SVGNames::maskUnitsAttr || attrName == SVGNames::maskContentUnitsAttr ||
@@ -113,90 +116,151 @@ void SVGMaskElement::svgAttributeChanged(const QualifiedName& attrName)
SVGLangSpace::isKnownAttribute(attrName) ||
SVGExternalResourcesRequired::isKnownAttribute(attrName) ||
SVGStyledElement::isKnownAttribute(attrName))
- m_masker->invalidate();
+ for (HashMap<const RenderObject*, RefPtr<SVGResourceMasker> >::iterator it = m_masker.begin(); it != m_masker.end(); ++it)
+ it->second->invalidate();
+}
+
+void SVGMaskElement::synchronizeProperty(const QualifiedName& attrName)
+{
+ SVGStyledElement::synchronizeProperty(attrName);
+
+ if (attrName == anyQName()) {
+ synchronizeMaskUnits();
+ synchronizeMaskContentUnits();
+ synchronizeX();
+ synchronizeY();
+ synchronizeExternalResourcesRequired();
+ synchronizeHref();
+ return;
+ }
+
+ if (attrName == SVGNames::maskUnitsAttr)
+ synchronizeMaskUnits();
+ else if (attrName == SVGNames::maskContentUnitsAttr)
+ synchronizeMaskContentUnits();
+ else if (attrName == SVGNames::xAttr)
+ synchronizeX();
+ else if (attrName == SVGNames::yAttr)
+ synchronizeY();
+ else if (SVGExternalResourcesRequired::isKnownAttribute(attrName))
+ synchronizeExternalResourcesRequired();
+ else if (SVGURIReference::isKnownAttribute(attrName))
+ synchronizeHref();
}
void SVGMaskElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
{
SVGStyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
- if (!m_masker)
+ if (m_masker.isEmpty())
return;
- m_masker->invalidate();
+ for (HashMap<const RenderObject*, RefPtr<SVGResourceMasker> >::iterator it = m_masker.begin(); it != m_masker.end(); ++it)
+ it->second->invalidate();
}
-PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const FloatRect& targetRect, FloatRect& maskDestRect) const
-{
- // Determine specified mask size
+FloatRect SVGMaskElement::maskBoundingBox(const FloatRect& objectBoundingBox) const
+{
+ FloatRect maskBBox;
if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
- maskDestRect = FloatRect(x().valueAsPercentage() * targetRect.width(),
- y().valueAsPercentage() * targetRect.height(),
- width().valueAsPercentage() * targetRect.width(),
- height().valueAsPercentage() * targetRect.height());
+ maskBBox = FloatRect(x().valueAsPercentage() * objectBoundingBox.width() + objectBoundingBox.x(),
+ y().valueAsPercentage() * objectBoundingBox.height() + objectBoundingBox.y(),
+ width().valueAsPercentage() * objectBoundingBox.width(),
+ height().valueAsPercentage() * objectBoundingBox.height());
else
- maskDestRect = FloatRect(x().value(this),
- y().value(this),
- width().value(this),
- height().value(this));
+ maskBBox = FloatRect(x().value(this),
+ y().value(this),
+ width().value(this),
+ height().value(this));
+
+ return maskBBox;
+}
+
+PassOwnPtr<ImageBuffer> SVGMaskElement::drawMaskerContent(const RenderObject* object, FloatRect& maskDestRect, bool& emptyMask) const
+{
+ FloatRect objectBoundingBox = object->objectBoundingBox();
+
+ // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present.
+ maskDestRect = object->repaintRectInLocalCoordinates();
+ if (maskDestRect.isEmpty()) {
+ emptyMask = true;
+ return 0;
+ }
+
+ // Calculate the smallest rect for the mask ImageBuffer.
+ FloatRect repaintRect;
+ Vector<RenderObject*> rendererList;
+ for (Node* node = firstChild(); node; node = node->nextSibling()) {
+ if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer())
+ continue;
+
+ rendererList.append(node->renderer());
+ repaintRect.unite(node->renderer()->localToParentTransform().mapRect(node->renderer()->repaintRectInLocalCoordinates()));
+ }
- IntSize imageSize(lroundf(maskDestRect.width()), lroundf(maskDestRect.height()));
- clampImageBufferSizeToViewport(document()->view(), imageSize);
+ TransformationMatrix contextTransform;
+ // We need to scale repaintRect for objectBoundingBox to get the drawing area.
+ if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
+ contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
+ FloatPoint contextAdjustment = repaintRect.location();
+ repaintRect = contextTransform.mapRect(repaintRect);
+ repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y());
+ contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y());
+ }
+ repaintRect.intersect(maskDestRect);
+ maskDestRect = repaintRect;
+ IntRect maskImageRect = enclosingIntRect(maskDestRect);
- if (imageSize.width() < static_cast<int>(maskDestRect.width()))
- maskDestRect.setWidth(imageSize.width());
+ maskImageRect.setLocation(IntPoint());
- if (imageSize.height() < static_cast<int>(maskDestRect.height()))
- maskDestRect.setHeight(imageSize.height());
+ // Don't create ImageBuffers with image size of 0
+ if (!maskImageRect.width() || !maskImageRect.height()) {
+ emptyMask = true;
+ return 0;
+ }
// FIXME: This changes color space to linearRGB, the default color space
// for masking operations in SVG. We need a switch for the other color-space
// attribute values sRGB, inherit and auto.
- OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(imageSize, LinearRGB);
+ OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB);
if (!maskImage)
return 0;
- FloatPoint maskContextLocation = maskDestRect.location();
- if (maskUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
- maskDestRect.move(targetRect.x(), targetRect.y());
- if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
- maskContextLocation.move(targetRect.x(), targetRect.y());
- } else {
- if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
- maskContextLocation.move(-targetRect.x(), -targetRect.y());
- }
-
GraphicsContext* maskImageContext = maskImage->context();
ASSERT(maskImageContext);
maskImageContext->save();
- maskImageContext->translate(-maskContextLocation.x(), -maskContextLocation.y());
- if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
- maskImageContext->save();
- maskImageContext->scale(FloatSize(targetRect.width(), targetRect.height()));
- }
+ if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
+ maskImageContext->translate(-maskDestRect.x(), -maskDestRect.y());
+ maskImageContext->concatCTM(contextTransform);
- // Render subtree into ImageBuffer
- for (Node* n = firstChild(); n; n = n->nextSibling()) {
- SVGElement* elem = 0;
- if (n->isSVGElement())
- elem = static_cast<SVGElement*>(n);
- if (!elem || !elem->isStyled())
- continue;
+ // draw the content into the ImageBuffer
+ Vector<RenderObject*>::iterator end = rendererList.end();
+ for (Vector<RenderObject*>::iterator it = rendererList.begin(); it != end; it++)
+ renderSubtreeToImage(maskImage.get(), *it);
+
+
+ maskImageContext->restore();
+
+ // create the luminance mask
+ RefPtr<ImageData> imageData(maskImage->getUnmultipliedImageData(maskImageRect));
+ CanvasPixelArray* srcPixelArray(imageData->data());
- SVGStyledElement* e = static_cast<SVGStyledElement*>(elem);
- RenderObject* item = e->renderer();
- if (!item)
+ for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset += 4) {
+ unsigned char a = srcPixelArray->get(pixelOffset + 3);
+ if (!a)
continue;
+ unsigned char r = srcPixelArray->get(pixelOffset);
+ unsigned char g = srcPixelArray->get(pixelOffset + 1);
+ unsigned char b = srcPixelArray->get(pixelOffset + 2);
- renderSubtreeToImage(maskImage.get(), item);
+ double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
+ srcPixelArray->set(pixelOffset + 3, luma);
}
- if (maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
- maskImageContext->restore();
+ maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, IntPoint());
- maskImageContext->restore();
return maskImage.release();
}
@@ -207,11 +271,18 @@ RenderObject* SVGMaskElement::createRenderer(RenderArena* arena, RenderStyle*)
return maskContainer;
}
-SVGResource* SVGMaskElement::canvasResource()
+SVGResource* SVGMaskElement::canvasResource(const RenderObject* object)
{
- if (!m_masker)
- m_masker = SVGResourceMasker::create(this);
- return m_masker.get();
+ ASSERT(object);
+
+ if (m_masker.contains(object))
+ return m_masker.get(object).get();
+
+ RefPtr<SVGResourceMasker> masker = SVGResourceMasker::create(this);
+ SVGResourceMasker* maskerPtr = masker.get();
+ m_masker.set(object, masker.release());
+
+ return maskerPtr;
}
}