Chromium Code Reviews| Index: Source/core/rendering/svg/RenderSVGResourcePattern.cpp |
| diff --git a/Source/core/rendering/svg/RenderSVGResourcePattern.cpp b/Source/core/rendering/svg/RenderSVGResourcePattern.cpp |
| index a15d4f3813c225a06ade3659b7b2e4fbc96cb201..8bd3305c5b90996e22c99ef6209c4470959135ec 100644 |
| --- a/Source/core/rendering/svg/RenderSVGResourcePattern.cpp |
| +++ b/Source/core/rendering/svg/RenderSVGResourcePattern.cpp |
| @@ -1,6 +1,7 @@ |
| /* |
| * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> |
| * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
| + * Copyright 2014 The Chromium Authors. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| @@ -24,10 +25,19 @@ |
| #include "core/dom/ElementTraversal.h" |
| #include "core/rendering/svg/SVGRenderingContext.h" |
| #include "core/svg/SVGFitToViewBox.h" |
| +#include "core/svg/SVGPatternElement.h" |
| +#include "platform/graphics/DisplayList.h" |
| #include "platform/graphics/GraphicsContext.h" |
| namespace blink { |
| +struct PatternData { |
| + WTF_MAKE_FAST_ALLOCATED; |
| +public: |
| + RefPtr<Pattern> pattern; |
| + AffineTransform transform; |
| +}; |
| + |
| const RenderSVGResourceType RenderSVGResourcePattern::s_resourceType = PatternResourceType; |
| RenderSVGResourcePattern::RenderSVGResourcePattern(SVGPatternElement* node) |
| @@ -50,63 +60,57 @@ void RenderSVGResourcePattern::removeClientFromCache(RenderObject* client, bool |
| markClientForInvalidation(client, markForInvalidation ? PaintInvalidation : ParentOnlyInvalidation); |
| } |
| -PatternData* RenderSVGResourcePattern::buildPattern(const RenderObject& object, const SVGPatternElement* patternElement) |
| +PatternData* RenderSVGResourcePattern::patternForRenderer(const RenderObject& object) |
| { |
| - PatternData* currentData = m_patternMap.get(&object); |
| - if (currentData && currentData->pattern) |
| - return currentData; |
| + OwnPtr<PatternData>& patternData = m_patternMap.add(&object, nullptr).storedValue->value; |
| + if (!patternData) |
| + patternData = buildPatternData(object); |
| + |
| + ASSERT(!m_shouldCollectPatternAttributes); |
| + return patternData.get(); |
| +} |
| +PassOwnPtr<PatternData> RenderSVGResourcePattern::buildPatternData(const RenderObject& object) |
| +{ |
| // If we couldn't determine the pattern content element root, stop here. |
| if (!m_attributes.patternContentElement()) |
| - return 0; |
| + return nullptr; |
| // An empty viewBox disables rendering. |
| if (m_attributes.hasViewBox() && m_attributes.viewBox().isEmpty()) |
| - return 0; |
| - |
| - // Compute all necessary transformations to build the tile image & the pattern. |
| - FloatRect tileBoundaries; |
| - AffineTransform tileImageTransform; |
| - if (!buildTileImageTransform(object, m_attributes, patternElement, tileBoundaries, tileImageTransform)) |
| - return 0; |
| - |
| - AffineTransform absoluteTransformIgnoringRotation; |
| - SVGRenderingContext::calculateDeviceSpaceTransformation(&object, absoluteTransformIgnoringRotation); |
| - |
| - // Ignore 2D rotation, as it doesn't affect the size of the tile. |
| - SVGRenderingContext::clear2DRotation(absoluteTransformIgnoringRotation); |
| - FloatRect absoluteTileBoundaries = absoluteTransformIgnoringRotation.mapRect(tileBoundaries); |
| - |
| - // Scale the tile size to match the scale level of the patternTransform. |
| - absoluteTileBoundaries.scale(static_cast<float>(m_attributes.patternTransform().xScale()), |
| - static_cast<float>(m_attributes.patternTransform().yScale())); |
| + return nullptr; |
| - // Build tile image. |
| - OwnPtr<ImageBuffer> tileImage = createTileImage(m_attributes, tileBoundaries, absoluteTileBoundaries, tileImageTransform); |
| - if (!tileImage) |
| - return 0; |
| + ASSERT(element()); |
| + // Compute tile metrics. |
| + FloatRect clientBoundingBox = object.objectBoundingBox(); |
| + FloatRect tileBounds = SVGLengthContext::resolveRectangle(element(), |
| + m_attributes.patternUnits(), clientBoundingBox, |
| + m_attributes.x(), m_attributes.y(), m_attributes.width(), m_attributes.height()); |
| + if (tileBounds.isEmpty()) |
| + return nullptr; |
| - RefPtr<Image> copiedImage = tileImage->copyImage(CopyBackingStore); |
| - if (!copiedImage) |
| - return 0; |
| + AffineTransform tileTransform; |
| + if (m_attributes.hasViewBox()) { |
| + if (m_attributes.viewBox().isEmpty()) |
| + return nullptr; |
| + tileTransform = SVGFitToViewBox::viewBoxToViewTransform(m_attributes.viewBox(), |
| + m_attributes.preserveAspectRatio(), tileBounds.width(), tileBounds.height()); |
| + } else { |
| + // A viewbox overrides patternContentUnits, per spec. |
| + if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) |
| + tileTransform.scale(clientBoundingBox.width(), clientBoundingBox.height()); |
| + } |
| - // Build pattern. |
| OwnPtr<PatternData> patternData = adoptPtr(new PatternData); |
| - patternData->pattern = Pattern::createBitmapPattern(copiedImage); |
| + patternData->pattern = Pattern::createDisplayListPattern(asDisplayList(tileBounds, tileTransform)); |
| // Compute pattern space transformation. |
| - const IntSize tileImageSize = tileImage->size(); |
| - patternData->transform.translate(tileBoundaries.x(), tileBoundaries.y()); |
| - patternData->transform.scale(tileBoundaries.width() / tileImageSize.width(), tileBoundaries.height() / tileImageSize.height()); |
| - |
| + patternData->transform.translate(tileBounds.x(), tileBounds.y()); |
| AffineTransform patternTransform = m_attributes.patternTransform(); |
| if (!patternTransform.isIdentity()) |
| patternData->transform = patternTransform * patternData->transform; |
| - // Various calls above may trigger invalidations in some fringe cases (ImageBuffer allocation |
| - // failures in the SVG image cache for example). To avoid having our PatternData deleted by |
| - // removeAllClientsFromCache(), we only make it visible in the cache at the very end. |
| - return m_patternMap.set(&object, patternData.release()).storedValue->value.get(); |
| + return patternData.release(); |
| } |
| SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject& object) |
| @@ -131,7 +135,7 @@ SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject& |
| if (m_attributes.patternUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX && objectBoundingBox.isEmpty()) |
| return SVGPaintServer::invalid(); |
| - PatternData* patternData = buildPattern(object, patternElement); |
| + PatternData* patternData = patternForRenderer(object); |
| if (!patternData) |
| return SVGPaintServer::invalid(); |
| @@ -140,80 +144,32 @@ SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject& |
| return SVGPaintServer(patternData->pattern); |
| } |
| -static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes, |
| - const FloatRect& objectBoundingBox, |
| - const SVGPatternElement* patternElement) |
| +PassRefPtr<DisplayList> RenderSVGResourcePattern::asDisplayList(const FloatRect& tileBounds, |
| + const AffineTransform& tileTransform) const |
| { |
| - ASSERT(patternElement); |
| - return SVGLengthContext::resolveRectangle(patternElement, attributes.patternUnits(), objectBoundingBox, attributes.x(), attributes.y(), attributes.width(), attributes.height()); |
| -} |
| - |
| -bool RenderSVGResourcePattern::buildTileImageTransform(const RenderObject& renderer, |
| - const PatternAttributes& attributes, |
| - const SVGPatternElement* patternElement, |
| - FloatRect& patternBoundaries, |
| - AffineTransform& tileImageTransform) const |
| -{ |
| - ASSERT(patternElement); |
| - |
| - FloatRect objectBoundingBox = renderer.objectBoundingBox(); |
| - patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); |
| - if (patternBoundaries.width() <= 0 || patternBoundaries.height() <= 0) |
| - return false; |
| - |
| - AffineTransform viewBoxCTM = SVGFitToViewBox::viewBoxToViewTransform(attributes.viewBox(), attributes.preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); |
| - |
| - // Apply viewBox/objectBoundingBox transformations. |
| - if (!viewBoxCTM.isIdentity()) |
| - tileImageTransform = viewBoxCTM; |
| - else if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) |
| - tileImageTransform.scale(objectBoundingBox.width(), objectBoundingBox.height()); |
| - |
| - return true; |
| -} |
| - |
| -PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(const PatternAttributes& attributes, |
| - const FloatRect& tileBoundaries, |
| - const FloatRect& absoluteTileBoundaries, |
| - const AffineTransform& tileImageTransform) const |
| -{ |
| - FloatRect clampedAbsoluteTileBoundaries = SVGRenderingContext::clampedAbsoluteTargetRect(absoluteTileBoundaries); |
| - |
| - IntSize imageSize(roundedIntSize(clampedAbsoluteTileBoundaries.size())); |
| - if (imageSize.isEmpty()) |
| - return nullptr; |
| - OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); |
| - if (!tileImage) |
| - return nullptr; |
| - |
| - GraphicsContext* tileImageContext = tileImage->context(); |
| - ASSERT(tileImageContext); |
| - IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size())); |
| - tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height()); |
| - |
| - // The image buffer represents the final rendered size, so the content has to be scaled (to avoid pixelation). |
| - tileImageContext->scale( |
| - clampedAbsoluteTileBoundaries.width() / tileBoundaries.width(), |
| - clampedAbsoluteTileBoundaries.height() / tileBoundaries.height()); |
| - |
| - // Apply tile image transformations. |
| - if (!tileImageTransform.isIdentity()) |
| - tileImageContext->concatCTM(tileImageTransform); |
| - |
| - AffineTransform contentTransformation; |
| - if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) |
| - contentTransformation = tileImageTransform; |
| - |
| - // Draw the content into the ImageBuffer. |
| - for (SVGElement* element = Traversal<SVGElement>::firstChild(*attributes.patternContentElement()); element; element = Traversal<SVGElement>::nextSibling(*element)) { |
| - if (!element->renderer()) |
| - continue; |
| - if (element->renderer()->needsLayout()) |
| - return nullptr; |
| - SVGRenderingContext::renderSubtree(tileImage->context(), element->renderer(), contentTransformation); |
| + ASSERT(!m_shouldCollectPatternAttributes); |
| + |
| + AffineTransform contentTransform; |
| + if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) |
| + contentTransform = tileTransform; |
| + |
| + // Draw the content into a DisplayList. |
| + GraphicsContext recordingContext(nullptr); |
| + recordingContext.beginRecording(FloatRect(FloatPoint(), tileBounds.size())); |
| + recordingContext.concatCTM(tileTransform); |
| + |
| + ASSERT(m_attributes.patternContentElement()); |
| + RenderSVGResourceContainer* patternRenderer = |
| + toRenderSVGResourceContainer(m_attributes.patternContentElement()->renderer()); |
| + ASSERT(patternRenderer); |
| + ASSERT(!patternRenderer->needsLayout()); |
| + |
| + for (RenderObject* child = patternRenderer->firstChild(); child; child = child->nextSibling()) { |
| + ASSERT(!child->needsLayout()); |
|
fs
2014/10/21 20:12:35
I think the below asserts this too.
f(malita)
2014/10/21 22:16:32
Done.
|
| + SVGRenderingContext::renderSubtree(&recordingContext, child, contentTransform); |
|
fs
2014/10/21 20:12:35
I began to wonder if this call really did any good
f(malita)
2014/10/21 22:16:32
I would very much like to get rid of renderSubtree
fs
2014/10/22 09:21:31
I think what I was getting at though is that for t
|
| } |
| - return tileImage.release(); |
| + return recordingContext.endRecording(); |
| } |
| } |