1/* 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#include "config.h" 22#include "RenderSVGResourcePattern.h" 23 24#include "ElementIterator.h" 25#include "FrameView.h" 26#include "GraphicsContext.h" 27#include "RenderSVGRoot.h" 28#include "SVGFitToViewBox.h" 29#include "SVGRenderingContext.h" 30 31namespace WebCore { 32 33RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType; 34 35RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement& element, PassRef<RenderStyle> style) 36 : RenderSVGResourceContainer(element, WTF::move(style)) 37 , m_shouldCollectPatternAttributes(true) 38{ 39} 40 41SVGPatternElement& RenderSVGResourcePattern::patternElement() const 42{ 43 return toSVGPatternElement(RenderSVGResourceContainer::element()); 44} 45 46void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation) 47{ 48 m_patternMap.clear(); 49 m_shouldCollectPatternAttributes = true; 50 markAllClientsForInvalidation(markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); 51} 52 53void RenderSVGResourcePattern::removeClientFromCache(RenderElement& client, bool markForInvalidation) 54{ 55 m_patternMap.remove(&client); 56 markClientForInvalidation(client, markForInvalidation ? RepaintInvalidation : ParentOnlyInvalidation); 57} 58 59PatternData* RenderSVGResourcePattern::buildPattern(RenderElement& renderer, unsigned short resourceMode) 60{ 61 PatternData* currentData = m_patternMap.get(&renderer); 62 if (currentData && currentData->pattern) 63 return currentData; 64 65 if (m_shouldCollectPatternAttributes) { 66 patternElement().synchronizeAnimatedSVGAttribute(anyQName()); 67 68 m_attributes = PatternAttributes(); 69 patternElement().collectPatternAttributes(m_attributes); 70 m_shouldCollectPatternAttributes = false; 71 } 72 73 // If we couldn't determine the pattern content element root, stop here. 74 if (!m_attributes.patternContentElement()) 75 return 0; 76 77 // An empty viewBox disables rendering. 78 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) 79 return 0; 80 81 // Compute all necessary transformations to build the tile image & the pattern. 82 FloatRect tileBoundaries; 83 AffineTransform tileImageTransform; 84 if (!buildTileImageTransform(renderer, m_attributes, patternElement(), tileBoundaries, tileImageTransform)) 85 return 0; 86 87 AffineTransform absoluteTransformIgnoringRotation; 88 SVGRenderingContext::calculateTransformationToOutermostCoordinateSystem(renderer, absoluteTransformIgnoringRotation); 89 90 // Ignore 2D rotation, as it doesn't affect the size of the tile. 91 SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); 92 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); 93 FloatRect clampedAbsoluteTileBoundaries; 94 95 // Scale the tile size to match the scale level of the patternTransform. 96 absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), 97 static_cast<float>(m_attributes.patternTransform().yScale())); 98 99 // Build tile image. 100 std::unique_ptr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform, clampedAbsoluteTileBoundaries); 101 if (!tileImage) 102 return 0; 103 104 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); 105 if (!copiedImage) 106 return 0; 107 108 // Build pattern. 109 auto patternData = std::make_unique<PatternData>(); 110 patternData->pattern = Pattern::create(copiedImage, true, true); 111 112 // Compute pattern space transformation. 113 const IntSize tileImageSize = tileImage->logicalSize(); 114 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); 115 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height()); 116 117 AffineTransform patternTransform = m_attributes.patternTransform(); 118 if (!patternTransform.isIdentity()) 119 patternData->transform = patternTransform * patternData->transform; 120 121 // Account for text drawing resetting the context to non-scaled, see SVGInlineTextBox::paintTextWithShadows. 122 if (resourceMode & ApplyToTextMode) { 123 AffineTransform additionalTextTransformation; 124 if (shouldTransformOnTextPainting(renderer, additionalTextTransformation)) 125 patternData->transform *= additionalTextTransformation; 126 } 127 patternData->pattern->setPatternSpaceTransform(patternData->transform); 128 129 // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation 130 // failures in the SVG image cache for example). To avoid having our PatternData deleted by 131 // removeAllClientsFromCache(), we only make it visible in the cache at the very end. 132 return m_patternMap.set(&renderer, WTF::move(patternData)).iterator->value.get(); 133} 134 135bool RenderSVGResourcePattern::applyResource(RenderElement& renderer, const RenderStyle& style, GraphicsContext*& context, unsigned short resourceMode) 136{ 137 ASSERT(context); 138 ASSERT(resourceMode != ApplyToDefaultMode); 139 140 // Spec: When the geometry of the applicable element has no width or height and objectBoundingBox is specified, 141 // then the given effect (e.g. a gradient or a filter) will be ignored. 142 FloatRect objectBoundingBox = renderer.objectBoundingBox(); 143 if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) 144 return false; 145 146 PatternData* patternData = buildPattern(renderer, resourceMode); 147 if (!patternData) 148 return false; 149 150 // Draw pattern 151 context->save(); 152 153 const SVGRenderStyle& svgStyle = style.svgStyle(); 154 155 if (resourceMode & ApplyToFillMode) { 156 context->setAlpha(svgStyle.fillOpacity()); 157 context->setFillPattern(patternData->pattern); 158 context->setFillRule(svgStyle.fillRule()); 159 } else if (resourceMode & ApplyToStrokeMode) { 160 if (svgStyle.vectorEffect() == VE_NON_SCALING_STROKE) 161 patternData->pattern->setPatternSpaceTransform(transformOnNonScalingStroke(&renderer, patternData->transform)); 162 context->setAlpha(svgStyle.strokeOpacity()); 163 context->setStrokePattern(patternData->pattern); 164 SVGRenderSupport::applyStrokeStyleToContext(context, style, renderer); 165 } 166 167 if (resourceMode & ApplyToTextMode) { 168 if (resourceMode & ApplyToFillMode) { 169 context->setTextDrawingMode(TextModeFill); 170 171#if USE(CG) 172 context->applyFillPattern(); 173#endif 174 } else if (resourceMode & ApplyToStrokeMode) { 175 context->setTextDrawingMode(TextModeStroke); 176 177#if USE(CG) 178 context->applyStrokePattern(); 179#endif 180 } 181 } 182 183 return true; 184} 185 186void RenderSVGResourcePattern::postApplyResource(RenderElement&, GraphicsContext*& context, unsigned short resourceMode, const Path* path, const RenderSVGShape* shape) 187{ 188 ASSERT(context); 189 ASSERT(resourceMode != ApplyToDefaultMode); 190 191 if (resourceMode & ApplyToFillMode) { 192 if (path) 193 context->fillPath(*path); 194 else if (shape) 195 shape->fillShape(context); 196 } 197 if (resourceMode & ApplyToStrokeMode) { 198 if (path) 199 context->strokePath(*path); 200 else if (shape) 201 shape->strokeShape(context); 202 } 203 204 context->restore(); 205} 206 207static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes, 208 const FloatRect& objectBoundingBox, 209 const SVGPatternElement& patternElement) 210{ 211 return SVGLengthContext::resolveRectangle(&patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height()); 212} 213 214bool RenderSVGResourcePattern::buildTileImageTransform(RenderElement& renderer, 215 const PatternAttributes& attributes, 216 const SVGPatternElement& patternElement, 217 FloatRect& patternBoundaries, 218 AffineTransform& tileImageTransform) const 219{ 220 FloatRect objectBoundingBox = renderer.objectBoundingBox(); 221 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 222 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) 223 return false; 224 225 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); 226 227 // Apply viewBox/objectBoundingBox transformations. 228 if (!viewBoxCTM.isIdentity()) 229 tileImageTransform = viewBoxCTM; 230 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) 231 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); 232 233 return true; 234} 235 236std::unique_ptr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, const FloatRect& tileBoundaries, const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform, FloatRect& clampedAbsoluteTileBoundaries) const 237{ 238 clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); 239 240 std::unique_ptr<ImageBuffer> tileImage; 241 242 if (!SVGRenderingContext::createImageBufferForPattern(absoluteTileBoundaries, clampedAbsoluteTileBoundaries, tileImage, ColorSpaceDeviceRGB, Unaccelerated)) 243 return nullptr; 244 245 GraphicsContext* tileImageContext = tileImage->context(); 246 ASSERT(tileImageContext); 247 248 // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). 249 tileImageContext->scale(FloatSize(clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), 250 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height())); 251 252 // Apply tile image transformations. 253 if (!tileImageTransform.isIdentity()) 254 tileImageContext->concatCTM(tileImageTransform); 255 256 AffineTransform contentTransformation; 257 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) 258 contentTransformation = tileImageTransform; 259 260 // Draw the content into the ImageBuffer. 261 for (auto& child : childrenOfType<SVGElement>(*attributes.patternContentElement())) { 262 if (!child.renderer()) 263 continue; 264 if (child.renderer()->needsLayout()) 265 return nullptr; 266 SVGRenderingContext::renderSubtreeToImageBuffer(tileImage.get(), *child.renderer(), contentTransformation); 267 } 268 269 return tileImage; 270} 271 272} 273