1/* 2 * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> 3 * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> 4 * Copyright (C) 2007 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2009 Google, Inc. All rights reserved. 6 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> 7 * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25#include "config.h" 26#include "SVGRenderingContext.h" 27 28#include "BasicShapes.h" 29#include "Frame.h" 30#include "FrameView.h" 31#include "Page.h" 32#include "RenderLayer.h" 33#include "RenderSVGImage.h" 34#include "RenderSVGResourceClipper.h" 35#include "RenderSVGResourceFilter.h" 36#include "RenderSVGResourceMasker.h" 37#include "RenderView.h" 38#include "SVGLengthContext.h" 39#include "SVGResources.h" 40#include "SVGResourcesCache.h" 41 42static int kMaxImageBufferSize = 4096; 43 44namespace WebCore { 45 46static inline bool isRenderingMaskImage(const RenderObject& object) 47{ 48 return object.view().frameView().paintBehavior() & PaintBehaviorRenderingSVGMask; 49} 50 51SVGRenderingContext::~SVGRenderingContext() 52{ 53 // Fast path if we don't need to restore anything. 54 if (!(m_renderingFlags & ActionsNeeded)) 55 return; 56 57 ASSERT(m_renderer && m_paintInfo); 58 59#if ENABLE(FILTERS) 60 if (m_renderingFlags & EndFilterLayer) { 61 ASSERT(m_filter); 62 m_filter->postApplyResource(*m_renderer, m_paintInfo->context, ApplyToDefaultMode, 0, 0); 63 m_paintInfo->context = m_savedContext; 64 m_paintInfo->rect = m_savedPaintRect; 65 } 66#endif 67 68 if (m_renderingFlags & EndOpacityLayer) 69 m_paintInfo->context->endTransparencyLayer(); 70 71 if (m_renderingFlags & EndShadowLayer) 72 m_paintInfo->context->endTransparencyLayer(); 73 74 if (m_renderingFlags & RestoreGraphicsContext) 75 m_paintInfo->context->restore(); 76} 77 78void SVGRenderingContext::prepareToRenderSVGContent(RenderElement& renderer, PaintInfo& paintInfo, NeedsGraphicsContextSave needsGraphicsContextSave) 79{ 80#ifndef NDEBUG 81 // This function must not be called twice! 82 ASSERT(!(m_renderingFlags & PrepareToRenderSVGContentWasCalled)); 83 m_renderingFlags |= PrepareToRenderSVGContentWasCalled; 84#endif 85 86 m_renderer = &renderer; 87 m_paintInfo = &paintInfo; 88#if ENABLE(FILTERS) 89 m_filter = 0; 90#endif 91 92 // We need to save / restore the context even if the initialization failed. 93 if (needsGraphicsContextSave == SaveGraphicsContext) { 94 m_paintInfo->context->save(); 95 m_renderingFlags |= RestoreGraphicsContext; 96 } 97 98 RenderStyle& style = m_renderer->style(); 99 100 const SVGRenderStyle& svgStyle = style.svgStyle(); 101 102 // Setup transparency layers before setting up SVG resources! 103 bool isRenderingMask = isRenderingMaskImage(*m_renderer); 104 // RenderLayer takes care of root opacity. 105 float opacity = (renderer.isSVGRoot() || isRenderingMask) ? 1 : style.opacity(); 106 const ShadowData* shadow = svgStyle.shadow(); 107 bool hasBlendMode = style.hasBlendMode(); 108 bool hasIsolation = style.hasIsolation(); 109 bool isolateMaskForBlending = false; 110 111#if ENABLE(CSS_COMPOSITING) 112 if (svgStyle.hasMasker() && toSVGElement(renderer.element())->isSVGGraphicsElement()) { 113 SVGGraphicsElement& graphicsElement = *toSVGGraphicsElement(renderer.element()); 114 isolateMaskForBlending = graphicsElement.shouldIsolateBlending(); 115 } 116#endif 117 118 if (opacity < 1 || shadow || hasBlendMode || isolateMaskForBlending || hasIsolation) { 119 FloatRect repaintRect = m_renderer->repaintRectInLocalCoordinates(); 120 m_paintInfo->context->clip(repaintRect); 121 122 if (opacity < 1 || hasBlendMode || isolateMaskForBlending || hasIsolation) { 123 124 if (hasBlendMode) 125 m_paintInfo->context->setCompositeOperation(m_paintInfo->context->compositeOperation(), style.blendMode()); 126 127 m_paintInfo->context->beginTransparencyLayer(opacity); 128 129 if (hasBlendMode) 130 m_paintInfo->context->setCompositeOperation(m_paintInfo->context->compositeOperation(), BlendModeNormal); 131 132 m_renderingFlags |= EndOpacityLayer; 133 } 134 135 if (shadow) { 136 m_paintInfo->context->setShadow(IntSize(roundToInt(shadow->x()), roundToInt(shadow->y())), shadow->radius(), shadow->color(), style.colorSpace()); 137 m_paintInfo->context->beginTransparencyLayer(1); 138 m_renderingFlags |= EndShadowLayer; 139 } 140 } 141 142 ClipPathOperation* clipPathOperation = style.clipPath(); 143 if (clipPathOperation && clipPathOperation->type() == ClipPathOperation::Shape) { 144 ShapeClipPathOperation& clipPath = toShapeClipPathOperation(*clipPathOperation); 145 FloatRect referenceBox; 146 if (clipPath.referenceBox() == Stroke) 147 // FIXME: strokeBoundingBox() takes dasharray into account but shouldn't. 148 referenceBox = renderer.strokeBoundingBox(); 149 else if (clipPath.referenceBox() == ViewBox && renderer.element()) { 150 FloatSize viewportSize; 151 SVGLengthContext(toSVGElement(renderer.element())).determineViewport(viewportSize); 152 referenceBox.setWidth(viewportSize.width()); 153 referenceBox.setHeight(viewportSize.height()); 154 } else 155 referenceBox = renderer.objectBoundingBox(); 156 m_paintInfo->context->clipPath(clipPath.pathForReferenceRect(referenceBox), clipPath.windRule()); 157 } 158 159 SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*m_renderer); 160 if (!resources) { 161#if ENABLE(FILTERS) 162 if (svgStyle.hasFilter()) 163 return; 164#endif 165 m_renderingFlags |= RenderingPrepared; 166 return; 167 } 168 169 if (!isRenderingMask) { 170 if (RenderSVGResourceMasker* masker = resources->masker()) { 171 if (!masker->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode)) 172 return; 173 } 174 } 175 176 RenderSVGResourceClipper* clipper = resources->clipper(); 177 if (!clipPathOperation && clipper) { 178 if (!clipper->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode)) 179 return; 180 } 181 182#if ENABLE(FILTERS) 183 if (!isRenderingMask) { 184 m_filter = resources->filter(); 185 if (m_filter) { 186 m_savedContext = m_paintInfo->context; 187 m_savedPaintRect = m_paintInfo->rect; 188 // Return with false here may mean that we don't need to draw the content 189 // (because it was either drawn before or empty) but we still need to apply the filter. 190 m_renderingFlags |= EndFilterLayer; 191 if (!m_filter->applyResource(*m_renderer, style, m_paintInfo->context, ApplyToDefaultMode)) 192 return; 193 194 // Since we're caching the resulting bitmap and do not invalidate it on repaint rect 195 // changes, we need to paint the whole filter region. Otherwise, elements not visible 196 // at the time of the initial paint (due to scrolling, window size, etc.) will never 197 // be drawn. 198 m_paintInfo->rect = IntRect(m_filter->drawingRegion(m_renderer)); 199 } 200 } 201#endif 202 203 m_renderingFlags |= RenderingPrepared; 204} 205 206static AffineTransform& currentContentTransformation() 207{ 208 DEPRECATED_DEFINE_STATIC_LOCAL(AffineTransform, s_currentContentTransformation, ()); 209 return s_currentContentTransformation; 210} 211 212float SVGRenderingContext::calculateScreenFontSizeScalingFactor(const RenderObject& renderer) 213{ 214 AffineTransform ctm; 215 calculateTransformationToOutermostCoordinateSystem(renderer, ctm); 216 return narrowPrecisionToFloat(sqrt((pow(ctm.xScale(), 2) + pow(ctm.yScale(), 2)) / 2)); 217} 218 219void SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(const RenderObject& renderer, AffineTransform& absoluteTransform) 220{ 221 absoluteTransform = currentContentTransformation(); 222 223 float deviceScaleFactor = renderer.document().deviceScaleFactor(); 224 // Walk up the render tree, accumulating SVG transforms. 225 const RenderObject* ancestor = &renderer; 226 while (ancestor) { 227 absoluteTransform = ancestor->localToParentTransform() * absoluteTransform; 228 if (ancestor->isSVGRoot()) 229 break; 230 ancestor = ancestor->parent(); 231 } 232 233 // Continue walking up the layer tree, accumulating CSS transforms. 234 RenderLayer* layer = ancestor ? ancestor->enclosingLayer() : nullptr; 235 while (layer) { 236 if (TransformationMatrix* layerTransform = layer->transform()) 237 absoluteTransform = layerTransform->toAffineTransform() * absoluteTransform; 238 239 // We can stop at compositing layers, to match the backing resolution. 240 if (layer->isComposited()) 241 break; 242 243 layer = layer->parent(); 244 } 245 246 absoluteTransform.scale(deviceScaleFactor); 247} 248 249bool SVGRenderingContext::createImageBuffer(const FloatRect& targetRect, const AffineTransform& absoluteTransform, std::unique_ptr<ImageBuffer>& imageBuffer, ColorSpace colorSpace, RenderingMode renderingMode) 250{ 251 IntRect paintRect = calculateImageBufferRect(targetRect, absoluteTransform); 252 // Don't create empty ImageBuffers. 253 if (paintRect.isEmpty()) 254 return false; 255 256 IntSize clampedSize = clampedAbsoluteSize(paintRect.size()); 257 std::unique_ptr<ImageBuffer> image = ImageBuffer::create(clampedSize, 1, colorSpace, renderingMode); 258 if (!image) 259 return false; 260 261 GraphicsContext* imageContext = image->context(); 262 ASSERT(imageContext); 263 264 imageContext->scale(FloatSize(static_cast<float>(clampedSize.width()) / paintRect.width(), 265 static_cast<float>(clampedSize.height()) / paintRect.height())); 266 imageContext->translate(-paintRect.x(), -paintRect.y()); 267 imageContext->concatCTM(absoluteTransform); 268 269 imageBuffer = WTF::move(image); 270 return true; 271} 272 273bool SVGRenderingContext::createImageBufferForPattern(const FloatRect& absoluteTargetRect, const FloatRect& clampedAbsoluteTargetRect, std::unique_ptr<ImageBuffer>& imageBuffer, ColorSpace colorSpace, RenderingMode renderingMode) 274{ 275 IntSize imageSize(roundedIntSize(clampedAbsoluteTargetRect.size())); 276 IntSize unclampedImageSize(roundedIntSize(absoluteTargetRect.size())); 277 278 // Don't create empty ImageBuffers. 279 if (imageSize.isEmpty()) 280 return false; 281 282 std::unique_ptr<ImageBuffer> image = ImageBuffer::create(imageSize, 1, colorSpace, renderingMode); 283 if (!image) 284 return false; 285 286 GraphicsContext* imageContext = image->context(); 287 ASSERT(imageContext); 288 289 // Compensate rounding effects, as the absolute target rect is using floating-point numbers and the image buffer size is integer. 290 imageContext->scale(FloatSize(unclampedImageSize.width() / absoluteTargetRect.width(), unclampedImageSize.height() / absoluteTargetRect.height())); 291 292 imageBuffer = WTF::move(image); 293 return true; 294} 295 296void SVGRenderingContext::renderSubtreeToImageBuffer(ImageBuffer* image, RenderElement& item, const AffineTransform& subtreeContentTransformation) 297{ 298 ASSERT(image); 299 ASSERT(image->context()); 300 301 PaintInfo info(image->context(), LayoutRect::infiniteRect(), PaintPhaseForeground, PaintBehaviorNormal); 302 303 AffineTransform& contentTransformation = currentContentTransformation(); 304 AffineTransform savedContentTransformation = contentTransformation; 305 contentTransformation = subtreeContentTransformation * contentTransformation; 306 307 ASSERT(!item.needsLayout()); 308 item.paint(info, IntPoint()); 309 310 contentTransformation = savedContentTransformation; 311} 312 313void SVGRenderingContext::clipToImageBuffer(GraphicsContext* context, const AffineTransform& absoluteTransform, const FloatRect& targetRect, std::unique_ptr<ImageBuffer>& imageBuffer, bool safeToClear) 314{ 315 ASSERT(context); 316 ASSERT(imageBuffer); 317 318 FloatRect absoluteTargetRect = calculateImageBufferRect(targetRect, absoluteTransform); 319 320 // The mask image has been created in the absolute coordinate space, as the image should not be scaled. 321 // So the actual masking process has to be done in the absolute coordinate space as well. 322 context->concatCTM(absoluteTransform.inverse()); 323 context->clipToImageBuffer(imageBuffer.get(), absoluteTargetRect); 324 context->concatCTM(absoluteTransform); 325 326 // When nesting resources, with objectBoundingBox as content unit types, there's no use in caching the 327 // resulting image buffer as the parent resource already caches the result. 328 if (safeToClear && !currentContentTransformation().isIdentity()) 329 imageBuffer.reset(); 330} 331 332FloatRect SVGRenderingContext::clampedAbsoluteTargetRect(const FloatRect& absoluteTargetRect) 333{ 334 const FloatSize maxImageBufferSize(kMaxImageBufferSize, kMaxImageBufferSize); 335 return FloatRect(absoluteTargetRect.location(), absoluteTargetRect.size().shrunkTo(maxImageBufferSize)); 336} 337 338IntSize SVGRenderingContext::clampedAbsoluteSize(const IntSize& absoluteSize) 339{ 340 const IntSize maxImageBufferSize(kMaxImageBufferSize, kMaxImageBufferSize); 341 return absoluteSize.shrunkTo(maxImageBufferSize); 342} 343 344void SVGRenderingContext::clear2DRotation(AffineTransform& transform) 345{ 346 AffineTransform::DecomposedType decomposition; 347 transform.decompose(decomposition); 348 decomposition.angle = 0; 349 transform.recompose(decomposition); 350} 351 352bool SVGRenderingContext::bufferForeground(std::unique_ptr<ImageBuffer>& imageBuffer) 353{ 354 ASSERT(m_paintInfo); 355 ASSERT(m_renderer->isSVGImage()); 356 FloatRect boundingBox = m_renderer->objectBoundingBox(); 357 358 // Invalidate an existing buffer if the scale is not correct. 359 if (imageBuffer) { 360 AffineTransform transform = m_paintInfo->context->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); 361 IntSize expandedBoundingBox = expandedIntSize(boundingBox.size()); 362 IntSize bufferSize(static_cast<int>(ceil(expandedBoundingBox.width() * transform.xScale())), static_cast<int>(ceil(expandedBoundingBox.height() * transform.yScale()))); 363 if (bufferSize != imageBuffer->internalSize()) 364 imageBuffer.reset(); 365 } 366 367 // Create a new buffer and paint the foreground into it. 368 if (!imageBuffer) { 369 if ((imageBuffer = m_paintInfo->context->createCompatibleBuffer(expandedIntSize(boundingBox.size()), true))) { 370 GraphicsContext* bufferedRenderingContext = imageBuffer->context(); 371 bufferedRenderingContext->translate(-boundingBox.x(), -boundingBox.y()); 372 PaintInfo bufferedInfo(*m_paintInfo); 373 bufferedInfo.context = bufferedRenderingContext; 374 toRenderSVGImage(m_renderer)->paintForeground(bufferedInfo); 375 } else 376 return false; 377 } 378 379 m_paintInfo->context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, boundingBox); 380 return true; 381} 382 383} 384