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