1 /* 2 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann (at) 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 23 #include "core/rendering/svg/RenderSVGResourcePattern.h" 24 25 #include "core/dom/ElementTraversal.h" 26 #include "core/rendering/svg/SVGRenderSupport.h" 27 #include "core/rendering/svg/SVGRenderingContext.h" 28 #include "core/svg/SVGFitToViewBox.h" 29 #include "platform/graphics/GraphicsContext.h" 30 31 namespace blink { 32 33 const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType; 34 35 RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node) 36 : RenderSVGResourceContainer(node) 37 , m_shouldCollectPatternAttributes(true) 38 { 39 } 40 41 void RenderSVGResourcePattern::removeAllClientsFromCache(bool markForInvalidation) 42 { 43 m_patternMap.clear(); 44 m_shouldCollectPatternAttributes = true; 45 markAllClientsForInvalidation(markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation); 46 } 47 48 void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool markForInvalidation) 49 { 50 ASSERT(client); 51 m_patternMap.remove(client); 52 markClientForInvalidation(client, markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation); 53 } 54 55 PatternData* RenderSVGResourcePattern::buildPattern(RenderObject* object, const SVGPatternElement* patternElement) 56 { 57 ASSERT(object); 58 PatternData* currentData = m_patternMap.get(object); 59 if (currentData && currentData->pattern) 60 return currentData; 61 62 // If we couldn't determine the pattern content element root, stop here. 63 if (!m_attributes.patternContentElement()) 64 return 0; 65 66 // An empty viewBox disables rendering. 67 if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) 68 return 0; 69 70 // Compute all necessary transformations to build the tile image & the pattern. 71 FloatRect tileBoundaries; 72 AffineTransform tileImageTransform; 73 if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) 74 return 0; 75 76 AffineTransform absoluteTransformIgnoringRotation; 77 SVGRenderingContext::calculateDeviceSpaceTransformation(object, absoluteTransformIgnoringRotation); 78 79 // Ignore 2D rotation, as it doesn't affect the size of the tile. 80 SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); 81 FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); 82 83 // Scale the tile size to match the scale level of the patternTransform. 84 absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), 85 static_cast<float>(m_attributes.patternTransform().yScale())); 86 87 // Build tile image. 88 OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform); 89 if (!tileImage) 90 return 0; 91 92 RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); 93 if (!copiedImage) 94 return 0; 95 96 // Build pattern. 97 OwnPtr<PatternData> patternData = adoptPtr(new PatternData); 98 patternData->pattern = Pattern::createBitmapPattern(copiedImage); 99 100 // Compute pattern space transformation. 101 const IntSize tileImageSize = tileImage->size(); 102 patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); 103 patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height()); 104 105 AffineTransform patternTransform = m_attributes.patternTransform(); 106 if (!patternTransform.isIdentity()) 107 patternData->transform = patternTransform * patternData->transform; 108 109 // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation 110 // failures in the SVG image cache for example). To avoid having our PatternData deleted by 111 // removeAllClientsFromCache(), we only make it visible in the cache at the very end. 112 return m_patternMap.set(object, patternData.release()).storedValue->value.get(); 113 } 114 115 bool RenderSVGResourcePattern::applyResource(RenderObject* object, RenderStyle* style, GraphicsContext*& context, unsigned short resourceMode) 116 { 117 ASSERT(object); 118 ASSERT(style); 119 ASSERT(context); 120 ASSERT(resourceMode != ApplyToDefaultMode); 121 122 clearInvalidationMask(); 123 124 SVGPatternElement* patternElement = toSVGPatternElement(element()); 125 if (!patternElement) 126 return false; 127 128 if (m_shouldCollectPatternAttributes) { 129 patternElement->synchronizeAnimatedSVGAttribute(anyQName()); 130 131 m_attributes = PatternAttributes(); 132 patternElement->collectPatternAttributes(m_attributes); 133 m_shouldCollectPatternAttributes = 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 (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) 140 return false; 141 142 PatternData* patternData = buildPattern(object, patternElement); 143 if (!patternData) 144 return false; 145 146 const SVGRenderStyle& svgStyle = style->svgStyle(); 147 148 AffineTransform computedPatternSpaceTransform = computeResourceSpaceTransform(object, patternData->transform, svgStyle, resourceMode); 149 patternData->pattern->setPatternSpaceTransform(computedPatternSpaceTransform); 150 151 // Draw pattern 152 context->save(); 153 154 if (resourceMode & ApplyToFillMode) { 155 context->setAlphaAsFloat(svgStyle.fillOpacity()); 156 context->setFillPattern(patternData->pattern); 157 context->setFillRule(svgStyle.fillRule()); 158 } else if (resourceMode & ApplyToStrokeMode) { 159 context->setAlphaAsFloat(svgStyle.strokeOpacity()); 160 context->setStrokePattern(patternData->pattern); 161 SVGRenderSupport::applyStrokeStyleToContext(context, style, object); 162 } 163 164 if (resourceMode & ApplyToTextMode) { 165 if (resourceMode & ApplyToFillMode) 166 context->setTextDrawingMode(TextModeFill); 167 else if (resourceMode & ApplyToStrokeMode) 168 context->setTextDrawingMode(TextModeStroke); 169 } 170 171 return true; 172 } 173 174 void RenderSVGResourcePattern::postApplyResource(RenderObject*, GraphicsContext*& context) 175 { 176 ASSERT(context); 177 context->restore(); 178 } 179 180 static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes, 181 const FloatRect& objectBoundingBox, 182 const SVGPatternElement* patternElement) 183 { 184 ASSERT(patternElement); 185 return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height()); 186 } 187 188 bool RenderSVGResourcePattern::buildTileImageTransform(RenderObject* renderer, 189 const PatternAttributes& attributes, 190 const SVGPatternElement* patternElement, 191 FloatRect& patternBoundaries, 192 AffineTransform& tileImageTransform) const 193 { 194 ASSERT(renderer); 195 ASSERT(patternElement); 196 197 FloatRect objectBoundingBox = renderer->objectBoundingBox(); 198 patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); 199 if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) 200 return false; 201 202 AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); 203 204 // Apply viewBox/objectBoundingBox transformations. 205 if (!viewBoxCTM.isIdentity()) 206 tileImageTransform = viewBoxCTM; 207 else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) 208 tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); 209 210 return true; 211 } 212 213 PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, 214 const FloatRect& tileBoundaries, 215 const FloatRect& absoluteTileBoundaries, 216 const AffineTransform& tileImageTransform) const 217 { 218 FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); 219 220 IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size())); 221 if (imageSize.isEmpty()) 222 return nullptr; 223 OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); 224 if (!tileImage) 225 return nullptr; 226 227 GraphicsContext* tileImageContext = tileImage->context(); 228 ASSERT(tileImageContext); 229 IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size())); 230 tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height()); 231 232 // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). 233 tileImageContext->scale( 234 clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), 235 clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()); 236 237 // Apply tile image transformations. 238 if (!tileImageTransform.isIdentity()) 239 tileImageContext->concatCTM(tileImageTransform); 240 241 AffineTransform contentTransformation; 242 if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) 243 contentTransformation = tileImageTransform; 244 245 // Draw the content into the ImageBuffer. 246 for (SVGElement* element = Traversal<SVGElement>::firstChild(*attributes.patternContentElement()); element; element = Traversal<SVGElement>::nextSibling(*element)) { 247 if (!element->renderer()) 248 continue; 249 if (element->renderer()->needsLayout()) 250 return nullptr; 251 SVGRenderingContext::renderSubtree(tileImage->context(), element->renderer(), contentTransformation); 252 } 253 254 return tileImage.release(); 255 } 256 257 } 258