1 /*
2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
4 * Copyright (C) 2008 Dirk Schulze <krit@webkit.org>
5 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include "config.h"
24
25 #if ENABLE(SVG)
26 #include "RenderSVGResourceGradient.h"
27
28 #include "GradientAttributes.h"
29 #include "GraphicsContext.h"
30 #include "RenderSVGText.h"
31 #include "SVGImageBufferTools.h"
32 #include "SVGRenderSupport.h"
33 #include <wtf/UnusedParam.h>
34
35 namespace WebCore {
36
RenderSVGResourceGradient(SVGGradientElement * node)37 RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement* node)
38 : RenderSVGResourceContainer(node)
39 , m_shouldCollectGradientAttributes(true)
40 #if USE(CG)
41 , m_savedContext(0)
42 #endif
43 {
44 }
45
~RenderSVGResourceGradient()46 RenderSVGResourceGradient::~RenderSVGResourceGradient()
47 {
48 if (m_gradient.isEmpty())
49 return;
50
51 deleteAllValues(m_gradient);
52 m_gradient.clear();
53 }
54
removeAllClientsFromCache(bool markForInvalidation)55 void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation)
56 {
57 if (!m_gradient.isEmpty()) {
58 deleteAllValues(m_gradient);
59 m_gradient.clear();
60 }
61
62 m_shouldCollectGradientAttributes = true;
63 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
64 }
65
removeClientFromCache(RenderObject * client,bool markForInvalidation)66 void RenderSVGResourceGradient::removeClientFromCache(RenderObject* client, bool markForInvalidation)
67 {
68 ASSERT(client);
69
70 if (m_gradient.contains(client))
71 delete m_gradient.take(client);
72
73 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
74 }
75
76 #if USE(CG)
createMaskAndSwapContextForTextGradient(GraphicsContext * & context,GraphicsContext * & savedContext,OwnPtr<ImageBuffer> & imageBuffer,RenderObject * object)77 static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context,
78 GraphicsContext*& savedContext,
79 OwnPtr<ImageBuffer>& imageBuffer,
80 RenderObject* object)
81 {
82 RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
83 ASSERT(textRootBlock);
84
85 AffineTransform absoluteTransform;
86 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
87
88 FloatRect absoluteTargetRect = absoluteTransform.mapRect(textRootBlock->repaintRectInLocalCoordinates());
89 FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
90 if (clampedAbsoluteTargetRect.isEmpty())
91 return false;
92
93 OwnPtr<ImageBuffer> maskImage;
94 if (!SVGImageBufferTools::createImageBuffer(absoluteTargetRect, clampedAbsoluteTargetRect, maskImage, ColorSpaceDeviceRGB))
95 return false;
96
97 GraphicsContext* maskImageContext = maskImage->context();
98 ASSERT(maskImageContext);
99
100 maskImageContext->translate(-clampedAbsoluteTargetRect.x(), -clampedAbsoluteTargetRect.y());
101 maskImageContext->concatCTM(absoluteTransform);
102
103 ASSERT(maskImage);
104 savedContext = context;
105 context = maskImageContext;
106 imageBuffer = maskImage.release();
107 return true;
108 }
109
clipToTextMask(GraphicsContext * context,OwnPtr<ImageBuffer> & imageBuffer,FloatRect & targetRect,RenderObject * object,bool boundingBoxMode,const AffineTransform & gradientTransform)110 static inline AffineTransform clipToTextMask(GraphicsContext* context,
111 OwnPtr<ImageBuffer>& imageBuffer,
112 FloatRect& targetRect,
113 RenderObject* object,
114 bool boundingBoxMode,
115 const AffineTransform& gradientTransform)
116 {
117 RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
118 ASSERT(textRootBlock);
119
120 targetRect = textRootBlock->repaintRectInLocalCoordinates();
121
122 AffineTransform absoluteTransform;
123 SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(textRootBlock, absoluteTransform);
124
125 FloatRect absoluteTargetRect = absoluteTransform.mapRect(targetRect);
126 FloatRect clampedAbsoluteTargetRect = SVGImageBufferTools::clampedAbsoluteTargetRectForRenderer(textRootBlock, absoluteTargetRect);
127
128 SVGImageBufferTools::clipToImageBuffer(context, absoluteTransform, clampedAbsoluteTargetRect, imageBuffer);
129
130 AffineTransform matrix;
131 if (boundingBoxMode) {
132 FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
133 matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
134 matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
135 }
136 matrix *= gradientTransform;
137 return matrix;
138 }
139 #endif
140
applyResource(RenderObject * object,RenderStyle * style,GraphicsContext * & context,unsigned short resourceMode)141 bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
142 {
143 ASSERT(object);
144 ASSERT(style);
145 ASSERT(context);
146 ASSERT(resourceMode != ApplyToDefaultMode);
147
148 // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further.
149 // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property
150 // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
151 // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash.
152 SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node());
153 if (!gradientElement)
154 return false;
155
156 if (m_shouldCollectGradientAttributes) {
157 gradientElement->updateAnimatedSVGAttribute(anyQName());
158 collectGradientAttributes(gradientElement);
159 m_shouldCollectGradientAttributes = false;
160 }
161
162 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
163 // then the given effect (e.g. a gradient or a filter) will be ignored.
164 FloatRect objectBoundingBox = object->objectBoundingBox();
165 if (boundingBoxMode() && objectBoundingBox.isEmpty())
166 return false;
167
168 if (!m_gradient.contains(object))
169 m_gradient.set(object, new GradientData);
170
171 GradientData* gradientData = m_gradient.get(object);
172 bool isPaintingText = resourceMode & ApplyToTextMode;
173
174 // Create gradient object
175 if (!gradientData->gradient) {
176 buildGradient(gradientData, gradientElement);
177
178 // CG platforms will handle the gradient space transform for text after applying the
179 // resource, so don't apply it here. For non-CG platforms, we want the text bounding
180 // box applied to the gradient space transform now, so the gradient shader can use it.
181 #if USE(CG)
182 if (boundingBoxMode() && !objectBoundingBox.isEmpty() && !isPaintingText) {
183 #else
184 if (boundingBoxMode() && !objectBoundingBox.isEmpty()) {
185 #endif
186 gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
187 gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
188 }
189
190 AffineTransform gradientTransform;
191 calculateGradientTransform(gradientTransform);
192
193 gradientData->userspaceTransform *= gradientTransform;
194 gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
195 }
196
197 if (!gradientData->gradient)
198 return false;
199
200 // Draw gradient
201 context->save();
202
203 if (isPaintingText) {
204 #if USE(CG)
205 if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
206 context->restore();
207 return false;
208 }
209 #endif
210
211 context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
212 }
213
214 const SVGRenderStyle* svgStyle = style->svgStyle();
215 ASSERT(svgStyle);
216
217 if (resourceMode & ApplyToFillMode) {
218 context->setAlpha(svgStyle->fillOpacity());
219 context->setFillGradient(gradientData->gradient);
220 context->setFillRule(svgStyle->fillRule());
221 } else if (resourceMode & ApplyToStrokeMode) {
222 if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
223 gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform));
224 context->setAlpha(svgStyle->strokeOpacity());
225 context->setStrokeGradient(gradientData->gradient);
226 SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
227 }
228
229 return true;
230 }
231
232 void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path)
233 {
234 ASSERT(context);
235 ASSERT(resourceMode != ApplyToDefaultMode);
236
237 if (resourceMode & ApplyToTextMode) {
238 #if USE(CG)
239 // CG requires special handling for gradient on text
240 if (m_savedContext && m_gradient.contains(object)) {
241 GradientData* gradientData = m_gradient.get(object);
242
243 // Restore on-screen drawing context
244 context = m_savedContext;
245 m_savedContext = 0;
246
247 AffineTransform gradientTransform;
248 calculateGradientTransform(gradientTransform);
249
250 FloatRect targetRect;
251 gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, boundingBoxMode(), gradientTransform));
252 context->setFillGradient(gradientData->gradient);
253
254 context->fillRect(targetRect);
255 m_imageBuffer.clear();
256 }
257 #else
258 UNUSED_PARAM(object);
259 #endif
260 } else if (path) {
261 if (resourceMode & ApplyToFillMode)
262 context->fillPath(*path);
263 else if (resourceMode & ApplyToStrokeMode)
264 context->strokePath(*path);
265 }
266
267 context->restore();
268 }
269
270 void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
271 {
272 ASSERT(gradientData->gradient);
273
274 const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
275 for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
276 gradientData->gradient->addColorStop(*it);
277 }
278
279 }
280
281 #endif
282