add replay entry point to SkPictureRecorder for Android
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 27 May 2014 23:41:45 +0000 (23:41 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Tue, 27 May 2014 23:41:45 +0000 (23:41 +0000)
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

include/core/SkPictureRecorder.h
src/core/SkPictureRecorder.cpp
tests/PictureTest.cpp

index 95ba7b0..8284c7d 100644 (file)
@@ -99,6 +99,15 @@ private:
     SkAutoTUnref<SkPictureFactory>  fFactory;
 #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;
 
     typedef SkNoncopyable INHERITED;
index a22cf5d..844b0e7 100644 (file)
@@ -5,6 +5,9 @@
  * found in the LICENSE file.
  */
 
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkPicturePlayback.h"
+#endif
 #include "SkPictureRecorder.h"
 
 SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
@@ -13,3 +16,19 @@ SkCanvas* SkPictureRecorder::beginRecording(int width, int height,
     fPicture.reset(SkNEW(SkPicture));
     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
index ac44a5c..ad23fd3 100644 (file)
@@ -916,6 +916,131 @@ static void set_canvas_to_save_count_4(SkCanvas* canvas) {
     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) {
     SkCanvas testCanvas(100, 100);
     set_canvas_to_save_count_4(&testCanvas);
@@ -994,20 +1119,21 @@ static void test_unbalanced_save_restores(skiatest::Reporter* reporter) {
         SkPaint 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);
 
         testCanvas.drawPicture(p2);
         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
         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());
         testCanvas.drawPicture(*p3);
         REPORTER_ASSERT(reporter, 4 == testCanvas.getSaveCount());
         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;
         p.serialize(&wStream);
         SkAutoDataUnref data(wStream.copyToData());
@@ -1299,7 +1425,7 @@ static void test_clip_bound_opt(skiatest::Reporter* reporter) {
  */
 class ClipCountingCanvas : public SkCanvas {
 public:
-    explicit ClipCountingCanvas(int width, int height)
+    ClipCountingCanvas(int width, int height)
         : INHERITED(width, height)
         , fClipCount(0){
     }