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