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
32namespace WebCore {
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 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
79void 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
113void 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
131PassRefPtr<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
154void 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
167FloatRect 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