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