Commit 770c6456 authored by fmalita@chromium.org's avatar fmalita@chromium.org

[SVG] DisplayList-based patterns.

Instead of building an ImageBuffer tile (with all the ills of record
time rasterization), build a DisplayList tile and use Skia's
new SkPictureShader mechanism to fill/stroke the shape.

To facilitate this, Pattern is extended to support both bitmap and
picture shaders (the latter only in repeatXY mode -- the only mode
currently used by SVG).

Patterns (and their SkShaders) are cached per RenderSVGResourcePattern
per client. Internally, SkPictureShader also caches its rasterized tile
so there should be no performance degradation.

A handful of tests (about 9) require minor rebaselining (and a couple
have been updated for incorrect patternUnits).

R=pdr@chromium.org,schenney@chromium.org,fs@opera.com,ed@opera.com
BUG=401814,425278

Review URL: https://codereview.chromium.org/453653003

git-svn-id: svn://svn.chromium.org/blink/trunk@184271 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 2be03bf8
......@@ -826,6 +826,15 @@ crbug.com/350829 [ Win Debug ] virtual/threaded/animations/missing-keyframe-prop
crbug.com/350829 [ Win Debug ] virtual/threaded/animations/duplicated-keyframes-name-unprefixed-03.html [ Timeout Pass ]
crbug.com/350829 [ Win Debug ] virtual/threaded/animations/keyframes-unprefixed-03.html [ Timeout Pass ]
crbug.com/401814 svg/W3C-SVG-1.1/pservers-grad-06-b.svg [ NeedsRebaseline ]
crbug.com/401814 svg/custom/nested-pattern-boundingBoxModeContent.svg [ NeedsRebaseline ]
crbug.com/401814 svg/custom/non-scaling-stroke.svg [ NeedsRebaseline ]
crbug.com/401814 svg/custom/pattern-scaled-pattern-space.svg [ NeedsRebaseline ]
crbug.com/401814 svg/custom/pattern-skew-transformed.svg [ NeedsRebaseline ]
crbug.com/401814 svg/custom/pattern-with-transformation.svg [ NeedsRebaseline ]
crbug.com/401814 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ NeedsRebaseline ]
crbug.com/401814 svg/transforms/text-with-pattern-with-svg-transform.svg [ NeedsRebaseline ]
crbug.com/352377 svg/animations/repeatn-event-1c.svg [ ImageOnlyFailure Pass ]
crbug.com/352385 [ Debug ] virtual/threaded/animations/3d/transform-perspective.html [ Timeout Pass ]
crbug.com/352705 [ Win Debug ] virtual/threaded/animations/sample-on-last-keyframe.html [ Timeout Pass ]
......
/*
* 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();
}
}
/*
* 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
......@@ -23,24 +24,18 @@
#include "core/rendering/svg/RenderSVGResourceContainer.h"
#include "core/svg/PatternAttributes.h"
#include "core/svg/SVGPatternElement.h"
#include "core/svg/SVGUnitTypes.h"
#include "platform/geometry/FloatRect.h"
#include "platform/graphics/ImageBuffer.h"
#include "platform/graphics/Pattern.h"
#include "platform/transforms/AffineTransform.h"
#include "wtf/HashMap.h"
#include "wtf/OwnPtr.h"
#include "wtf/RefPtr.h"
namespace blink {
struct PatternData {
WTF_MAKE_FAST_ALLOCATED;
public:
RefPtr<Pattern> pattern;
AffineTransform transform;
};
class AffineTransform;
class DisplayList;
class FloatRect;
class SVGPatternElement;
struct PatternData;
class RenderSVGResourcePattern final : public RenderSVGResourceContainer {
public:
......@@ -57,15 +52,19 @@ public:
static const RenderSVGResourceType s_resourceType;
private:
bool buildTileImageTransform(const RenderObject&, const PatternAttributes&, const SVGPatternElement*, FloatRect& patternBoundaries, AffineTransform& tileImageTransform) const;
PassOwnPtr<ImageBuffer> createTileImage(const PatternAttributes&, const FloatRect& tileBoundaries,
const FloatRect& absoluteTileBoundaries, const AffineTransform& tileImageTransform) const;
PatternData* buildPattern(const RenderObject&, const SVGPatternElement*);
PassOwnPtr<PatternData> buildPatternData(const RenderObject&);
PassRefPtr<DisplayList> asDisplayList(const FloatRect& tileBounds, const AffineTransform&) const;
PatternData* patternForRenderer(const RenderObject&);
bool m_shouldCollectPatternAttributes : 1;
PatternAttributes m_attributes;
// FIXME: we can almost do away with this per-object map, but not quite: the tile size can be
// relative to the client bounding box, and it gets captured in the cached Pattern shader.
// Hence, we need one Pattern shader per client. The display list OTOH is the same => we
// should be able to cache a single display list per RenderSVGResourcePattern + one
// Pattern(shader) for each client -- this would avoid re-recording when multiple clients
// share the same pattern.
HashMap<const RenderObject*, OwnPtr<PatternData> > m_patternMap;
};
......
......@@ -37,8 +37,6 @@
#include "core/rendering/svg/SVGResourcesCache.h"
#include "platform/FloatConversion.h"
static int kMaxImageBufferSize = 4096;
namespace blink {
SVGRenderingContext::~SVGRenderingContext()
......@@ -254,20 +252,6 @@ void SVGRenderingContext::renderSubtree(GraphicsContext* context, RenderObject*
item->paint(info, IntPoint());
}
FloatRect SVGRenderingContext::clampedAbsoluteTargetRect(const FloatRect& absoluteTargetRect)
{
const FloatSize maxImageBufferSize(kMaxImageBufferSize, kMaxImageBufferSize);
return FloatRect(absoluteTargetRect.location(), absoluteTargetRect.size().shrunkTo(maxImageBufferSize));
}
void SVGRenderingContext::clear2DRotation(AffineTransform& transform)
{
AffineTransform::DecomposedType decomposition;
transform.decompose(decomposition);
decomposition.angle = 0;
transform.recompose(decomposition);
}
bool SVGRenderingContext::bufferForeground(OwnPtr<ImageBuffer>& imageBuffer)
{
ASSERT(m_paintInfo);
......
......@@ -86,8 +86,6 @@ public:
static float calculateScreenFontSizeScalingFactor(const RenderObject*);
static void calculateDeviceSpaceTransformation(const RenderObject*, AffineTransform& absoluteTransform);
static FloatRect clampedAbsoluteTargetRect(const FloatRect& absoluteTargetRect);
static void clear2DRotation(AffineTransform&);
// Support for the buffered-rendering hint.
bool bufferForeground(OwnPtr<ImageBuffer>&);
......
......@@ -559,6 +559,8 @@
'graphics/DeferredImageDecoder.cpp',
'graphics/DeferredImageDecoder.h',
'graphics/DisplayList.h',
'graphics/DisplayListPattern.cpp',
'graphics/DisplayListPattern.h',
'graphics/DrawLooperBuilder.cpp',
'graphics/DrawLooperBuilder.h',
'graphics/FirstPaintInvalidationTracking.cpp',
......
......@@ -18,9 +18,9 @@ public:
virtual ~BitmapPattern() { }
protected:
virtual PassRefPtr<SkShader> createShader() override;
protected:
virtual SkImageInfo getBitmapInfo() override;
virtual void drawBitmapToCanvas(SkCanvas&, SkPaint&) override;
......
......@@ -14,9 +14,9 @@ public:
BitmapPatternBase(RepeatMode, int64_t externalMemoryAllocated = 0);
virtual ~BitmapPatternBase();
protected:
virtual PassRefPtr<SkShader> createShader() override;
protected:
virtual SkImageInfo getBitmapInfo() = 0;
virtual void drawBitmapToCanvas(SkCanvas&, SkPaint&) = 0;
};
......
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "config.h"
#include "platform/graphics/DisplayListPattern.h"
#include "platform/graphics/DisplayList.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "third_party/skia/include/core/SkShader.h"
namespace blink {
DisplayListPattern::DisplayListPattern(PassRefPtr<DisplayList> displayList, RepeatMode mode)
: Pattern(mode)
, m_tileDisplayList(displayList)
{
// All current clients use RepeatModeXY, so we only support this mode for now.
ASSERT(isRepeatXY());
// FIXME: we don't have a good way to account for DL memory utilization.
}
DisplayListPattern::~DisplayListPattern()
{
}
PassRefPtr<SkShader> DisplayListPattern::createShader()
{
SkMatrix localMatrix = affineTransformToSkMatrix(m_patternSpaceTransformation);
SkRect tileBounds = SkRect::MakeWH(m_tileDisplayList->bounds().width(),
m_tileDisplayList->bounds().height());
return adoptRef(SkShader::CreatePictureShader(m_tileDisplayList->picture(),
SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix, &tileBounds));
}
} // namespace
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef DisplayListPattern_h
#define DisplayListPattern_h
#include "platform/graphics/Pattern.h"
namespace blink {
class DisplayList;
class PLATFORM_EXPORT DisplayListPattern : public Pattern {
public:
static PassRefPtr<DisplayListPattern> create(PassRefPtr<DisplayList> displayList,
RepeatMode repeatMode)
{
return adoptRef(new DisplayListPattern(displayList, repeatMode));
}
virtual ~DisplayListPattern();
protected:
virtual PassRefPtr<SkShader> createShader() override;
private:
DisplayListPattern(PassRefPtr<DisplayList>, RepeatMode);
RefPtr<DisplayList> m_tileDisplayList;
};
} // namespace
#endif
......@@ -29,6 +29,8 @@
#include "platform/graphics/Pattern.h"
#include "platform/graphics/BitmapPattern.h"
#include "platform/graphics/DisplayList.h"
#include "platform/graphics/DisplayListPattern.h"
#include "platform/graphics/StaticBitmapPattern.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkShader.h"
......@@ -44,6 +46,12 @@ PassRefPtr<Pattern> Pattern::createBitmapPattern(PassRefPtr<Image> tileImage, Re
return BitmapPattern::create(tileImage, repeatMode);
}
PassRefPtr<Pattern> Pattern::createDisplayListPattern(PassRefPtr<DisplayList> displayList,
RepeatMode repeatMode)
{
return DisplayListPattern::create(displayList, repeatMode);
}
Pattern::Pattern(RepeatMode repeatMode, int64_t externalMemoryAllocated)
: m_repeatMode(repeatMode)
, m_externalMemoryAllocated(0)
......
......@@ -41,6 +41,8 @@ class SkShader;
namespace blink {
class DisplayList;
class PLATFORM_EXPORT Pattern : public RefCounted<Pattern> {
public:
enum RepeatMode {
......@@ -53,6 +55,8 @@ public:
static PassRefPtr<Pattern> createBitmapPattern(PassRefPtr<Image> tileImage,
RepeatMode = RepeatModeXY);
static PassRefPtr<Pattern> createDisplayListPattern(PassRefPtr<DisplayList>,
RepeatMode = RepeatModeXY);
virtual ~Pattern();
SkShader* shader();
......
......@@ -15,9 +15,9 @@ public:
virtual ~StaticBitmapPattern();
protected:
virtual PassRefPtr<SkShader> createShader() override;
protected:
virtual SkImageInfo getBitmapInfo() override;
virtual void drawBitmapToCanvas(SkCanvas&, SkPaint&) override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment