add replay entry point to SkPictureRecorder for Android

This CL adds an Android-only entry point to address the Java Picture(Picture) and serialize use cases. Note that (in its current form) it doesn't preserve the old API's handling of unbalanced saves/saveLayers (this CL always balances them).

R=reed@google.com, scroggo@google.com, djsollen@google.com, mtklein@google.com

Author: robertphillips@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@14911 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
commit-bot@chromium.org 2014-05-27 23:41:45 +00:00
parent 293a4b367a
commit 6d3eaeabdd
3 changed files with 158 additions and 4 deletions

View File

@ -99,6 +99,15 @@ private:
SkAutoTUnref<SkPictureFactory> fFactory; SkAutoTUnref<SkPictureFactory> fFactory;
#endif #endif
#ifdef SK_BUILD_FOR_ANDROID
/** Replay the current (partially recorded) operation stream into
canvas. This call doesn't close the current recording.
*/
friend class AndroidPicture;
friend class SkPictureRecorderReplayTester; // for unit testing
void partialReplay(SkCanvas* canvas);
#endif
SkAutoTUnref<SkPicture> fPicture; SkAutoTUnref<SkPicture> fPicture;
typedef SkNoncopyable INHERITED; typedef SkNoncopyable INHERITED;

View File

@ -5,6 +5,9 @@
* found in the LICENSE file. * found in the LICENSE file.
*/ */
#ifdef SK_BUILD_FOR_ANDROID
#include "SkPicturePlayback.h"
#endif
#include "SkPictureRecorder.h" #include "SkPictureRecorder.h"
SkCanvas* SkPictureRecorder::beginRecording(int width, int height, SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
@ -13,3 +16,19 @@ SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
fPicture.reset(SkNEW(SkPicture)); fPicture.reset(SkNEW(SkPicture));
return fPicture->beginRecording(width, height, bbhFactory, recordFlags); return fPicture->beginRecording(width, height, bbhFactory, recordFlags);
} }
#ifdef SK_BUILD_FOR_ANDROID
void SkPictureRecorder::partialReplay(SkCanvas* canvas) {
if (NULL == fPicture.get() || NULL == canvas) {
// Not recording or nothing to replay into
return;
}
SkASSERT(NULL != fPicture->fRecord);
SkAutoTDelete<SkPicturePlayback> playback(SkPicture::FakeEndRecording(fPicture,
*fPicture->fRecord,
false));
playback->draw(*canvas, NULL);
}
#endif

View File

@ -916,6 +916,131 @@ static void set_canvas_to_save_count_4(SkCanvas* canvas) {
canvas->save(); canvas->save();
} }
#ifdef SK_BUILD_FOR_ANDROID
/**
* A canvas that records the number of saves, saveLayers and restores.
*/
class SaveCountingCanvas : public SkCanvas {
public:
SaveCountingCanvas(int width, int height)
: INHERITED(width, height)
, fSaveCount(0)
, fSaveLayerCount(0)
, fRestoreCount(0){
}
virtual SaveLayerStrategy willSaveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags) SK_OVERRIDE {
++fSaveLayerCount;
return this->INHERITED::willSaveLayer(bounds, paint, flags);
}
virtual void willSave(SaveFlags flags) SK_OVERRIDE {
++fSaveCount;
this->INHERITED::willSave(flags);
}
virtual void willRestore() SK_OVERRIDE {
++fRestoreCount;
this->INHERITED::willRestore();
}
unsigned int getSaveCount() const { return fSaveCount; }
unsigned int getSaveLayerCount() const { return fSaveLayerCount; }
unsigned int getRestoreCount() const { return fRestoreCount; }
private:
unsigned int fSaveCount;
unsigned int fSaveLayerCount;
unsigned int fRestoreCount;
typedef SkCanvas INHERITED;
};
void check_save_state(skiatest::Reporter* reporter, SkPicture* picture,
unsigned int numSaves, unsigned int numSaveLayers,
unsigned int numRestores) {
SaveCountingCanvas canvas(picture->width(), picture->height());
picture->draw(&canvas);
REPORTER_ASSERT(reporter, numSaves == canvas.getSaveCount());
REPORTER_ASSERT(reporter, numSaveLayers == canvas.getSaveLayerCount());
REPORTER_ASSERT(reporter, numRestores == canvas.getRestoreCount());
}
// This class exists so SkPicture can friend it and give it access to
// the 'partialReplay' method.
class SkPictureRecorderReplayTester {
public:
static SkPicture* Copy(SkPictureRecorder* recorder) {
SkPictureRecorder recorder2;
SkCanvas* canvas = recorder2.beginRecording(10, 10, NULL, 0);
recorder->partialReplay(canvas);
return recorder2.endRecording();
}
};
// Test out SkPictureRecorder::partialReplay
DEF_TEST(PictureRecorder_replay, reporter) {
// check save/saveLayer state
{
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0);
canvas->saveLayer(NULL, NULL);
SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
// The extra save and restore comes from the Copy process.
check_save_state(reporter, copy, 2, 1, 3);
canvas->saveLayer(NULL, NULL);
SkAutoTUnref<SkPicture> final(recorder.endRecording());
check_save_state(reporter, final, 1, 2, 3);
// The copy shouldn't pick up any operations added after it was made
check_save_state(reporter, copy, 2, 1, 3);
}
// (partially) check leakage of draw ops
{
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(10, 10, NULL, 0);
SkRect r = SkRect::MakeWH(5, 5);
SkPaint p;
canvas->drawRect(r, p);
SkAutoTUnref<SkPicture> copy(SkPictureRecorderReplayTester::Copy(&recorder));
REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
SkBitmap bm;
make_bm(&bm, 10, 10, SK_ColorRED, true);
r.offset(5.0f, 5.0f);
canvas->drawBitmapRectToRect(bm, NULL, r);
SkAutoTUnref<SkPicture> final(recorder.endRecording());
REPORTER_ASSERT(reporter, final->willPlayBackBitmaps());
REPORTER_ASSERT(reporter, copy->uniqueID() != final->uniqueID());
// The snapshot shouldn't pick up any operations added after it was made
REPORTER_ASSERT(reporter, !copy->willPlayBackBitmaps());
}
}
#endif
static void test_unbalanced_save_restores(skiatest::Reporter* reporter) { static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
SkCanvas testCanvas(100, 100); SkCanvas testCanvas(100, 100);
set_canvas_to_save_count_4(&testCanvas); set_canvas_to_save_count_4(&testCanvas);
@ -994,20 +1119,21 @@ static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
SkPaint paint; SkPaint paint;
canvas->drawRect(r, paint); canvas->drawRect(r, paint);
// Copying a mid-recording picture could result in unbalanced saves/restores // Check that copying a mid-recording picture does not result in unbalanced saves/restores
SkPicture p2(p); SkPicture p2(p);
testCanvas.drawPicture(p2); testCanvas.drawPicture(p2);
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
set_canvas_to_save_count_4(&testCanvas); set_canvas_to_save_count_4(&testCanvas);
// Cloning a mid-recording picture could result in unbalanced saves/restores // Check that cloning a mid-recording picture does not result in unbalanced saves/restores
SkAutoTUnref<SkPicture> p3(p.clone()); SkAutoTUnref<SkPicture> p3(p.clone());
testCanvas.drawPicture(*p3); testCanvas.drawPicture(*p3);
REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount()); REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
set_canvas_to_save_count_4(&testCanvas); set_canvas_to_save_count_4(&testCanvas);
// Serializing a mid-recording picture could result in unbalanced saves/restores // Check that serializing a mid-recording picture doesn't result in unbalanced
// saves/restores
SkDynamicMemoryWStream wStream; SkDynamicMemoryWStream wStream;
p.serialize(&wStream); p.serialize(&wStream);
SkAutoDataUnref data(wStream.copyToData()); SkAutoDataUnref data(wStream.copyToData());
@ -1299,7 +1425,7 @@ static void test_clip_bound_opt(skiatest::Reporter* reporter) {
*/ */
class ClipCountingCanvas : public SkCanvas { class ClipCountingCanvas : public SkCanvas {
public: public:
explicit ClipCountingCanvas(int width, int height) ClipCountingCanvas(int width, int height)
: INHERITED(width, height) : INHERITED(width, height)
, fClipCount(0){ , fClipCount(0){
} }