SVG: postApplyResource() should take a RenderElement&.
[WebKit-https.git] / Source / WebCore / rendering / svg / RenderSVGResourceGradient.cpp
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 "RenderSVGShape.h"
31 #include "RenderSVGText.h"
32 #include "SVGRenderSupport.h"
33 #include "SVGRenderingContext.h"
34
35 namespace WebCore {
36
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
46 void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation)
47 {
48     m_gradientMap.clear();
49     m_shouldCollectGradientAttributes = true;
50     markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
51 }
52
53 void RenderSVGResourceGradient::removeClientFromCache(RenderObject* client, bool markForInvalidation)
54 {
55     ASSERT(client);
56     m_gradientMap.remove(client);
57     markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
58 }
59
60 #if USE(CG)
61 static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context,
62                                                            GraphicsContext*& savedContext,
63                                                            OwnPtr<ImageBuffer>& imageBuffer,
64                                                            RenderObject* object)
65 {
66     RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
67     ASSERT(textRootBlock);
68
69     AffineTransform absoluteTransform;
70     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(textRootBlock, absoluteTransform);
71
72     FloatRect repaintRect = textRootBlock->repaintRectInLocalCoordinates();
73     OwnPtr<ImageBuffer> maskImage;
74     if (!SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, maskImage, ColorSpaceDeviceRGB, Unaccelerated))
75         return false;
76
77     GraphicsContext* maskImageContext = maskImage->context();
78     ASSERT(maskImageContext);
79     ASSERT(maskImage);
80     savedContext = context;
81     context = maskImageContext;
82     imageBuffer = maskImage.release();
83     return true;
84 }
85
86 static inline AffineTransform clipToTextMask(GraphicsContext* context,
87                                              OwnPtr<ImageBuffer>& imageBuffer,
88                                              FloatRect& targetRect,
89                                              RenderObject* object,
90                                              bool boundingBoxMode,
91                                              const AffineTransform& gradientTransform)
92 {
93     RenderObject* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(object);
94     ASSERT(textRootBlock);
95
96     AffineTransform absoluteTransform;
97     SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(textRootBlock, absoluteTransform);
98
99     targetRect = textRootBlock->repaintRectInLocalCoordinates();
100     SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, targetRect, imageBuffer, false);
101
102     AffineTransform matrix;
103     if (boundingBoxMode) {
104         FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
105         matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
106         matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
107     }
108     matrix *= gradientTransform;
109     return matrix;
110 }
111 #endif
112
113 bool RenderSVGResourceGradient::applyResource(RenderElement& renderer, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode)
114 {
115     ASSERT(style);
116     ASSERT(context);
117     ASSERT(resourceMode != ApplyToDefaultMode);
118
119     // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further.
120     // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property
121     // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
122     // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash.
123     if (m_shouldCollectGradientAttributes) {
124         gradientElement().synchronizeAnimatedSVGAttribute(anyQName());
125         if (!collectGradientAttributes())
126             return false;
127
128         m_shouldCollectGradientAttributes = false;
129     }
130
131     // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
132     // then the given effect (e.g. a gradient or a filter) will be ignored.
133     FloatRect objectBoundingBox = renderer.objectBoundingBox();
134     if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
135         return false;
136
137     OwnPtr<GradientData>& gradientData = m_gradientMap.add(&renderer, nullptr).iterator->value;
138     if (!gradientData)
139         gradientData = adoptPtr(new GradientData);
140
141     bool isPaintingText = resourceMode & ApplyToTextMode;
142
143     // Create gradient object
144     if (!gradientData->gradient) {
145         buildGradient(gradientData.get());
146
147         // CG platforms will handle the gradient space transform for text after applying the
148         // resource, so don't apply it here. For non-CG platforms, we want the text bounding
149         // box applied to the gradient space transform now, so the gradient shader can use it.
150 #if USE(CG)
151         if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty() && !isPaintingText) {
152 #else
153         if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty()) {
154 #endif
155             gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
156             gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
157         }
158
159         AffineTransform gradientTransform;
160         calculateGradientTransform(gradientTransform);
161
162         gradientData->userspaceTransform *= gradientTransform;
163         if (isPaintingText) {
164             // Depending on font scaling factor, we may need to rescale the gradient here since
165             // text painting removes the scale factor from the context.
166             AffineTransform additionalTextTransform;
167             if (shouldTransformOnTextPainting(&renderer, additionalTextTransform))
168                 gradientData->userspaceTransform *= additionalTextTransform;
169         }
170         gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
171     }
172
173     if (!gradientData->gradient)
174         return false;
175
176     // Draw gradient
177     context->save();
178
179     if (isPaintingText) {
180 #if USE(CG)
181         if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, &renderer)) {
182             context->restore();
183             return false;
184         }
185 #endif
186
187         context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
188     }
189
190     const SVGRenderStyle* svgStyle = style->svgStyle();
191     ASSERT(svgStyle);
192
193     if (resourceMode & ApplyToFillMode) {
194         context->setAlpha(svgStyle->fillOpacity());
195         context->setFillGradient(gradientData->gradient);
196         context->setFillRule(svgStyle->fillRule());
197     } else if (resourceMode & ApplyToStrokeMode) {
198         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
199             gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(&renderer, gradientData->userspaceTransform));
200         context->setAlpha(svgStyle->strokeOpacity());
201         context->setStrokeGradient(gradientData->gradient);
202         SVGRenderSupport::applyStrokeStyleToContext(context, style, &renderer);
203     }
204
205     return true;
206 }
207
208 void RenderSVGResourceGradient::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
209 {
210     ASSERT(context);
211     ASSERT(resourceMode != ApplyToDefaultMode);
212
213     if (resourceMode & ApplyToTextMode) {
214 #if USE(CG)
215         // CG requires special handling for gradient on text
216         GradientData* gradientData;
217         if (m_savedContext && (gradientData = m_gradientMap.get(&renderer))) {
218             // Restore on-screen drawing context
219             context = m_savedContext;
220             m_savedContext = 0;
221
222             AffineTransform gradientTransform;
223             calculateGradientTransform(gradientTransform);
224
225             FloatRect targetRect;
226             gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, &renderer, gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, gradientTransform));
227             context->setFillGradient(gradientData->gradient);
228
229             context->fillRect(targetRect);
230             m_imageBuffer.clear();
231         }
232 #else
233         UNUSED_PARAM(renderer);
234 #endif
235     } else {
236         if (resourceMode & ApplyToFillMode) {
237             if (path)
238                 context->fillPath(*path);
239             else if (shape)
240                 shape->fillShape(context);
241         }
242         if (resourceMode & ApplyToStrokeMode) {
243             if (path)
244                 context->strokePath(*path);
245             else if (shape)
246                 shape->strokeShape(context);
247         }
248     }
249
250     context->restore();
251 }
252
253 void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
254 {
255     ASSERT(gradientData->gradient);
256
257     const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
258     for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
259         gradientData->gradient->addColorStop(*it);
260 }
261
262 GradientSpreadMethod RenderSVGResourceGradient::platformSpreadMethodFromSVGType(SVGSpreadMethodType method) const
263 {
264     switch (method) {
265     case SVGSpreadMethodUnknown:
266     case SVGSpreadMethodPad:
267         return SpreadMethodPad;
268     case SVGSpreadMethodReflect:
269         return SpreadMethodReflect;
270     case SVGSpreadMethodRepeat:
271         return SpreadMethodRepeat;
272     }
273
274     ASSERT_NOT_REACHED();
275     return SpreadMethodPad;
276 }
277
278 }
279
280 #endif