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 #include "core/rendering/svg/RenderSVGResourceMasker.h"
     23 
     24 #include "core/rendering/svg/RenderSVGResource.h"
     25 #include "core/rendering/svg/SVGRenderingContext.h"
     26 #include "core/svg/SVGElement.h"
     27 #include "platform/graphics/DisplayList.h"
     28 #include "platform/graphics/GraphicsContextStateSaver.h"
     29 #include "platform/transforms/AffineTransform.h"
     30 #include "wtf/Vector.h"
     31 
     32 namespace WebCore {
     33 
     34 const RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType;
     35 
     36 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
     37     : RenderSVGResourceContainer(node)
     38 {
     39 }
     40 
     41 RenderSVGResourceMasker::~RenderSVGResourceMasker()
     42 {
     43 }
     44 
     45 void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation)
     46 {
     47     m_maskContentDisplayList.clear();
     48     m_maskContentBoundaries = FloatRect();
     49     markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
     50 }
     51 
     52 void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
     53 {
     54     ASSERT(client);
     55     markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
     56 }
     57 
     58 bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*,
     59     GraphicsContext*& context, unsigned short resourceMode)
     60 {
     61     ASSERT(object);
     62     ASSERT(context);
     63     ASSERT(style());
     64     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
     65     ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
     66 
     67     clearInvalidationMask();
     68 
     69     FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates();
     70     if (repaintRect.isEmpty() || !element()->hasChildren())
     71         return false;
     72 
     73     // Content layer start.
     74     context->beginTransparencyLayer(1, &repaintRect);
     75 
     76     return true;
     77 }
     78 
     79 void RenderSVGResourceMasker::postApplyResource(RenderObject* object, GraphicsContext*& context,
     80     unsigned short resourceMode, const Path*, const RenderSVGShape*)
     81 {
     82     ASSERT(object);
     83     ASSERT(context);
     84     ASSERT(style());
     85     ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
     86     ASSERT_WITH_SECURITY_IMPLICATION(!needsLayout());
     87 
     88     FloatRect repaintRect = object->paintInvalidationRectInLocalCoordinates();
     89 
     90     const SVGRenderStyle* svgStyle = style()->svgStyle();
     91     ASSERT(svgStyle);
     92     ColorFilter maskLayerFilter = svgStyle->maskType() == MT_LUMINANCE
     93         ? ColorFilterLuminanceToAlpha : ColorFilterNone;
     94     ColorFilter maskContentFilter = svgStyle->colorInterpolation() == CI_LINEARRGB
     95         ? ColorFilterSRGBToLinearRGB : ColorFilterNone;
     96 
     97     // Mask layer start.
     98     context->beginLayer(1, CompositeDestinationIn, &repaintRect, maskLayerFilter);
     99     {
    100         // Draw the mask with color conversion (when needed).
    101         GraphicsContextStateSaver maskContentSaver(*context);
    102         context->setColorFilter(maskContentFilter);
    103 
    104         drawMaskForRenderer(context, object->objectBoundingBox());
    105     }
    106 
    107     // Transfer mask layer -> content layer (DstIn)
    108     context->endLayer();
    109     // Transfer content layer -> backdrop (SrcOver)
    110     context->endLayer();
    111 }
    112 
    113 void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, const FloatRect& targetBoundingBox)
    114 {
    115     ASSERT(context);
    116 
    117     AffineTransform contentTransformation;
    118     SVGUnitTypes::SVGUnitType contentUnits = toSVGMaskElement(element())->maskContentUnits()->currentValue()->enumValue();
    119     if (contentUnits == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    120         contentTransformation.translate(targetBoundingBox.x(), targetBoundingBox.y());
    121         contentTransformation.scaleNonUniform(targetBoundingBox.width(), targetBoundingBox.height());
    122         context->concatCTM(contentTransformation);
    123     }
    124 
    125     if (!m_maskContentDisplayList)
    126         m_maskContentDisplayList = asDisplayList(context, contentTransformation);
    127     ASSERT(m_maskContentDisplayList);
    128     context->drawDisplayList(m_maskContentDisplayList.get());
    129 }
    130 
    131 PassRefPtr<DisplayList> RenderSVGResourceMasker::asDisplayList(GraphicsContext* context,
    132     const AffineTransform& contentTransform)
    133 {
    134     ASSERT(context);
    135 
    136     // Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection
    137     // with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and
    138     // userSpaceOnUse units (http://crbug.com/294900).
    139     context->beginRecording(strokeBoundingBox());
    140     for (Element* childElement = ElementTraversal::firstWithin(*element()); childElement; childElement = ElementTraversal::nextSibling(*childElement)) {
    141         RenderObject* renderer = childElement->renderer();
    142         if (!childElement->isSVGElement() || !renderer)
    143             continue;
    144         RenderStyle* style = renderer->style();
    145         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
    146             continue;
    147 
    148         SVGRenderingContext::renderSubtree(context, renderer, contentTransform);
    149     }
    150 
    151     return context->endRecording();
    152 }
    153 
    154 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
    155 {
    156     for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
    157         RenderObject* renderer = childElement->renderer();
    158         if (!renderer)
    159             continue;
    160         RenderStyle* style = renderer->style();
    161         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
    162              continue;
    163         m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->paintInvalidationRectInLocalCoordinates()));
    164     }
    165 }
    166 
    167 FloatRect RenderSVGResourceMasker::resourceBoundingBox(const RenderObject* object)
    168 {
    169     SVGMaskElement* maskElement = toSVGMaskElement(element());
    170     ASSERT(maskElement);
    171 
    172     FloatRect objectBoundingBox = object->objectBoundingBox();
    173     FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits()->currentValue()->enumValue(), objectBoundingBox);
    174 
    175     // Resource was not layouted yet. Give back clipping rect of the mask.
    176     if (selfNeedsLayout())
    177         return maskBoundaries;
    178 
    179     if (m_maskContentBoundaries.isEmpty())
    180         calculateMaskContentRepaintRect();
    181 
    182     FloatRect maskRect = m_maskContentBoundaries;
    183     if (maskElement->maskContentUnits()->currentValue()->value() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
    184         AffineTransform transform;
    185         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
    186         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
    187         maskRect = transform.mapRect(maskRect);
    188     }
    189 
    190     maskRect.intersect(maskBoundaries);
    191     return maskRect;
    192 }
    193 
    194 }
    195