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 35namespace WebCore { 36 37RenderSVGResourceGradient::RenderSVGResourceGradient(SVGGradientElement* node) 38 : RenderSVGResourceContainer(node) 39 , m_shouldCollectGradientAttributes(true) 40#if USE(CG) 41 , m_savedContext(0) 42#endif 43{ 44} 45 46void RenderSVGResourceGradient::removeAllClientsFromCache(bool markForInvalidation) 47{ 48 m_gradientMap.clear(); 49 m_shouldCollectGradientAttributes = true; 50 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); 51} 52 53void 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) 61static 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 86static 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 113bool RenderSVGResourceGradient::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) 114{ 115 ASSERT(object); 116 ASSERT(style); 117 ASSERT(context); 118 ASSERT(resourceMode != ApplyToDefaultMode); 119 120 // Be sure to synchronize all SVG properties on the gradientElement _before_ processing any further. 121 // Otherwhise the call to collectGradientAttributes() in createTileImage(), may cause the SVG DOM property 122 // synchronization to kick in, which causes removeAllClientsFromCache() to be called, which in turn deletes our 123 // GradientData object! Leaving out the line below will cause svg/dynamic-updates/SVG*GradientElement-svgdom* to crash. 124 SVGGradientElement* gradientElement = static_cast<SVGGradientElement*>(node()); 125 if (!gradientElement) 126 return false; 127 128 if (m_shouldCollectGradientAttributes) { 129 gradientElement->synchronizeAnimatedSVGAttribute(anyQName()); 130 if (!collectGradientAttributes(gradientElement)) 131 return false; 132 133 m_shouldCollectGradientAttributes = false; 134 } 135 136 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, 137 // then the given effect (e.g. a gradient or a filter) will be ignored. 138 FloatRect objectBoundingBox = object->objectBoundingBox(); 139 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) 140 return false; 141 142 OwnPtr<GradientData>& gradientData = m_gradientMap.add(object, nullptr).iterator->value; 143 if (!gradientData) 144 gradientData = adoptPtr(new GradientData); 145 146 bool isPaintingText = resourceMode & ApplyToTextMode; 147 148 // Create gradient object 149 if (!gradientData->gradient) { 150 buildGradient(gradientData.get()); 151 152 // CG platforms will handle the gradient space transform for text after applying the 153 // resource, so don't apply it here. For non-CG platforms, we want the text bounding 154 // box applied to the gradient space transform now, so the gradient shader can use it. 155#if USE(CG) 156 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty() && !isPaintingText) { 157#else 158 if (gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && !objectBoundingBox.isEmpty()) { 159#endif 160 gradientData->userspaceTransform.translate(objectBoundingBox.x(), objectBoundingBox.y()); 161 gradientData->userspaceTransform.scaleNonUniform(objectBoundingBox.width(), objectBoundingBox.height()); 162 } 163 164 AffineTransform gradientTransform; 165 calculateGradientTransform(gradientTransform); 166 167 gradientData->userspaceTransform *= gradientTransform; 168 if (isPaintingText) { 169 // Depending on font scaling factor, we may need to rescale the gradient here since 170 // text painting removes the scale factor from the context. 171 AffineTransform additionalTextTransform; 172 if (shouldTransformOnTextPainting(object, additionalTextTransform)) 173 gradientData->userspaceTransform *= additionalTextTransform; 174 } 175 gradientData->gradient->setGradientSpaceTransform(gradientData->userspaceTransform); 176 } 177 178 if (!gradientData->gradient) 179 return false; 180 181 // Draw gradient 182 context->save(); 183 184 if (isPaintingText) { 185#if USE(CG) 186 if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) { 187 context->restore(); 188 return false; 189 } 190#endif 191 192 context->setTextDrawingMode(resourceMode & ApplyToFillMode ? TextModeFill : TextModeStroke); 193 } 194 195 const SVGRenderStyle* svgStyle = style->svgStyle(); 196 ASSERT(svgStyle); 197 198 if (resourceMode & ApplyToFillMode) { 199 context->setAlpha(svgStyle->fillOpacity()); 200 context->setFillGradient(gradientData->gradient); 201 context->setFillRule(svgStyle->fillRule()); 202 } else if (resourceMode & ApplyToStrokeMode) { 203 if (svgStyle->vectorEffect() == VE_NON_SCALING_STROKE) 204 gradientData->gradient->setGradientSpaceTransform(transformOnNonScalingStroke(object, gradientData->userspaceTransform)); 205 context->setAlpha(svgStyle->strokeOpacity()); 206 context->setStrokeGradient(gradientData->gradient); 207 SVGRenderSupport::applyStrokeStyleToContext(context, style, object); 208 } 209 210 return true; 211} 212 213void RenderSVGResourceGradient::postApplyResource(RenderObject* object, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape) 214{ 215 ASSERT(context); 216 ASSERT(resourceMode != ApplyToDefaultMode); 217 218 if (resourceMode & ApplyToTextMode) { 219#if USE(CG) 220 // CG requires special handling for gradient on text 221 GradientData* gradientData; 222 if (m_savedContext && (gradientData = m_gradientMap.get(object))) { 223 // Restore on-screen drawing context 224 context = m_savedContext; 225 m_savedContext = 0; 226 227 AffineTransform gradientTransform; 228 calculateGradientTransform(gradientTransform); 229 230 FloatRect targetRect; 231 gradientData->gradient->setGradientSpaceTransform(clipToTextMask(context, m_imageBuffer, targetRect, object, gradientUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, gradientTransform)); 232 context->setFillGradient(gradientData->gradient); 233 234 context->fillRect(targetRect); 235 m_imageBuffer.clear(); 236 } 237#else 238 UNUSED_PARAM(object); 239#endif 240 } else { 241 if (resourceMode & ApplyToFillMode) { 242 if (path) 243 context->fillPath(*path); 244 else if (shape) 245 shape->fillShape(context); 246 } 247 if (resourceMode & ApplyToStrokeMode) { 248 if (path) 249 context->strokePath(*path); 250 else if (shape) 251 shape->strokeShape(context); 252 } 253 } 254 255 context->restore(); 256} 257 258void RenderSVGResourceGradient::addStops(GradientData* gradientData, const Vector<Gradient::ColorStop>& stops) const 259{ 260 ASSERT(gradientData->gradient); 261 262 const Vector<Gradient::ColorStop>::const_iterator end = stops.end(); 263 for (Vector<Gradient::ColorStop>::const_iterator it = stops.begin(); it != end; ++it) 264 gradientData->gradient->addColorStop(*it); 265} 266 267GradientSpreadMethod RenderSVGResourceGradient::platformSpreadMethodFromSVGType(SVGSpreadMethodType method) const 268{ 269 switch (method) { 270 case SVGSpreadMethodUnknown: 271 case SVGSpreadMethodPad: 272 return SpreadMethodPad; 273 case SVGSpreadMethodReflect: 274 return SpreadMethodReflect; 275 case SVGSpreadMethodRepeat: 276 return SpreadMethodRepeat; 277 } 278 279 ASSERT_NOT_REACHED(); 280 return SpreadMethodPad; 281} 282 283} 284 285#endif 286