WebCore/rendering/RenderSVGResourceMasker.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved.
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public License
       
    15  * along with this library; see the file COPYING.LIB.  If not, write to
       
    16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    17  * Boston, MA 02110-1301, USA.
       
    18  *
       
    19  */
       
    20 
       
    21 #include "config.h"
       
    22 #if ENABLE(SVG)
       
    23 #include "RenderSVGResourceMasker.h"
       
    24 
       
    25 #include "AffineTransform.h"
       
    26 #include "CanvasPixelArray.h"
       
    27 #include "Element.h"
       
    28 #include "FloatPoint.h"
       
    29 #include "FloatRect.h"
       
    30 #include "GraphicsContext.h"
       
    31 #include "Image.h"
       
    32 #include "ImageBuffer.h"
       
    33 #include "ImageData.h"
       
    34 #include "IntRect.h"
       
    35 #include "RenderSVGResource.h"
       
    36 #include "SVGElement.h"
       
    37 #include "SVGMaskElement.h"
       
    38 #include "SVGStyledElement.h"
       
    39 #include "SVGUnitTypes.h"
       
    40 #include <wtf/Vector.h>
       
    41 #include <wtf/UnusedParam.h>
       
    42 
       
    43 namespace WebCore {
       
    44 
       
    45 RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType;
       
    46 
       
    47 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
       
    48     : RenderSVGResourceContainer(node)
       
    49 {
       
    50 }
       
    51 
       
    52 RenderSVGResourceMasker::~RenderSVGResourceMasker()
       
    53 {
       
    54     deleteAllValues(m_masker);
       
    55     m_masker.clear();
       
    56 }
       
    57 
       
    58 void RenderSVGResourceMasker::invalidateClients()
       
    59 {
       
    60     HashMap<RenderObject*, MaskerData*>::const_iterator end = m_masker.end();
       
    61     for (HashMap<RenderObject*, MaskerData*>::const_iterator it = m_masker.begin(); it != end; ++it)
       
    62         markForLayoutAndResourceInvalidation(it->first);
       
    63 
       
    64     deleteAllValues(m_masker);
       
    65     m_masker.clear();
       
    66     m_maskBoundaries = FloatRect();
       
    67 }
       
    68 
       
    69 void RenderSVGResourceMasker::invalidateClient(RenderObject* object)
       
    70 {
       
    71     ASSERT(object);
       
    72     if (!m_masker.contains(object))
       
    73         return;
       
    74 
       
    75     delete m_masker.take(object);
       
    76     markForLayoutAndResourceInvalidation(object);
       
    77 }
       
    78 
       
    79 bool RenderSVGResourceMasker::childElementReferencesResource(const SVGRenderStyle* style, const String& referenceId) const
       
    80 {
       
    81     if (!style->hasMasker())
       
    82         return false;
       
    83 
       
    84     return style->maskerResource() == referenceId;
       
    85 }
       
    86 
       
    87 bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
       
    88 {
       
    89     ASSERT(object);
       
    90     ASSERT(context);
       
    91 #ifndef NDEBUG
       
    92     ASSERT(resourceMode == ApplyToDefaultMode);
       
    93 #else
       
    94     UNUSED_PARAM(resourceMode);
       
    95 #endif
       
    96 
       
    97     if (!m_masker.contains(object))
       
    98         m_masker.set(object, new MaskerData);
       
    99 
       
   100     MaskerData* maskerData = m_masker.get(object);
       
   101     if (!maskerData->maskImage && !maskerData->emptyMask) {
       
   102         SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
       
   103         if (!maskElement)
       
   104             return false;
       
   105 
       
   106         // Early exit, if this resource contains a child which references ourselves.
       
   107         if (containsCyclicReference(node()))
       
   108             return false;
       
   109 
       
   110         createMaskImage(maskerData, maskElement, object);
       
   111     }
       
   112 
       
   113     if (!maskerData->maskImage)
       
   114         return false;
       
   115 
       
   116     context->clipToImageBuffer(maskerData->maskRect, maskerData->maskImage.get());
       
   117     return true;
       
   118 }
       
   119 
       
   120 void RenderSVGResourceMasker::createMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object)
       
   121 {
       
   122     FloatRect objectBoundingBox = object->objectBoundingBox();
       
   123 
       
   124     // Mask rect clipped with clippingBoundingBox and filterBoundingBox as long as they are present.
       
   125     maskerData->maskRect = object->repaintRectInLocalCoordinates();
       
   126     if (maskerData->maskRect.isEmpty()) {
       
   127         maskerData->emptyMask = true;
       
   128         return;
       
   129     }
       
   130     
       
   131     if (m_maskBoundaries.isEmpty())
       
   132         calculateMaskContentRepaintRect();
       
   133 
       
   134     FloatRect repaintRect = m_maskBoundaries;
       
   135     AffineTransform contextTransform;
       
   136     // We need to scale repaintRect for objectBoundingBox to get the drawing area.
       
   137     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
       
   138         contextTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
       
   139         FloatPoint contextAdjustment = repaintRect.location();
       
   140         repaintRect = contextTransform.mapRect(repaintRect);
       
   141         repaintRect.move(objectBoundingBox.x(), objectBoundingBox.y());
       
   142         contextTransform.translate(-contextAdjustment.x(), -contextAdjustment.y());
       
   143     }
       
   144     repaintRect.intersect(maskerData->maskRect);
       
   145     maskerData->maskRect = repaintRect;
       
   146     IntRect maskImageRect = enclosingIntRect(maskerData->maskRect);
       
   147 
       
   148     maskImageRect.setLocation(IntPoint());
       
   149 
       
   150     // Don't create ImageBuffers with image size of 0
       
   151     if (maskImageRect.isEmpty()) {
       
   152         maskerData->emptyMask = true;
       
   153         return;
       
   154     }
       
   155 
       
   156     // FIXME: This changes color space to linearRGB, the default color space
       
   157     // for masking operations in SVG. We need a switch for the other color-space
       
   158     // attribute values sRGB, inherit and auto.
       
   159     maskerData->maskImage = ImageBuffer::create(maskImageRect.size(), LinearRGB);
       
   160     if (!maskerData->maskImage)
       
   161         return;
       
   162 
       
   163     GraphicsContext* maskImageContext = maskerData->maskImage->context();
       
   164     ASSERT(maskImageContext);
       
   165 
       
   166     maskImageContext->save();
       
   167 
       
   168     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE)
       
   169         maskImageContext->translate(-maskerData->maskRect.x(), -maskerData->maskRect.y());
       
   170     maskImageContext->concatCTM(contextTransform);
       
   171 
       
   172     // draw the content into the ImageBuffer
       
   173     for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
       
   174         RenderObject* renderer = node->renderer();
       
   175         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer)
       
   176             continue;
       
   177         RenderStyle* style = renderer->style();
       
   178         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
       
   179             continue;
       
   180         SVGRenderSupport::renderSubtreeToImage(maskerData->maskImage.get(), renderer);
       
   181     }
       
   182 
       
   183     maskImageContext->restore();
       
   184 
       
   185 #if !PLATFORM(CG)
       
   186     maskerData->maskImage->transformColorSpace(DeviceRGB, LinearRGB);
       
   187 #endif
       
   188 
       
   189     // create the luminance mask
       
   190     RefPtr<ImageData> imageData(maskerData->maskImage->getUnmultipliedImageData(maskImageRect));
       
   191     CanvasPixelArray* srcPixelArray(imageData->data());
       
   192 
       
   193     for (unsigned pixelOffset = 0; pixelOffset < srcPixelArray->length(); pixelOffset += 4) {
       
   194         unsigned char a = srcPixelArray->get(pixelOffset + 3);
       
   195         if (!a)
       
   196             continue;
       
   197         unsigned char r = srcPixelArray->get(pixelOffset);
       
   198         unsigned char g = srcPixelArray->get(pixelOffset + 1);
       
   199         unsigned char b = srcPixelArray->get(pixelOffset + 2);
       
   200 
       
   201         double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
       
   202         srcPixelArray->set(pixelOffset + 3, luma);
       
   203     }
       
   204 
       
   205     maskerData->maskImage->putUnmultipliedImageData(imageData.get(), maskImageRect, IntPoint());
       
   206 }
       
   207 
       
   208 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
       
   209 {
       
   210     for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
       
   211         RenderObject* renderer = childNode->renderer();
       
   212         if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
       
   213             continue;
       
   214         RenderStyle* style = renderer->style();
       
   215         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
       
   216              continue;
       
   217         m_maskBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
       
   218     }
       
   219 }
       
   220 
       
   221 FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
       
   222 {
       
   223     // Resource was not layouted yet. Give back clipping rect of the mask.
       
   224     SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
       
   225     FloatRect objectBoundingBox = object->objectBoundingBox();
       
   226     FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox);
       
   227     if (selfNeedsLayout())
       
   228         return maskBoundaries;
       
   229 
       
   230     if (m_maskBoundaries.isEmpty())
       
   231         calculateMaskContentRepaintRect();
       
   232 
       
   233     if (!maskElement)
       
   234         return FloatRect();
       
   235 
       
   236     FloatRect maskRect = m_maskBoundaries;
       
   237     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
       
   238         AffineTransform transform;
       
   239         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
       
   240         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
       
   241         maskRect =  transform.mapRect(maskRect);
       
   242     }
       
   243 
       
   244     maskRect.intersect(maskBoundaries);
       
   245     return maskRect;
       
   246 }
       
   247 
       
   248 }
       
   249 
       
   250 #endif // ENABLE(SVG)