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
RenderSVGResourceMasker(SVGMaskElement * node)48 RenderSVGResourceMasker::RenderSVGResourceMasker(SVGMaskElement* node)
49 : RenderSVGResourceContainer(node)
50 {
51 }
52
~RenderSVGResourceMasker()53 RenderSVGResourceMasker::~RenderSVGResourceMasker()
54 {
55 if (m_masker.isEmpty())
56 return;
57
58 deleteAllValues(m_masker);
59 m_masker.clear();
60 }
61
removeAllClientsFromCache(bool markForInvalidation)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
removeClientFromCache(RenderObject * client,bool markForInvalidation)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
applyResource(RenderObject * object,RenderStyle *,GraphicsContext * & context,unsigned short resourceMode)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::clampedAbsoluteTargetRectForRenderer(object, absoluteTargetRect);
103
104 if (!maskerData->maskImage && !clampedAbsoluteTargetRect.isEmpty()) {
105 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
106 if (!maskElement)
107 return false;
108
109 if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskerData->maskImage, ColorSpaceLinearRGB))
110 return false;
111
112 GraphicsContext* maskImageContext = maskerData->maskImage->context();
113 ASSERT(maskImageContext);
114
115 // The save/restore pair is needed for clipToImageBuffer - it doesn't work without it on non-Cg platforms.
116 maskImageContext->save();
117 maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
118 maskImageContext->concatCTM(absoluteTransform);
119
120 drawContentIntoMaskImage(maskerData, maskElement, object);
121 }
122
123 if (!maskerData->maskImage)
124 return false;
125
126 SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, maskerData->maskImage);
127 return true;
128 }
129
drawContentIntoMaskImage(MaskerData * maskerData,const SVGMaskElement * maskElement,RenderObject * object)130 void RenderSVGResourceMasker::drawContentIntoMaskImage(MaskerData* maskerData, const SVGMaskElement* maskElement, RenderObject* object)
131 {
132 GraphicsContext* maskImageContext = maskerData->maskImage->context();
133 ASSERT(maskImageContext);
134
135 // Eventually adjust the mask image context according to the target objectBoundingBox.
136 AffineTransform maskContentTransformation;
137 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
138 FloatRect objectBoundingBox = object->objectBoundingBox();
139 maskContentTransformation.translate(objectBoundingBox.x(), objectBoundingBox.y());
140 maskContentTransformation.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
141 maskImageContext->concatCTM(maskContentTransformation);
142 }
143
144 // Draw the content into the ImageBuffer.
145 for (Node* node = maskElement->firstChild(); node; node = node->nextSibling()) {
146 RenderObject* renderer = node->renderer();
147 if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !renderer)
148 continue;
149 RenderStyle* style = renderer->style();
150 if (!style || style->display() == NONE || style->visibility() != VISIBLE)
151 continue;
152 SVGImageBufferTools::renderSubtreeToImageBuffer(maskerData->maskImage.get(), renderer, maskContentTransformation);
153 }
154
155 maskImageContext->restore();
156
157 #if !USE(CG)
158 maskerData->maskImage->transformColorSpace(ColorSpaceDeviceRGB, ColorSpaceLinearRGB);
159 #endif
160
161 // Create the luminance mask.
162 IntRect maskImageRect(IntPoint(), maskerData->maskImage->size());
163 RefPtr<ByteArray> srcPixelArray = maskerData->maskImage->getUnmultipliedImageData(maskImageRect);
164
165 unsigned pixelArrayLength = srcPixelArray->length();
166 for (unsigned pixelOffset = 0; pixelOffset < pixelArrayLength; pixelOffset += 4) {
167 unsigned char a = srcPixelArray->get(pixelOffset + 3);
168 if (!a)
169 continue;
170 unsigned char r = srcPixelArray->get(pixelOffset);
171 unsigned char g = srcPixelArray->get(pixelOffset + 1);
172 unsigned char b = srcPixelArray->get(pixelOffset + 2);
173
174 double luma = (r * 0.2125 + g * 0.7154 + b * 0.0721) * ((double)a / 255.0);
175 srcPixelArray->set(pixelOffset + 3, luma);
176 }
177
178 maskerData->maskImage->putUnmultipliedImageData(srcPixelArray.get(), maskImageRect.size(), maskImageRect, IntPoint());
179 }
180
calculateMaskContentRepaintRect()181 void RenderSVGResourceMasker::calculateMaskContentRepaintRect()
182 {
183 for (Node* childNode = node()->firstChild(); childNode; childNode = childNode->nextSibling()) {
184 RenderObject* renderer = childNode->renderer();
185 if (!childNode->isSVGElement() || !static_cast<SVGElement*>(childNode)->isStyled() || !renderer)
186 continue;
187 RenderStyle* style = renderer->style();
188 if (!style || style->display() == NONE || style->visibility() != VISIBLE)
189 continue;
190 m_maskContentBoundaries.unite(renderer->localToParentTransform().mapRect(renderer->repaintRectInLocalCoordinates()));
191 }
192 }
193
resourceBoundingBox(RenderObject * object)194 FloatRect RenderSVGResourceMasker::resourceBoundingBox(RenderObject* object)
195 {
196 SVGMaskElement* maskElement = static_cast<SVGMaskElement*>(node());
197 ASSERT(maskElement);
198
199 FloatRect objectBoundingBox = object->objectBoundingBox();
200 FloatRect maskBoundaries = maskElement->maskBoundingBox(objectBoundingBox);
201
202 // Resource was not layouted yet. Give back clipping rect of the mask.
203 if (selfNeedsLayout())
204 return maskBoundaries;
205
206 if (m_maskContentBoundaries.isEmpty())
207 calculateMaskContentRepaintRect();
208
209 FloatRect maskRect = m_maskContentBoundaries;
210 if (maskElement->maskContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
211 AffineTransform transform;
212 transform.translate(objectBoundingBox.x(), objectBoundingBox.y());
213 transform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
214 maskRect = transform.mapRect(maskRect);
215 }
216
217 maskRect.intersect(maskBoundaries);
218 return maskRect;
219 }
220
221 }
222
223 #endif // ENABLE(SVG)
224