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.
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.
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.
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.
24 #include "RenderSVGResourceGradient.h"
26 #include "GradientAttributes.h"
27 #include "GraphicsContext.h"
28 #include "RenderSVGText.h"
29 #include "SVGRenderingContext.h"
33 RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement& node, Ref<RenderStyle>&& style)
34 : RenderSVGResourceContainer(node, WTF::move(style))
35 , m_shouldCollectGradientAttributes(true)
42 void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation)
44 m_gradientMap.clear();
45 m_shouldCollectGradientAttributes = true;
46 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
49 void RenderSVGResourceGradient::removeClientFromCache(RenderElement& client, bool markForInvalidation)
51 m_gradientMap.remove(&client);
52 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation);
56 static inline bool createMaskAndSwapContextForTextGradient(GraphicsContext*& context, GraphicsContext*& savedContext, std::unique_ptr<ImageBuffer>& imageBuffer, RenderObject* object)
58 auto* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(*object);
59 ASSERT(textRootBlock);
61 AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(*textRootBlock);
62 FloatRect repaintRect = textRootBlock->repaintRectInLocalCoordinates();
64 auto maskImage = SVGRenderingContext::createImageBuffer(repaintRect, absoluteTransform, ColorSpaceDeviceRGB, Unaccelerated);
68 GraphicsContext* maskImageContext = maskImage->context();
69 ASSERT(maskImageContext);
71 savedContext = context;
72 context = maskImageContext;
73 imageBuffer = WTF::move(maskImage);
77 static inline AffineTransform clipToTextMask(GraphicsContext* context, std::unique_ptr<ImageBuffer>& imageBuffer, FloatRect& targetRect, RenderObject* object, bool boundingBoxMode, const AffineTransform& gradientTransform)
79 auto* textRootBlock = RenderSVGText::locateRenderSVGTextAncestor(*object);
80 ASSERT(textRootBlock);
82 AffineTransform absoluteTransform = SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(*textRootBlock);
84 targetRect = textRootBlock->repaintRectInLocalCoordinates();
86 SVGRenderingContext::clipToImageBuffer(context, absoluteTransform, targetRect, imageBuffer, false);
88 AffineTransform matrix;
89 if (boundingBoxMode) {
90 FloatRect maskBoundingBox = textRootBlock->objectBoundingBox();
91 matrix.translate(maskBoundingBox.x(), maskBoundingBox.y());
92 matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height());
94 matrix *= gradientTransform;
99 bool RenderSVGResourceGradient::applyResource(RenderElement& renderer, const RenderStyle& style, GraphicsContext*& context, unsigned short resourceMode)
102 ASSERT(resourceMode != ApplyToDefaultMode);
104 // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further.
105 // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property
106 // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our
107 // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash.
108 if (m_shouldCollectGradientAttributes) {
109 gradientElement().synchronizeAnimatedSVGAttribute(anyQName());
110 if (!collectGradientAttributes())
113 m_shouldCollectGradientAttributes = false;
116 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified,
117 // then the given effect (e.g. a gradient or a filter) will be ignored.
118 FloatRect objectBoundingBox = renderer.objectBoundingBox();
119 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty())
122 auto& gradientData = m_gradientMap.add(&renderer, nullptr).iterator->value;
124 gradientData = std::make_unique<GradientData>();
126 bool isPaintingText = resourceMode & ApplyToTextMode;
128 // Create gradient object
129 if (!gradientData->gradient) {
130 buildGradient(gradientData.get());
132 // CG platforms will handle the gradient space transform for text after applying the
133 // resource, so don't apply it here. For non-CG platforms, we want the text bounding
134 // box applied to the gradient space transform now, so the gradient shader can use it.
136 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty() && !isPaintingText) {
138 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty()) {
140 gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y());
141 gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height());
144 AffineTransform gradientTransform;
145 calculateGradientTransform(gradientTransform);
147 gradientData->userspaceTransform *= gradientTransform;
148 if (isPaintingText) {
149 // Depending on font scaling factor, we may need to rescale the gradient here since
150 // text painting removes the scale factor from the context.
151 AffineTransform additionalTextTransform;
152 if (shouldTransformOnTextPainting(renderer, additionalTextTransform))
153 gradientData->userspaceTransform *= additionalTextTransform;
155 gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform);
158 if (!gradientData->gradient)
164 if (isPaintingText) {
166 if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, &renderer)) {
172 context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke);
175 const SVGRenderStyle& svgStyle = style.svgStyle();
177 if (resourceMode & ApplyToFillMode) {
178 context->setAlpha(svgStyle.fillOpacity());
179 context->setFillGradient(*gradientData->gradient);
180 context->setFillRule(svgStyle.fillRule());
181 } else if (resourceMode & ApplyToStrokeMode) {
182 if (svgStyle.vectorEffect() == VE_NON_SCALING_STROKE)
183 gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(&renderer, gradientData->userspaceTransform));
184 context->setAlpha(svgStyle.strokeOpacity());
185 context->setStrokeGradient(*gradientData->gradient);
186 SVGRenderSupport::applyStrokeStyleToContext(context, style, renderer);
192 void RenderSVGResourceGradient::postApplyResource(RenderElement& renderer, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape)
195 ASSERT(resourceMode != ApplyToDefaultMode);
197 if (resourceMode & ApplyToTextMode) {
199 // CG requires special handling for gradient on text
200 GradientData* gradientData;
201 if (m_savedContext && (gradientData = m_gradientMap.get(&renderer))) {
202 // Restore on-screen drawing context
203 context = m_savedContext;
206 AffineTransform gradientTransform;
207 calculateGradientTransform(gradientTransform);
209 FloatRect targetRect;
210 gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, &renderer, gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, gradientTransform));
211 context->setFillGradient(*gradientData->gradient);
213 context->fillRect(targetRect);
214 m_imageBuffer.reset();
217 UNUSED_PARAM(renderer);
220 if (resourceMode & ApplyToFillMode) {
222 context->fillPath(*path);
224 shape->fillShape(context);
226 if (resourceMode & ApplyToStrokeMode) {
228 context->strokePath(*path);
230 shape->strokeShape(context);
237 void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const
239 ASSERT(gradientData->gradient);
241 const Vector<Gradient::ColorStop>::const_iterator end = stops.end();
242 for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it)
243 gradientData->gradient->addColorStop(*it);
246 GradientSpreadMethod RenderSVGResourceGradient::platformSpreadMethodFromSVGType(SVGSpreadMethodType method) const
249 case SVGSpreadMethodUnknown:
250 case SVGSpreadMethodPad:
251 return SpreadMethodPad;
252 case SVGSpreadMethodReflect:
253 return SpreadMethodReflect;
254 case SVGSpreadMethodRepeat:
255 return SpreadMethodRepeat;
258 ASSERT_NOT_REACHED();
259 return SpreadMethodPad;