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 "SVGMaskElement.h"
36#include "SVGRenderingContext.h"
37#include "SVGStyledElement.h"
38#include "SVGUnitTypes.h"
39#include <wtf/Vector.h>
40
41namespace WebCore {
42
43RenderSVGResourceType RenderSVGResourceMasker::s_resourceType = MaskerResourceType;
44
45RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
46    : RenderSVGResourceContainer(node)
47{
48}
49
50RenderSVGResourceMasker::~RenderSVGResourceMasker()
51{
52    if (m_masker.isEmpty())
53        return;
54
55    deleteAllValues(m_masker);
56    m_masker.clear();
57}
58
59void RenderSVGResourceMasker::removeAllClientsFromCache(bool markForInvalidation)
60{
61    m_maskContentBoundaries = FloatRect();
62    if (!m_masker.isEmpty()) {
63        deleteAllValues(m_masker);
64        m_masker.clear();
65    }
66
67    markAllClientsForInvalidation(markForInvalidation ? LayoutAndBoundariesInvalidation : ParentOnlyInvalidation);
68}
69
70void RenderSVGResourceMasker::removeClientFromCache(RenderObject* client, bool markForInvalidation)
71{
72    ASSERT(client);
73
74    if (m_masker.contains(client))
75        delete m_masker.take(client);
76
77    markClientForInvalidation(client, markForInvalidation ? BoundariesInvalidation : ParentOnlyInvalidation);
78}
79
80bool RenderSVGResourceMasker::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode)
81{
82    ASSERT(object);
83    ASSERT(context);
84    ASSERT_UNUSED(resourceMode, resourceMode == ApplyToDefaultMode);
85
86    bool missingMaskerData = !m_masker.contains(object);
87    if (missingMaskerData)
88        m_masker.set(object, new MaskerData);
89
90    MaskerData* maskerData = m_masker.get(object);
91
92    AffineTransform absoluteTransform;
93    SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(object, absoluteTransform);
94
95    FloatRect repaintRect = object->repaintRectInLocalCoordinates();
96
97    if (!maskerData->maskImage && !repaintRect.isEmpty()) {
98        SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
99        if (!maskElement)
100            return false;
101
102        ASSERT(style());
103        const SVGRenderStyle* svgStyle = style()->svgStyle();
104        ASSERT(svgStyle);
105        ColorSpace colorSpace = svgStyle->colorInterpolation() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB;
106        if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, maskerData->maskImage, colorSpace, Unaccelerated))
107            return false;
108
109        if (!drawContentIntoMaskImage(maskerData, colorSpace, maskElement, object)) {
110            maskerData->maskImage.clear();
111        }
112    }
113
114    if (!maskerData->maskImage)
115        return false;
116
117    SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, repaintRect, maskerData->maskImage, missingMaskerData);
118    return true;
119}
120
121bool RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, ColorSpace colorSpace, const SVGMaskElement* maskElement, RenderObject* object)
122{
123    GraphicsContext* maskImageContext = maskerData->maskImage->context();
124    ASSERT(maskImageContext);
125
126    // Eventually adjust the mask image context according to the target objectBoundingBox.
127    AffineTransform maskContentTransformation;
128    if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
129        FloatRect objectBoundingBox = object->objectBoundingBox();
130        maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
131        maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
132        maskImageContext->concatCTM(maskContentTransformation);
133    }
134
135    // Draw the content into the ImageBuffer.
136    for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
137        RenderObject* renderer = node->renderer();
138        if (!node->isSVGElement() || !toSVGElement(node)->isSVGStyledElement() || !renderer)
139            continue;
140        if (renderer->needsLayout())
141            return false;
142        RenderStyle* style = renderer->style();
143        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
144            continue;
145        SVGRenderingContext::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
146    }
147
148#if !USE(CG)
149    maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, colorSpace);
150#else
151    UNUSED_PARAM(colorSpace);
152#endif
153
154    ASSERT(style());
155    ASSERT(style()->svgStyle());
156    // Create the luminance mask.
157    if (style()->svgStyle()->maskType() == MT_LUMINANCE)
158        maskerData->maskImage->convertToLuminanceMask();
159
160    return true;
161}
162
163void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
164{
165    for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
166        RenderObject* renderer = childNode->renderer();
167        if (!childNode->isSVGElement() || !toSVGElement(childNode)->isSVGStyledElement() || !renderer)
168            continue;
169        RenderStyle* style = renderer->style();
170        if (!style || style->display() == NONE || style->visibility() != VISIBLE)
171             continue;
172        m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
173    }
174}
175
176FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
177{
178    SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
179    ASSERT(maskElement);
180
181    FloatRect objectBoundingBox = object->objectBoundingBox();
182    FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits(), objectBoundingBox);
183
184    // Resource was not layouted yet. Give back clipping rect of the mask.
185    if (selfNeedsLayout())
186        return maskBoundaries;
187
188    if (m_maskContentBoundaries.isEmpty())
189        calculateMaskContentRepaintRect();
190
191    FloatRect maskRect = m_maskContentBoundaries;
192    if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
193        AffineTransform transform;
194        transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
195        transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
196        maskRect = transform.mapRect(maskRect);
197    }
198
199    maskRect.intersect(maskBoundaries);
200    return maskRect;
201}
202
203}
204
205#endif // ENABLE(SVG)
206