Rename first/second to key/value in HashMap iterators
[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->value;
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         if (isPaintingText) {
170             // Depending on font scaling factor, we may need to rescale the gradient here since
171             // text painting removes the scale factor from the context.
172             AffineTransform additionalTextTransform;
173             if (shouldTransformOnTextPainting(object, additionalTextTransform))
174                 gradientData->userspaceTransform *= additionalTextTransform;
175         }
176         gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
177     }
178
179     if (!gradientData->gradient)
180         return false;
181
182     // Draw gradient
183     context->save();
184
185     if (isPaintingText) {
186 #if USE(CG)
187         if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) {
188             context->restore();
189             return false;
190         }
191 #endif
192
193         context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
194     }
195
196     const SVGRenderStyle* svgStyle = style->svgStyle();
197     ASSERT(svgStyle);
198
199     if (resourceMode & ApplyToFillMode) {
200         context->setAlpha(svgStyle->fillOpacity());
201         context->setFillGradient(gradientData->gradient);
202         context->setFillRule(svgStyle->fillRule());
203     } else if (resourceMode & ApplyToStrokeMode) {
204         if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE)
205             gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform));
206         context->setAlpha(svgStyle->strokeOpacity());
207         context->setStrokeGradient(gradientData->gradient);
208         SVGRenderSupport::applyStrokeStyleToContext(context, style, object);
209     }
210
211     return true;
212 }
213
214 void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
215 {
216     ASSERT(context);
217     ASSERT(resourceMode != ApplyToDefaultMode);
218
219     if (resourceMode & ApplyToTextMode) {
220 #if USE(CG)
221         // CG requires special handling for gradient on text
222         GradientData* gradientData;
223         if (m_savedContext && (gradientData = m_gradientMap.get(object))) {
224             // Restore on-screen drawing context
225             context = m_savedContext;
226             m_savedContext = 0;
227
228             AffineTransform gradientTransform;
229             calculateGradientTransform(gradientTransform);
230
231             FloatRect targetRect;
232             gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, gradientTransform));
233             context->setFillGradient(gradientData->gradient);
234
235             context->fillRect(targetRect);
236             m_imageBuffer.clear();
237         }
238 #else
239         UNUSED_PARAM(object);
240 #endif
241     } else {
242         if (resourceMode & ApplyToFillMode) {
243             if (path)
244                 context->fillPath(*path);
245             else if (shape)
246                 shape->fillShape(context);
247         }
248         if (resourceMode & ApplyToStrokeMode) {
249             if (path)
250                 context->strokePath(*path);
251             else if (shape)
252                 shape->strokeShape(context);
253         }
254     }
255
256     context->restore();
257 }
258
259 void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
260 {
261     ASSERT(gradientData->gradient);
262
263     const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
264     for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
265         gradientData->gradient->addColorStop(*it);
266 }
267
268 GradientSpreadMethod RenderSVGResourceGradient::platformSpreadMethodFromSVGType(SVGSpreadMethodType method) const
269 {
270     switch (method) {
271     case SVGSpreadMethodUnknown:
272     case SVGSpreadMethodPad:
273         return SpreadMethodPad;
274     case SVGSpreadMethodReflect:
275         return SpreadMethodReflect;
276     case SVGSpreadMethodRepeat:
277         return SpreadMethodRepeat;
278     }
279
280     ASSERT_NOT_REACHED();
281     return SpreadMethodPad;
282 }
283
284 }
285
286 #endif