2011-11-10 Nikolas Zimmermann <nzimmermann@rim.com>
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGResourceMasker.cpp
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::clampedAbsoluteTargetRect(absoluteTargetRect);
103
104     if (!maskerData->maskImage && !clampedAbsoluteTargetRect.isEmpty()) {
105         SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
106         if (!maskElement)
107             return false;
108
109         ASSERT(style());
110         const SVGRenderStyle* svgStyle = style()->svgStyle();
111         ASSERT(svgStyle);
112         ColorSpace colorSpace = svgStyle->colorInterpolation() == CI_LINEARRGB ? ColorSpaceLinearRGB : ColorSpaceDeviceRGB;
113         if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, colorSpace))
114             return false;
115
116         GraphicsContext* maskImageContext = maskerData->maskImage->context();
117         ASSERT(maskImageContext);
118
119         // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
120         maskImageContext->save();
121         maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
122         maskImageContext->concatCTM(absoluteTransform);
123
124         drawContentIntoMaskImage(maskerData, colorSpace, maskElement, object);
125     }
126
127     if (!maskerData->maskImage)
128         return false;
129
130     SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, maskerData->maskImage);
131     return true;
132 }
133
134 void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, ColorSpace colorSpace, const SVGMaskElement* maskElement, RenderObject* object)
135 {
136     GraphicsContext* maskImageContext = maskerData->maskImage->context();
137     ASSERT(maskImageContext);
138
139     // Eventually adjust the mask image context according to the target objectBoundingBox.
140     AffineTransform maskContentTransformation;
141     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
142         FloatRect objectBoundingBox = object->objectBoundingBox();
143         maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
144         maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
145         maskImageContext->concatCTM(maskContentTransformation);
146     }
147
148     // Draw the content into the ImageBuffer.
149     for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
150         RenderObject* renderer = node->renderer();
151         if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer)
152             continue;
153         RenderStyle* style = renderer->style();
154         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
155             continue;
156         SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
157     }
158
159     maskImageContext->restore();
160
161 #if !USE(CG)
162     maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, colorSpace);
163 #else
164     UNUSED_PARAM(colorSpace);
165 #endif
166
167     // Create the luminance mask.
168     maskerData->maskImage->convertToLuminanceMask();
169 }
170
171 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
172 {
173     for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
174         RenderObject* renderer = childNode->renderer();
175         if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
176             continue;
177         RenderStyle* style = renderer->style();
178         if (!style || style->display() == NONE || style->visibility() != VISIBLE)
179              continue;
180         m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
181     }
182 }
183
184 FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
185 {
186     SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
187     ASSERT(maskElement);
188
189     FloatRect objectBoundingBox = object->objectBoundingBox();
190     FloatRect maskBoundaries = SVGLengthContext::resolveRectangle<SVGMaskElement>(maskElement, maskElement->maskUnits(), objectBoundingBox);
191
192     // Resource was not layouted yet. Give back clipping rect of the mask.
193     if (selfNeedsLayout())
194         return maskBoundaries;
195
196     if (m_maskContentBoundaries.isEmpty())
197         calculateMaskContentRepaintRect();
198
199     FloatRect maskRect = m_maskContentBoundaries;
200     if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
201         AffineTransform transform;
202         transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
203         transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
204         maskRect = transform.mapRect(maskRect);
205     }
206
207     maskRect.intersect(maskBoundaries);
208     return maskRect;
209 }
210
211 }
212
213 #endif // ENABLE(SVG)