Commit 12c97dea authored by schenney@chromium.org's avatar schenney@chromium.org

Modifications to DisplayList for better Slimming Paint

Add clipping.
Add transform.
Change recording to be driven by GraphicsContext.
Update dependent code.
Move a method used only by SVGFEImage from AffineTransform to SVGFEImage

R=pdr,senorblanco
BUG=410019

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

git-svn-id: svn://svn.chromium.org/blink/trunk@181502 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent bc325ac0
......@@ -245,13 +245,13 @@ void RenderSVGResourceClipper::drawClipMaskContent(GraphicsContext* context, con
}
if (!m_clipContentDisplayList)
m_clipContentDisplayList = asDisplayList(context, contentTransformation);
createDisplayList(context, contentTransformation);
ASSERT(m_clipContentDisplayList);
context->drawDisplayList(m_clipContentDisplayList.get());
}
PassRefPtr<DisplayList> RenderSVGResourceClipper::asDisplayList(GraphicsContext* context,
void RenderSVGResourceClipper::createDisplayList(GraphicsContext* context,
const AffineTransform& contentTransformation)
{
ASSERT(context);
......@@ -260,7 +260,8 @@ PassRefPtr<DisplayList> RenderSVGResourceClipper::asDisplayList(GraphicsContext*
// Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection
// with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and
// userSpaceOnUse units (http://crbug.com/294900).
context->beginRecording(strokeBoundingBox());
FloatRect bounds = strokeBoundingBox();
context->beginRecording(bounds);
// Switch to a paint behavior where all children of this <clipPath> will be rendered using special constraints:
// - fill-opacity/stroke-opacity/opacity set to 1
......@@ -304,7 +305,7 @@ PassRefPtr<DisplayList> RenderSVGResourceClipper::asDisplayList(GraphicsContext*
frame()->view()->setPaintBehavior(oldBehavior);
return context->endRecording();
m_clipContentDisplayList = context->endRecording();
}
void RenderSVGResourceClipper::calculateClipContentPaintInvalidationRect()
......
......@@ -76,7 +76,7 @@ public:
private:
bool tryPathOnlyClipping(GraphicsContext*, const AffineTransform&, const FloatRect&);
void drawClipMaskContent(GraphicsContext*, const FloatRect& targetBoundingBox);
PassRefPtr<DisplayList> asDisplayList(GraphicsContext*, const AffineTransform&);
void createDisplayList(GraphicsContext*, const AffineTransform&);
void calculateClipContentPaintInvalidationRect();
RefPtr<DisplayList> m_clipContentDisplayList;
......
......@@ -144,6 +144,7 @@ static bool createImageBuffer(const Filter* filter, OwnPtr<ImageBuffer>& imageBu
static void beginDeferredFilter(GraphicsContext* context, FilterData* filterData)
{
context->beginRecording(filterData->boundaries);
// We pass the boundaries to SkPictureImageFilter so it knows the
// world-space position of the filter primitives. It gets them
// from the DisplayList, which also applies the inverse translate
......
......@@ -122,12 +122,12 @@ void RenderSVGResourceMasker::drawMaskForRenderer(GraphicsContext* context, cons
}
if (!m_maskContentDisplayList)
m_maskContentDisplayList = asDisplayList(context, contentTransformation);
createDisplayList(context, contentTransformation);
ASSERT(m_maskContentDisplayList);
context->drawDisplayList(m_maskContentDisplayList.get());
}
PassRefPtr<DisplayList> RenderSVGResourceMasker::asDisplayList(GraphicsContext* context,
void RenderSVGResourceMasker::createDisplayList(GraphicsContext* context,
const AffineTransform& contentTransform)
{
ASSERT(context);
......@@ -135,7 +135,8 @@ PassRefPtr<DisplayList> RenderSVGResourceMasker::asDisplayList(GraphicsContext*
// Using strokeBoundingBox (instead of paintInvalidationRectInLocalCoordinates) to avoid the intersection
// with local clips/mask, which may yield incorrect results when mixing objectBoundingBox and
// userSpaceOnUse units (http://crbug.com/294900).
context->beginRecording(strokeBoundingBox());
FloatRect bounds = strokeBoundingBox();
context->beginRecording(bounds);
for (SVGElement* childElement = Traversal<SVGElement>::firstChild(*element()); childElement; childElement = Traversal<SVGElement>::nextSibling(*childElement)) {
RenderObject* renderer = childElement->renderer();
if (!renderer)
......@@ -146,8 +147,7 @@ PassRefPtr<DisplayList> RenderSVGResourceMasker::asDisplayList(GraphicsContext*
SVGRenderingContext::renderSubtree(context, renderer, contentTransform);
}
return context->endRecording();
m_maskContentDisplayList = context->endRecording();
}
void RenderSVGResourceMasker::calculateMaskContentPaintInvalidationRect()
......
......@@ -57,7 +57,7 @@ public:
private:
void calculateMaskContentPaintInvalidationRect();
void drawMaskForRenderer(GraphicsContext*, const FloatRect& targetBoundingBox);
PassRefPtr<DisplayList> asDisplayList(GraphicsContext*, const AffineTransform&);
void createDisplayList(GraphicsContext*, const AffineTransform&);
RefPtr<DisplayList> m_maskContentDisplayList;
FloatRect m_maskContentBoundaries;
......
......@@ -72,6 +72,14 @@ static FloatRect getRendererRepaintRect(RenderObject* renderer)
renderer->paintInvalidationRectInLocalCoordinates());
}
AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest)
{
AffineTransform transform;
transform.translate(dest.x() - source.x(), dest.y() - source.y());
transform.scale(dest.width() / source.width(), dest.height() / source.height());
return transform;
}
FloatRect FEImage::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
{
RenderObject* renderer = referencedRenderer();
......@@ -203,8 +211,9 @@ PassRefPtr<SkImageFilter> FEImage::createImageFilterForRenderer(RenderObject* re
if (!context)
return adoptRef(SkBitmapSource::Create(SkBitmap()));
AffineTransform contentTransformation;
FloatRect bounds(FloatPoint(), dstRect.size());
context->save();
context->beginRecording(FloatRect(FloatPoint(), dstRect.size()));
context->beginRecording(bounds);
context->concatCTM(transform);
SVGRenderingContext::renderSubtree(context, renderer, contentTransformation);
RefPtr<DisplayList> displayList = context->endRecording();
......
......@@ -567,7 +567,6 @@
'graphics/DecodingImageGenerator.h',
'graphics/DeferredImageDecoder.cpp',
'graphics/DeferredImageDecoder.h',
'graphics/DisplayList.cpp',
'graphics/DisplayList.h',
'graphics/DrawLooperBuilder.cpp',
'graphics/DrawLooperBuilder.h',
......
/*
* Copyright (C) 2013 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "platform/graphics/DisplayList.h"
#include "platform/geometry/IntSize.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "wtf/PassOwnPtr.h"
namespace blink {
DisplayList::DisplayList(const FloatRect& bounds)
: m_bounds(bounds)
{
}
DisplayList::~DisplayList()
{
}
const FloatRect& DisplayList::bounds() const
{
return m_bounds;
}
SkPicture* DisplayList::picture() const
{
return m_picture.get();
}
SkCanvas* DisplayList::beginRecording(const IntSize& size, uint32_t recordFlags)
{
m_picture.clear();
if (!m_recorder)
m_recorder = adoptPtr(new SkPictureRecorder);
return m_recorder->beginRecording(size.width(), size.height(), 0, recordFlags);
}
void DisplayList::endRecording()
{
if (m_recorder) {
m_picture = adoptRef(m_recorder->endRecording());
m_recorder.clear();
}
}
} // namespace blink
......@@ -32,15 +32,13 @@
#define DisplayList_h
#include "platform/geometry/FloatRect.h"
#include "platform/geometry/IntRect.h"
#include "platform/geometry/LayoutPoint.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "wtf/FastAllocBase.h"
#include "wtf/RefCounted.h"
#include "wtf/RefPtr.h"
class SkCanvas;
class SkPicture;
class SkPictureRecorder;
namespace blink {
class IntSize;
......@@ -49,25 +47,50 @@ class PLATFORM_EXPORT DisplayList FINAL : public WTF::RefCounted<DisplayList> {
WTF_MAKE_FAST_ALLOCATED;
WTF_MAKE_NONCOPYABLE(DisplayList);
public:
DisplayList(const FloatRect&);
~DisplayList();
static PassRefPtr<DisplayList> create(const FloatRect& bounds)
{
return adoptRef(new DisplayList(bounds));
}
virtual ~DisplayList() { }
const FloatRect& bounds() const;
const FloatRect& bounds() const { return m_bounds; }
// This entry point will return 0 when the DisplayList is in the
// midst of recording (i.e., between a beginRecording/endRecording pair)
// and if no recording has ever been completed. Otherwise it will return
// the picture created by the last endRecording call.
SkPicture* picture() const;
SkPicture* picture() const { return m_picture.get(); }
void setPicture(SkPicture* picture) { m_picture = adoptRef(picture); }
SkCanvas* beginRecording(const IntSize&, uint32_t recordFlags = 0);
bool isRecording() const { return m_recorder; }
void endRecording();
// FIXME: Need unit testing of these methods and their effect
const SkMatrix& transform() const { return m_transform; }
void setTransform(const SkMatrix& transform) { m_transform = transform; }
void setTransformFromPaintOffset(const LayoutPoint& paintOffset)
{
SkMatrix m;
m.setTranslate(paintOffset.x().toFloat(), paintOffset.y().toFloat());
setTransform(m);
}
void clearTransform() { m_transform.reset(); }
// FIXME: Need unit testing of these methods and their effect
const SkRect& clip() const { return m_clip; }
void setClip(const IntRect& rect) { m_clip = rect; }
void setClip(const FloatRect& rect) { m_clip = rect; }
void clearClip() { m_clip.setEmpty(); }
private:
DisplayList(const FloatRect& bounds) : m_bounds(bounds)
{
clearTransform();
clearClip();
}
FloatRect m_bounds;
SkMatrix m_transform;
SkRect m_clip; // TODO: Do we need to support other types of clips here?
RefPtr<SkPicture> m_picture;
OwnPtr<SkPictureRecorder> m_recorder;
};
} // namespace blink
......
......@@ -44,6 +44,7 @@
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkDevice.h"
#include "third_party/skia/include/core/SkPicture.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkRRect.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkSurface.h"
......@@ -99,15 +100,17 @@ struct GraphicsContext::CanvasSaveState {
};
struct GraphicsContext::RecordingState {
RecordingState(SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList)
: m_savedCanvas(currentCanvas)
, m_displayList(displayList)
, m_savedMatrix(currentMatrix)
{
}
RecordingState(SkPictureRecorder* recorder, SkCanvas* currentCanvas, const SkMatrix& currentMatrix, PassRefPtr<DisplayList> displayList)
: m_displayList(displayList)
, m_recorder(recorder)
, m_savedCanvas(currentCanvas)
, m_savedMatrix(currentMatrix) { }
~RecordingState() { }
SkCanvas* m_savedCanvas;
RefPtr<DisplayList> m_displayList;
SkPictureRecorder* m_recorder;
SkCanvas* m_savedCanvas;
const SkMatrix m_savedMatrix;
};
......@@ -492,16 +495,20 @@ void GraphicsContext::endLayer()
#endif
}
void GraphicsContext::beginRecording(const FloatRect& bounds)
void GraphicsContext::beginRecording(const FloatRect& bounds, uint32_t recordFlags)
{
RefPtr<DisplayList> displayList = adoptRef(new DisplayList(bounds));
RefPtr<DisplayList> displayList = DisplayList::create(bounds);
SkPictureRecorder* recorder = 0;
SkCanvas* savedCanvas = m_canvas;
SkMatrix savedMatrix = getTotalMatrix();
if (!contextDisabled()) {
IntRect recordingRect = enclosingIntRect(bounds);
m_canvas = displayList->beginRecording(recordingRect.size());
FloatRect bounds = displayList->bounds();
IntSize recordingSize = enclosingIntRect(bounds).size();
recorder = new SkPictureRecorder;
m_canvas = recorder->beginRecording(recordingSize.width(), recordingSize.height(), 0, recordFlags);
// We want the bounds offset mapped to (0, 0), such that the display list content
// is fully contained within the SkPictureRecord's bounds.
......@@ -512,7 +519,7 @@ void GraphicsContext::beginRecording(const FloatRect& bounds)
}
}
m_recordingStateStack.append(RecordingState(savedCanvas, savedMatrix, displayList));
m_recordingStateStack.append(RecordingState(recorder, savedCanvas, savedMatrix, displayList));
}
PassRefPtr<DisplayList> GraphicsContext::endRecording()
......@@ -520,15 +527,14 @@ PassRefPtr<DisplayList> GraphicsContext::endRecording()
ASSERT(!m_recordingStateStack.isEmpty());
RecordingState recording = m_recordingStateStack.last();
if (!contextDisabled()) {
ASSERT(recording.m_displayList->isRecording());
recording.m_displayList->endRecording();
}
if (!contextDisabled())
recording.m_displayList->setPicture(recording.m_recorder->endRecording());
m_recordingStateStack.removeLast();
m_canvas = recording.m_savedCanvas;
delete recording.m_recorder;
m_recordingStateStack.removeLast();
return recording.m_displayList.release();
return recording.m_displayList;
}
bool GraphicsContext::isRecording() const
......@@ -539,21 +545,33 @@ bool GraphicsContext::isRecording() const
void GraphicsContext::drawDisplayList(DisplayList* displayList)
{
ASSERT(displayList);
ASSERT(!displayList->isRecording());
if (contextDisabled() || displayList->bounds().isEmpty())
return;
bool performClip = !displayList->clip().isEmpty();
bool performTransform = !displayList->transform().isIdentity();
if (performClip || performTransform) {
save();
if (performTransform)
concat(displayList->transform());
if (performClip)
clipRect(displayList->clip());
}
realizeCanvasSave();
const FloatRect& bounds = displayList->bounds();
if (bounds.x() || bounds.y()) {
const FloatPoint& location = displayList->bounds().location();
if (location.x() || location.y()) {
SkMatrix m;
m.setTranslate(bounds.x(), bounds.y());
m.setTranslate(location.x(), location.y());
m_canvas->drawPicture(displayList->picture(), &m, 0);
} else {
m_canvas->drawPicture(displayList->picture());
}
if (performClip || performTransform)
restore();
}
void GraphicsContext::drawConvexPolygon(size_t numPoints, const FloatPoint* points, bool shouldAntialias)
......
......@@ -310,10 +310,15 @@ public:
void clip(const FloatRect& rect) { clipRect(rect); }
void clipRoundedRect(const RoundedRect&, SkRegion::Op = SkRegion::kIntersect_Op);
void clipOut(const IntRect& rect) { clipRect(rect, NotAntiAliased, SkRegion::kDifference_Op); }
void clipOut(const Path&);
void clipOutRoundedRect(const RoundedRect&);
void clipPath(const Path&, WindRule = RULE_EVENODD);
void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true);
void clipRect(const SkRect&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op);
// This clip function is used only by <canvas> code. It allows
// implementations to handle clipping on the canvas differently since
// the discipline is different.
void canvasClip(const Path&, WindRule = RULE_EVENODD);
void drawText(const Font&, const TextRunPaintInfo&, const FloatPoint&);
void drawEmphasisMarks(const Font&, const TextRunPaintInfo&, const AtomicString& mark, const FloatPoint&);
......@@ -335,8 +340,9 @@ public:
void endCull();
// Instead of being dispatched to the active canvas, draw commands following beginRecording()
// are stored in a display list that can be replayed at a later time.
void beginRecording(const FloatRect& bounds);
// are stored in a display list that can be replayed at a later time. Pass in the bounding
// rectangle for the content in the list.
void beginRecording(const FloatRect&, uint32_t = 0);
PassRefPtr<DisplayList> endRecording();
bool hasShadow() const;
......@@ -364,12 +370,6 @@ public:
typedef unsigned Edges;
void drawInnerShadow(const RoundedRect&, const Color& shadowColor, const IntSize shadowOffset, int shadowBlur, int shadowSpread, Edges clippedEdges = NoEdge);
// This clip function is used only by <canvas> code. It allows
// implementations to handle clipping on the canvas differently since
// the discipline is different.
void canvasClip(const Path&, WindRule = RULE_EVENODD);
void clipOut(const Path&);
// ---------- Transformation methods -----------------
// Note that the getCTM method returns only the current transform from Blink's perspective,
// which is not the final transform used to place content on screen. It cannot be relied upon
......@@ -448,7 +448,6 @@ private:
void drawFocusRingPath(const SkPath&, const Color&, int width);
void drawFocusRingRect(const SkRect&, const Color&, int width);
// SkCanvas wrappers.
void clipPath(const SkPath&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op);
void clipRRect(const SkRRect&, AntiAliasingMode = NotAntiAliased, SkRegion::Op = SkRegion::kIntersect_Op);
......
......@@ -1154,45 +1154,36 @@ TEST(GraphicsContextTest, RecordingTotalMatrix)
EXPECT_EQ(context.getCTM(), controlContext.getCTM());
}
TEST(GraphicsContextTest, DisplayList)
TEST(GraphicsContextTest, RecordingCanvas)
{
FloatRect rect(0, 0, 1, 1);
RefPtr<DisplayList> dl = adoptRef(new DisplayList(rect));
// picture() returns 0 initially
SkPicture* pic = dl->picture();
EXPECT_FALSE(pic);
SkBitmap bitmap;
bitmap.allocN32Pixels(1, 1);
bitmap.eraseColor(0);
SkCanvas canvas(bitmap);
GraphicsContext context(&canvas);
// endRecording without a beginRecording does nothing
dl->endRecording();
pic = dl->picture();
EXPECT_FALSE(pic);
FloatRect rect(0, 0, 1, 1);
// Two beginRecordings in a row generate two canvases.
// Unfortunately the new one could be allocated in the same
// spot as the old one so ref the first one to prolong its life.
IntSize size(1, 1);
SkCanvas* canvas1 = dl->beginRecording(size);
context.beginRecording(rect);
SkCanvas* canvas1 = context.canvas();
EXPECT_TRUE(canvas1);
canvas1->ref();
SkCanvas* canvas2 = dl->beginRecording(size);
context.beginRecording(rect);
SkCanvas* canvas2 = context.canvas();
EXPECT_TRUE(canvas2);
EXPECT_NE(canvas1, canvas2);
EXPECT_EQ(1, canvas1->getRefCnt());
canvas1->unref();
EXPECT_TRUE(dl->isRecording());
// picture() returns 0 during recording
pic = dl->picture();
EXPECT_FALSE(pic);
// endRecording finally makes the picture accessible
dl->endRecording();
pic = dl->picture();
RefPtr<DisplayList> dl = context.endRecording();
SkPicture* pic = dl->picture();
EXPECT_TRUE(pic);
EXPECT_EQ(1, pic->getRefCnt());
context.endRecording();
}
} // namespace
......@@ -223,14 +223,6 @@ AffineTransform& AffineTransform::skewY(double angle)
return shear(0, tan(deg2rad(angle)));
}
AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest)
{
AffineTransform transform;
transform.translate(dest.x() - source.x(), dest.y() - source.y());
transform.scale(dest.width() / source.width(), dest.height() / source.height());
return transform;
}
void AffineTransform::map(double x, double y, double& x2, double& y2) const
{
x2 = (m_transform[0] * x + m_transform[2] * y + m_transform[4]);
......
......@@ -177,8 +177,6 @@ private:
Transform m_transform;
};
PLATFORM_EXPORT AffineTransform makeMapBetweenRects(const FloatRect& source, const FloatRect& dest);
}
#endif
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