Home | History | Annotate | Download | only in svg
      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 #include "config.h"
     21 
     22 #if ENABLE(SVG)
     23 #include "RenderSVGResourceMasker.h"
     24 
     25 #include "AffineTransform.h"
     26 #include "Element.h"
     27 #include "FloatPoint.h"
     28 #include "FloatRect.h"
     29 #include "GraphicsContext.h"
     30 #include "Image.h"
     31 #include "ImageBuffer.h"
     32 #include "IntRect.h"
     33 #include "RenderSVGResource.h"
     34 #include "SVGElement.h"
     35 #include "SVGImageBufferTools.h"
     36 #include "SVGMaskElement.h"
     37 #include "SVGStyledElement.h"
     38 #include "SVGUnitTypes.h"
     39 
     40 #include <wtf/ByteArray.h>
     41 #include <wtf/UnusedParam.h>
     42 #include <wtf/Vector.h>
     43 
     44 namespace WebCore {
     45 
     46 RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType;
     47 
     48 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
     49     : RenderSVGResourceContainer(node)
     50 {
     51 }
     52 
     53 RenderSVGResourceMasker::~RenderSVGResourceMasker()
     54 {
     55     if (m_masker.isEmpty())
     56         return;
     57 
     58     deleteAllValues(m_masker);
     59     m_masker.clear();
     60 }
     61 
     62 void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation)
     63 {
     64     m_maskContentBoundaries = FloatRect();
     65     if (!m_masker.isEmpty()) {
     66         deleteAllValues(m_masker);
     67         m_masker.clear();
     68     }
     69 
     70     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     71 }
     72 
     73 void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     74 {
     75     ASSERT(client);
     76 
     77     if (m_masker.contains(client))
     78         delete m_masker.take(client);
     79 
     80     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     81 }
     82 
     83 bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
     84 {
     85     ASSERT(object);
     86     ASSERT(context);
     87 #ifndef NDEBUG
     88     ASSERT(resourceMode == ApplyToDefaultMode);
     89 #else
     90     UNUSED_PARAM(resourceMode);
     91 #endif
     92 
     93     if (!m_masker.contains(object))
     94         m_masker.set(object, new MaskerData);
     95 
     96     MaskerData* maskerData = m_masker.get(object);
     97 
     98     AffineTransform absoluteTransform;
     99     SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform);
    100 
    101     FloatRect absoluteTargetRect = absoluteTransform.mapRect(object->repaintRectInLocalCoordinates());
    102     FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect);
    103 
    104     if (!maskerData->maskImage && !clampedAbsoluteTargetRect.isEmpty()) {
    105         SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
    106         if (!maskElement)
    107             return false;
    108 
    109         if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, ColorSpaceLinearRGB))
    110             return false;
    111 
    112         GraphicsContext* maskImageContext = maskerData->maskImage->context();
    113         ASSERT(maskImageContext);
    114 
    115         // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
    116         maskImageContext->save();
    117         maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
    118         maskImageContext->concatCTM(absoluteTransform);
    119 
    120         drawContentIntoMaskImage(maskerData, maskElement, object);
    121     }
    122 
    123     if (!maskerData->maskImage)
    124         return false;
    125 
    126     SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, maskerData->maskImage);
    127     return true;
    128 }
    129 
    130 void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object)
    131 {
    132     GraphicsContext* maskImageContext = maskerData->maskImage->context();
    133     ASSERT(maskImageContext);
    134 
    135     // Eventually adjust the mask image context according to the target objectBoundingBox.
    136     AffineTransform maskContentTransformation;
    137     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    138         FloatRect objectBoundingBox = object->objectBoundingBox();
    139         maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
    140         maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    141         maskImageContext->concatCTM(maskContentTransformation);
    142     }
    143 
    144     // Draw the content into the ImageBuffer.
    145     for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
    146         RenderObject* renderer = node->renderer();
    147         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer)
    148             continue;
    149         RenderStyle* style = renderer->style();
    150         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
    151             continue;
    152         SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
    153     }
    154 
    155     maskImageContext->restore();
    156 
    157 #if !USE(CG)
    158     maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
    159 #endif
    160 
    161     // Create the luminance mask.
    162     IntRect maskImageRect(IntPoint(), maskerData->maskImage->size());
    163     RefPtr<ByteArray> srcPixelArray = maskerData->maskImage->getUnmultipliedImageData(maskImageRect);
    164 
    165     unsigned pixelArrayLength = srcPixelArray->length();
    166     for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) {
    167         unsigned char a = srcPixelArray->get(pixelOffset + 3);
    168         if (!a)
    169             continue;
    170         unsigned char r = srcPixelArray->get(pixelOffset);
    171         unsigned char g = srcPixelArray->get(pixelOffset + 1);
    172         unsigned char b = srcPixelArray->get(pixelOffset + 2);
    173 
    174         double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
    175         srcPixelArray->set(pixelOffset + 3, luma);
    176     }
    177 
    178     maskerData->maskImage->putUnmultipliedImageData(srcPixelArray.get(), maskImageRect.size(), maskImageRect, IntPoint());
    179 }
    180 
    181 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
    182 {
    183     for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
    184         RenderObject* renderer = childNode->renderer();
    185         if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
    186             continue;
    187         RenderStyle* style = renderer->style();
    188         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
    189              continue;
    190         m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
    191     }
    192 }
    193 
    194 FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
    195 {
    196     SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
    197     ASSERT(maskElement);
    198 
    199     FloatRect objectBoundingBox = object->objectBoundingBox();
    200     FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox);
    201 
    202     // Resource was not layouted yet. Give back clipping rect of the mask.
    203     if (selfNeedsLayout())
    204         return maskBoundaries;
    205 
    206     if (m_maskContentBoundaries.isEmpty())
    207         calculateMaskContentRepaintRect();
    208 
    209     FloatRect maskRect = m_maskContentBoundaries;
    210     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    211         AffineTransform transform;
    212         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
    213         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    214         maskRect = transform.mapRect(maskRect);
    215     }
    216 
    217     maskRect.intersect(maskBoundaries);
    218     return maskRect;
    219 }
    220 
    221 }
    222 
    223 #endif // ENABLE(SVG)
    224