Tick off some TODOs:

- support fRecord in copy constructor
  - support SkDrawPictureCallback

Moved SkDrawPictureCallback to its own header so
SkRecordDraw can include it without pulling in all of
SkPicture.

Adding an SkAutoSaveRestore to SkRecordDraw was the easiest
way to match the balance guarantees of the callback, and
probably not a bad idea in general.  Updated its tests.

BUG=skia:
R=robertphillips@google.com

Review URL: https://codereview.chromium.org/349973008
This commit is contained in:
Mike Klein 2014-06-24 11:29:06 -04:00
parent 5a940b5cbb
commit c11530ea73
7 changed files with 128 additions and 39 deletions

View File

@ -0,0 +1,29 @@
/*
* Copyright 2014 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkDrawPictureCallback_DEFINED
#define SkDrawPictureCallback_DEFINED
/**
* Subclasses of this can be passed to canvas.drawPicture(). During the drawing
* of the picture, this callback will periodically be invoked. If its
* abortDrawing() returns true, then picture playback will be interrupted.
*
* The resulting drawing is undefined, as there is no guarantee how often the
* callback will be invoked. If the abort happens inside some level of nested
* calls to save(), restore will automatically be called to return the state
* to the same level it was before the drawPicture call was made.
*/
class SK_API SkDrawPictureCallback {
public:
SkDrawPictureCallback() {}
virtual ~SkDrawPictureCallback() {}
virtual bool abortDrawing() = 0;
};
#endif//SkDrawPictureCallback_DEFINED

View File

@ -11,6 +11,7 @@
#define SkPicture_DEFINED
#include "SkBitmap.h"
#include "SkDrawPictureCallback.h"
#include "SkImageDecoder.h"
#include "SkRefCnt.h"
@ -21,7 +22,6 @@ class GrContext;
class SkBBHFactory;
class SkBBoxHierarchy;
class SkCanvas;
class SkDrawPictureCallback;
class SkData;
class SkPicturePlayback;
class SkPictureRecord;
@ -314,22 +314,4 @@ private:
SkAutoTDelete<SkRecord> fRecord;
};
/**
* Subclasses of this can be passed to canvas.drawPicture. During the drawing
* of the picture, this callback will periodically be invoked. If its
* abortDrawing() returns true, then picture playback will be interrupted.
*
* The resulting drawing is undefined, as there is no guarantee how often the
* callback will be invoked. If the abort happens inside some level of nested
* calls to save(), restore will automatically be called to return the state
* to the same level it was before the drawPicture call was made.
*/
class SK_API SkDrawPictureCallback {
public:
SkDrawPictureCallback() {}
virtual ~SkDrawPictureCallback() {}
virtual bool abortDrawing() = 0;
};
#endif

View File

@ -15,6 +15,7 @@
#include "SkBitmapDevice.h"
#include "SkCanvas.h"
#include "SkChunkAlloc.h"
#include "SkDrawPictureCallback.h"
#include "SkPaintPriv.h"
#include "SkPicture.h"
#include "SkRegion.h"
@ -34,6 +35,7 @@
#include "SkRecord.h"
#include "SkRecordDraw.h"
#include "SkRecorder.h"
template <typename T> int SafeCount(const T* obj) {
return obj ? obj->count() : 0;
@ -146,29 +148,42 @@ SkPicture::SkPicture(int width, int height,
fPlayback.reset(SkNEW_ARGS(SkPicturePlayback, (record, info, deepCopyOps)));
}
// fRecord TODO
// The simplest / safest way to copy an SkRecord is to replay it into a new one.
static SkRecord* copy(const SkRecord& src, int width, int height) {
SkRecord* dst = SkNEW(SkRecord);
SkRecorder recorder(dst, width, height);
SkRecordDraw(src, &recorder);
return dst;
}
// fRecord OK
SkPicture::SkPicture(const SkPicture& src) : INHERITED() {
this->needsNewGenID();
fWidth = src.fWidth;
fHeight = src.fHeight;
if (src.fPlayback.get()) {
if (NULL != src.fPlayback.get()) {
fPlayback.reset(SkNEW_ARGS(SkPicturePlayback, (*src.fPlayback)));
fUniqueID = src.uniqueID(); // need to call method to ensure != 0
fUniqueID = src.uniqueID(); // need to call method to ensure != 0
}
if (NULL != src.fRecord.get()) {
fRecord.reset(copy(*src.fRecord, fWidth, fHeight));
fUniqueID = src.uniqueID(); // need to call method to ensure != 0
}
}
// fRecord OK
SkPicture::~SkPicture() {}
// fRecord TODO
// fRecord TODO, fix by deleting this method
SkPicture* SkPicture::clone() const {
SkPicture* clonedPicture = SkNEW(SkPicture);
this->clone(clonedPicture, 1);
return clonedPicture;
}
// fRecord TODO
// fRecord TODO, fix by deleting this method
void SkPicture::clone(SkPicture* pictures, int count) const {
SkPictCopyInfo copyInfo;
@ -293,8 +308,7 @@ void SkPicture::draw(SkCanvas* canvas, SkDrawPictureCallback* callback) const {
fPlayback->draw(*canvas, callback);
}
if (NULL != fRecord.get()) {
// TODO: support SkDrawPictureCallback
SkRecordDraw(*fRecord, canvas);
SkRecordDraw(*fRecord, canvas, callback);
}
}
@ -495,7 +509,7 @@ bool SkPicture::willPlayBackBitmaps() const {
}
#ifdef SK_BUILD_FOR_ANDROID
// fRecord TODO
// fRecord TODO, fix by switching Android to SkDrawPictureCallback, then deleting this method
void SkPicture::abortPlayback() {
if (NULL == fPlayback.get()) {
return;

View File

@ -6,6 +6,7 @@
*/
#include <new>
#include "SkBBoxHierarchy.h"
#include "SkDrawPictureCallback.h"
#include "SkPicturePlayback.h"
#include "SkPictureRecord.h"
#include "SkPictureStateTree.h"
@ -1355,7 +1356,7 @@ bool SkPicturePlayback::suitableForGpuRasterization(GrContext* context, const ch
&& 0 == sampleCount);
bool ret = suitableForDash &&
(fContentInfo.numAAConcavePaths() - fContentInfo.numAAHairlineConcavePaths())
(fContentInfo.numAAConcavePaths() - fContentInfo.numAAHairlineConcavePaths())
< kNumAAConcavePaths;
if (!ret && NULL != reason) {
if (!suitableForDash) {
@ -1364,7 +1365,7 @@ bool SkPicturePlayback::suitableForGpuRasterization(GrContext* context, const ch
} else {
*reason = "Too many non dashed path effects.";
}
} else if ((fContentInfo.numAAConcavePaths() - fContentInfo.numAAHairlineConcavePaths())
} else if ((fContentInfo.numAAConcavePaths() - fContentInfo.numAAHairlineConcavePaths())
>= kNumAAConcavePaths)
*reason = "Too many anti-aliased concave paths.";
else

View File

@ -7,8 +7,12 @@
#include "SkRecordDraw.h"
void SkRecordDraw(const SkRecord& record, SkCanvas* canvas) {
void SkRecordDraw(const SkRecord& record, SkCanvas* canvas, SkDrawPictureCallback* callback) {
SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
for (SkRecords::Draw draw(canvas); draw.index() < record.count(); draw.next()) {
if (NULL != callback && callback->abortDrawing()) {
return;
}
record.visit<void>(draw.index(), draw);
}
}

View File

@ -10,9 +10,10 @@
#include "SkRecord.h"
#include "SkCanvas.h"
#include "SkDrawPictureCallback.h"
// Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw.
void SkRecordDraw(const SkRecord&, SkCanvas*);
void SkRecordDraw(const SkRecord&, SkCanvas*, SkDrawPictureCallback* = NULL);
namespace SkRecords {

View File

@ -9,6 +9,7 @@
#include "RecordTestUtils.h"
#include "SkDebugCanvas.h"
#include "SkDrawPictureCallback.h"
#include "SkRecord.h"
#include "SkRecordOpts.h"
#include "SkRecordDraw.h"
@ -26,8 +27,51 @@ static void draw_pos_text_h(SkCanvas* canvas, const char* text, SkScalar y) {
canvas->drawPosTextH(text, len, xpos, y, SkPaint());
}
// Rerecord into another SkRecord using full SkCanvas semantics,
// tracking clips and allowing SkRecordDraw's quickReject() calls to work.
class JustOneDraw : public SkDrawPictureCallback {
public:
JustOneDraw() : fCalls(0) {}
virtual bool abortDrawing() SK_OVERRIDE { return fCalls++ > 0; }
private:
int fCalls;
};
DEF_TEST(RecordDraw_Abort, r) {
// Record two commands.
SkRecord record;
SkRecorder recorder(&record, W, H);
recorder.drawRect(SkRect::MakeWH(200, 300), SkPaint());
recorder.clipRect(SkRect::MakeWH(100, 200));
SkRecord rerecord;
SkRecorder canvas(&rerecord, W, H);
JustOneDraw callback;
SkRecordDraw(record, &canvas, &callback);
REPORTER_ASSERT(r, 3 == rerecord.count());
assert_type<SkRecords::Save> (r, rerecord, 0);
assert_type<SkRecords::DrawRect>(r, rerecord, 1);
assert_type<SkRecords::Restore> (r, rerecord, 2);
}
DEF_TEST(RecordDraw_Unbalanced, r) {
SkRecord record;
SkRecorder recorder(&record, W, H);
recorder.save(); // We won't balance this, but SkRecordDraw will for us.
SkRecord rerecord;
SkRecorder canvas(&rerecord, W, H);
SkRecordDraw(record, &canvas);
REPORTER_ASSERT(r, 4 == rerecord.count());
assert_type<SkRecords::Save> (r, rerecord, 0);
assert_type<SkRecords::Save> (r, rerecord, 1);
assert_type<SkRecords::Restore> (r, rerecord, 2);
assert_type<SkRecords::Restore> (r, rerecord, 3);
}
// Rerecord into another SkRecord with a clip.
static void record_clipped(const SkRecord& record, SkRect clip, SkRecord* clipped) {
SkRecorder recorder(clipped, W, H);
recorder.clipRect(clip);
@ -46,8 +90,11 @@ DEF_TEST(RecordDraw_PosTextHQuickReject, r) {
SkRecord clipped;
record_clipped(record, SkRect::MakeLTRB(20, 20, 200, 200), &clipped);
// clipRect and the first drawPosTextH.
REPORTER_ASSERT(r, 2 == clipped.count());
REPORTER_ASSERT(r, 4 == clipped.count());
assert_type<SkRecords::ClipRect> (r, clipped, 0);
assert_type<SkRecords::Save> (r, clipped, 1);
assert_type<SkRecords::DrawPosTextH>(r, clipped, 2);
assert_type<SkRecords::Restore> (r, clipped, 3);
}
DEF_TEST(RecordDraw_Culling, r) {
@ -70,9 +117,15 @@ DEF_TEST(RecordDraw_Culling, r) {
SkRecord clipped;
record_clipped(record, SkRect::MakeLTRB(20, 20, 200, 200), &clipped);
// We'll keep the clipRect call from above, and the outer two drawRects, and the push/pop pair.
// If culling weren't working, we'd see 8 commands recorded here.
REPORTER_ASSERT(r, 5 == clipped.count());
// If culling weren't working, we'd see 3 more commands recorded here.
REPORTER_ASSERT(r, 7 == clipped.count());
assert_type<SkRecords::ClipRect>(r, clipped, 0);
assert_type<SkRecords::Save> (r, clipped, 1);
assert_type<SkRecords::PushCull>(r, clipped, 2);
assert_type<SkRecords::DrawRect>(r, clipped, 3);
assert_type<SkRecords::DrawRect>(r, clipped, 4);
assert_type<SkRecords::PopCull> (r, clipped, 5);
assert_type<SkRecords::Restore> (r, clipped, 6);
}
DEF_TEST(RecordDraw_SetMatrixClobber, r) {
@ -91,6 +144,11 @@ DEF_TEST(RecordDraw_SetMatrixClobber, r) {
translateCanvas.setMatrix(translate);
SkRecordDraw(scaleRecord, &translateCanvas);
REPORTER_ASSERT(r, 4 == translateRecord.count());
assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
assert_type<SkRecords::Save> (r, translateRecord, 1);
assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
assert_type<SkRecords::Restore> (r, translateRecord, 3);
// When we look at translateRecord now, it should have its first +20,+20 translate,
// then a 2x,3x scale that's been concatted with that +20,+20 translate.
@ -98,7 +156,7 @@ DEF_TEST(RecordDraw_SetMatrixClobber, r) {
setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 0);
REPORTER_ASSERT(r, setMatrix->matrix == translate);
setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 1);
setMatrix = assert_type<SkRecords::SetMatrix>(r, translateRecord, 2);
SkMatrix expected = scale;
expected.postConcat(translate);
REPORTER_ASSERT(r, setMatrix->matrix == expected);