| Index: Source/core/rendering/svg/RenderSVGResourcePattern.cpp
|
| diff --git a/Source/core/rendering/svg/RenderSVGResourcePattern.cpp b/Source/core/rendering/svg/RenderSVGResourcePattern.cpp
|
| index 01b5829d595b7aaa1cbafad95be6c14b1d9c6c30..eb8096415095747702e6208eac5b02d4cfe1d52f 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,59 @@ 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;
|
| + auto addResult = m_patternMap.add(&object, nullptr);
|
| + OwnPtr<PatternData>& patternData = addResult.storedValue->value;
|
| +
|
| + if (addResult.isNewEntry)
|
| + 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 +137,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,82 +146,31 @@ SVGPaintServer RenderSVGResourcePattern::preparePaintServer(const RenderObject&
|
| return SVGPaintServer(patternData->pattern);
|
| }
|
|
|
| -static inline FloatRect calculatePatternBoundaries(const PatternAttributes& attributes,
|
| - const FloatRect& objectBoundingBox,
|
| - const SVGPatternElement* patternElement)
|
| -{
|
| - 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
|
| +PassRefPtr<DisplayList> RenderSVGResourcePattern::asDisplayList(const FloatRect& tileBounds,
|
| + const AffineTransform& tileTransform) 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;
|
| + ASSERT(!m_shouldCollectPatternAttributes);
|
|
|
| - GraphicsContext* tileImageContext = tileImage->context();
|
| - ASSERT(tileImageContext);
|
| - IntSize unclampedImageSize(roundedIntSize(absoluteTileBoundaries.size()));
|
| - tileImageContext->scale(unclampedImageSize.width() / absoluteTileBoundaries.width(), unclampedImageSize.height() / absoluteTileBoundaries.height());
|
| + AffineTransform contentTransform;
|
| + if (m_attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
|
| + contentTransform = tileTransform;
|
|
|
| - // 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());
|
| + // Draw the content into a DisplayList.
|
| + GraphicsContext recordingContext(nullptr);
|
| + recordingContext.beginRecording(FloatRect(FloatPoint(), tileBounds.size()));
|
| + recordingContext.concatCTM(tileTransform);
|
|
|
| - // Apply tile image transformations.
|
| - if (!tileImageTransform.isIdentity())
|
| - tileImageContext->concatCTM(tileImageTransform);
|
| + ASSERT(m_attributes.patternContentElement());
|
| + RenderSVGResourceContainer* patternRenderer =
|
| + toRenderSVGResourceContainer(m_attributes.patternContentElement()->renderer());
|
| + ASSERT(patternRenderer);
|
| + ASSERT(!patternRenderer->needsLayout());
|
|
|
| - AffineTransform contentTransformation;
|
| - if (attributes.patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX)
|
| - contentTransformation = tileImageTransform;
|
| -
|
| - SubtreeContentTransformScope contentTransformScope(contentTransformation);
|
| -
|
| - // 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());
|
| - }
|
| + SubtreeContentTransformScope contentTransformScope(contentTransform);
|
| + for (RenderObject* child = patternRenderer->firstChild(); child; child = child->nextSibling())
|
| + SVGRenderingContext::renderSubtree(&recordingContext, child);
|
|
|
| - return tileImage.release();
|
| + return recordingContext.endRecording();
|
| }
|
|
|
| }
|
|
|