1 /* 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * 2008 Eric Seidel <eric (at) webkit.org> 4 * 2008 Dirk Schulze <krit (at) webkit.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "config.h" 29 30 #if ENABLE(SVG) 31 #include "SVGPaintServerGradient.h" 32 33 #include "FloatConversion.h" 34 #include "GraphicsContext.h" 35 #include "ImageBuffer.h" 36 #include "RenderObject.h" 37 #include "RenderView.h" 38 #include "SVGGradientElement.h" 39 #include "SVGPaintServerLinearGradient.h" 40 #include "SVGPaintServerRadialGradient.h" 41 #include "SVGRenderSupport.h" 42 #include "SVGRenderTreeAsText.h" 43 44 using namespace std; 45 46 namespace WebCore { 47 48 static TextStream& operator<<(TextStream& ts, GradientSpreadMethod m) 49 { 50 switch (m) { 51 case SpreadMethodPad: 52 ts << "PAD"; break; 53 case SpreadMethodRepeat: 54 ts << "REPEAT"; break; 55 case SpreadMethodReflect: 56 ts << "REFLECT"; break; 57 } 58 59 return ts; 60 } 61 62 static TextStream& operator<<(TextStream& ts, const Vector<SVGGradientStop>& l) 63 { 64 ts << "["; 65 for (Vector<SVGGradientStop>::const_iterator it = l.begin(); it != l.end(); ++it) { 66 ts << "(" << it->first << "," << it->second << ")"; 67 if (it + 1 != l.end()) 68 ts << ", "; 69 } 70 ts << "]"; 71 return ts; 72 } 73 74 SVGPaintServerGradient::SVGPaintServerGradient(const SVGGradientElement* owner) 75 : m_boundingBoxMode(true) 76 , m_ownerElement(owner) 77 #if PLATFORM(CG) 78 , m_savedContext(0) 79 , m_imageBuffer(0) 80 #endif 81 { 82 ASSERT(owner); 83 } 84 85 SVGPaintServerGradient::~SVGPaintServerGradient() 86 { 87 } 88 89 Gradient* SVGPaintServerGradient::gradient() const 90 { 91 return m_gradient.get(); 92 } 93 94 void SVGPaintServerGradient::setGradient(PassRefPtr<Gradient> gradient) 95 { 96 m_gradient = gradient; 97 } 98 99 bool SVGPaintServerGradient::boundingBoxMode() const 100 { 101 return m_boundingBoxMode; 102 } 103 104 void SVGPaintServerGradient::setBoundingBoxMode(bool mode) 105 { 106 m_boundingBoxMode = mode; 107 } 108 109 AffineTransform SVGPaintServerGradient::gradientTransform() const 110 { 111 return m_gradientTransform; 112 } 113 114 void SVGPaintServerGradient::setGradientTransform(const AffineTransform& transform) 115 { 116 m_gradientTransform = transform; 117 } 118 119 #if PLATFORM(CG) 120 static inline const RenderObject* findTextRootObject(const RenderObject* start) 121 { 122 while (start && !start->isSVGText()) 123 start = start->parent(); 124 ASSERT(start); 125 ASSERT(start->isSVGText()); 126 127 return start; 128 } 129 130 static inline AffineTransform absoluteTransformForRenderer(const RenderObject* object) 131 { 132 AffineTransform absoluteTransform; 133 134 const RenderObject* currentObject = object; 135 while (currentObject) { 136 absoluteTransform = currentObject->localToParentTransform() * absoluteTransform; 137 currentObject = currentObject->parent(); 138 } 139 140 return absoluteTransform; 141 } 142 143 static inline bool createMaskAndSwapContextForTextGradient( 144 GraphicsContext*& context, GraphicsContext*& savedContext, 145 OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object) 146 { 147 const RenderObject* textRootBlock = findTextRootObject(object); 148 149 AffineTransform transform = absoluteTransformForRenderer(textRootBlock); 150 FloatRect maskAbsoluteBoundingBox = transform.mapRect(textRootBlock->repaintRectInLocalCoordinates()); 151 152 IntRect maskImageRect = enclosingIntRect(maskAbsoluteBoundingBox); 153 if (maskImageRect.isEmpty()) 154 return false; 155 156 // Allocate an image buffer as big as the absolute unclipped size of the object 157 OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskImageRect.size()); 158 if (!maskImage) 159 return false; 160 161 GraphicsContext* maskImageContext = maskImage->context(); 162 163 // Transform the mask image coordinate system to absolute screen coordinates 164 maskImageContext->translate(-maskAbsoluteBoundingBox.x(), -maskAbsoluteBoundingBox.y()); 165 maskImageContext->concatCTM(transform); 166 167 imageBuffer.set(maskImage.release()); 168 savedContext = context; 169 context = maskImageContext; 170 171 return true; 172 } 173 174 static inline AffineTransform clipToTextMask(GraphicsContext* context, 175 OwnPtr<ImageBuffer>& imageBuffer, const RenderObject* object, 176 const SVGPaintServerGradient* gradientServer) 177 { 178 const RenderObject* textRootBlock = findTextRootObject(object); 179 context->clipToImageBuffer(textRootBlock->repaintRectInLocalCoordinates(), imageBuffer.get()); 180 181 AffineTransform matrix; 182 if (gradientServer->boundingBoxMode()) { 183 FloatRect maskBoundingBox = textRootBlock->objectBoundingBox(); 184 matrix.translate(maskBoundingBox.x(), maskBoundingBox.y()); 185 matrix.scaleNonUniform(maskBoundingBox.width(), maskBoundingBox.height()); 186 } 187 matrix.multiply(gradientServer->gradientTransform()); 188 return matrix; 189 } 190 #endif 191 192 bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, const RenderStyle*style, SVGPaintTargetType type, bool isPaintingText) const 193 { 194 m_ownerElement->buildGradient(); 195 196 const SVGRenderStyle* svgStyle = style->svgStyle(); 197 bool isFilled = (type & ApplyToFillTargetType) && svgStyle->hasFill(); 198 bool isStroked = (type & ApplyToStrokeTargetType) && svgStyle->hasStroke(); 199 200 ASSERT((isFilled && !isStroked) || (!isFilled && isStroked)); 201 202 context->save(); 203 204 if (isPaintingText) { 205 #if PLATFORM(CG) 206 if (!createMaskAndSwapContextForTextGradient(context, m_savedContext, m_imageBuffer, object)) { 207 context->restore(); 208 return false; 209 } 210 #endif 211 context->setTextDrawingMode(isFilled ? cTextFill : cTextStroke); 212 } 213 214 if (isFilled) { 215 context->setAlpha(svgStyle->fillOpacity()); 216 context->setFillGradient(m_gradient); 217 context->setFillRule(svgStyle->fillRule()); 218 } 219 if (isStroked) { 220 context->setAlpha(svgStyle->strokeOpacity()); 221 context->setStrokeGradient(m_gradient); 222 applyStrokeStyleToContext(context, style, object); 223 } 224 225 AffineTransform matrix; 226 // CG platforms will handle the gradient space transform for text in 227 // teardown, so we don't apply it here. For non-CG platforms, we 228 // want the text bounding box applied to the gradient space transform now, 229 // so the gradient shader can use it. 230 #if PLATFORM(CG) 231 if (boundingBoxMode() && !isPaintingText) { 232 #else 233 if (boundingBoxMode()) { 234 #endif 235 FloatRect bbox = object->objectBoundingBox(); 236 // Don't use gradients for 1d objects like horizontal/vertical 237 // lines or rectangles without width or height. 238 if (bbox.width() == 0 || bbox.height() == 0) { 239 Color color(0, 0, 0); 240 context->setStrokeColor(color, style->colorSpace()); 241 return true; 242 } 243 matrix.translate(bbox.x(), bbox.y()); 244 matrix.scaleNonUniform(bbox.width(), bbox.height()); 245 } 246 matrix.multiply(gradientTransform()); 247 m_gradient->setGradientSpaceTransform(matrix); 248 249 return true; 250 } 251 252 void SVGPaintServerGradient::teardown(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType, bool isPaintingText) const 253 { 254 #if PLATFORM(CG) 255 // renderPath() is not used when painting text, so we paint the gradient during teardown() 256 if (isPaintingText && m_savedContext) { 257 // Restore on-screen drawing context 258 context = m_savedContext; 259 m_savedContext = 0; 260 261 AffineTransform matrix = clipToTextMask(context, m_imageBuffer, object, this); 262 m_gradient->setGradientSpaceTransform(matrix); 263 context->setFillGradient(m_gradient); 264 265 const RenderObject* textRootBlock = findTextRootObject(object); 266 context->fillRect(textRootBlock->repaintRectInLocalCoordinates()); 267 268 m_imageBuffer.clear(); 269 } 270 #endif 271 context->restore(); 272 } 273 274 TextStream& SVGPaintServerGradient::externalRepresentation(TextStream& ts) const 275 { 276 // Gradients/patterns aren't setup, until they are used for painting. Work around that fact. 277 m_ownerElement->buildGradient(); 278 279 // abstract, don't stream type 280 ts << "[stops=" << gradientStops() << "]"; 281 if (m_gradient->spreadMethod() != SpreadMethodPad) 282 ts << "[method=" << m_gradient->spreadMethod() << "]"; 283 if (!boundingBoxMode()) 284 ts << " [bounding box mode=" << boundingBoxMode() << "]"; 285 if (!gradientTransform().isIdentity()) 286 ts << " [transform=" << gradientTransform() << "]"; 287 288 return ts; 289 } 290 291 } // namespace WebCore 292 293 #endif 294