aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/core/SkPictureRecorder.h15
-rw-r--r--src/core/SkPictureRecorder.cpp25
-rw-r--r--src/core/SkRecordOpts.cpp2
-rw-r--r--tests/PictureTest.cpp60
-rw-r--r--tests/RecordReplaceDrawTest.cpp5
5 files changed, 96 insertions, 11 deletions
diff --git a/include/core/SkPictureRecorder.h b/include/core/SkPictureRecorder.h
index 3f0cbcc801..419c01bc88 100644
--- a/include/core/SkPictureRecorder.h
+++ b/include/core/SkPictureRecorder.h
@@ -33,11 +33,15 @@ public:
enum RecordFlags {
// This flag indicates that, if some BHH is being computed, saveLayer
// information should also be extracted at the same time.
- kComputeSaveLayerInfo_RecordFlag = 0x01,
+ kComputeSaveLayerInfo_RecordFlag = 1 << 0,
// If you call drawPicture() or drawDrawable() on the recording canvas, this flag forces
// that object to playback its contents immediately rather than reffing the object.
- kPlaybackDrawPicture_RecordFlag = 0x02,
+ kPlaybackDrawPicture_RecordFlag = 1 << 1,
+ };
+
+ enum FinishFlags {
+ kReturnNullForEmpty_FinishFlag = 1 << 0, // no draw-ops will return nullptr
};
/** Returns the canvas that records the drawing commands.
@@ -72,7 +76,7 @@ public:
* reflect their current state, but will not contain a live reference to the drawables
* themselves.
*/
- sk_sp<SkPicture> finishRecordingAsPicture();
+ sk_sp<SkPicture> finishRecordingAsPicture(uint32_t endFlags = 0);
/**
* Signal that the caller is done recording, and update the cull rect to use for bounding
@@ -83,7 +87,8 @@ public:
* and subsequent culling operations.
* @return the picture containing the recorded content.
*/
- sk_sp<SkPicture> finishRecordingAsPictureWithCull(const SkRect& cullRect);
+ sk_sp<SkPicture> finishRecordingAsPictureWithCull(const SkRect& cullRect,
+ uint32_t endFlags = 0);
/**
* Signal that the caller is done recording. This invalidates the canvas returned by
@@ -95,7 +100,7 @@ public:
* and therefore this drawable will reflect the current state of those nested drawables anytime
* it is drawn or a new picture is snapped from it (by calling drawable->newPictureSnapshot()).
*/
- sk_sp<SkDrawable> finishRecordingAsDrawable();
+ sk_sp<SkDrawable> finishRecordingAsDrawable(uint32_t endFlags = 0);
#ifdef SK_SUPPORT_LEGACY_PICTURE_PTR
SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture() {
diff --git a/src/core/SkPictureRecorder.cpp b/src/core/SkPictureRecorder.cpp
index a157d0dfe0..8ce770efce 100644
--- a/src/core/SkPictureRecorder.cpp
+++ b/src/core/SkPictureRecorder.cpp
@@ -51,17 +51,26 @@ SkCanvas* SkPictureRecorder::getRecordingCanvas() {
return fActivelyRecording ? fRecorder.get() : nullptr;
}
-sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture() {
+sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture(uint32_t finishFlags) {
fActivelyRecording = false;
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
if (fRecord->count() == 0) {
+ if (finishFlags & kReturnNullForEmpty_FinishFlag) {
+ return nullptr;
+ }
return fMiniRecorder.detachAsPicture(fCullRect);
}
// TODO: delay as much of this work until just before first playback?
SkRecordOptimize(fRecord);
+ if (fRecord->count() == 0) {
+ if (finishFlags & kReturnNullForEmpty_FinishFlag) {
+ return nullptr;
+ }
+ }
+
SkAutoTUnref<SkLayerInfo> saveLayerData;
if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) {
@@ -97,9 +106,10 @@ sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture() {
saveLayerData.release(), subPictureBytes);
}
-sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPictureWithCull(const SkRect& cullRect) {
+sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPictureWithCull(const SkRect& cullRect,
+ uint32_t finishFlags) {
fCullRect = cullRect;
- return this->finishRecordingAsPicture();
+ return this->finishRecordingAsPicture(finishFlags);
}
@@ -118,14 +128,19 @@ void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
SkRecordDraw(*fRecord, canvas, nullptr, drawables, drawableCount, nullptr/*bbh*/, nullptr/*callback*/);
}
-sk_sp<SkDrawable> SkPictureRecorder::finishRecordingAsDrawable() {
+sk_sp<SkDrawable> SkPictureRecorder::finishRecordingAsDrawable(uint32_t finishFlags) {
fActivelyRecording = false;
fRecorder->flushMiniRecorder();
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
- // TODO: delay as much of this work until just before first playback?
SkRecordOptimize(fRecord);
+ if (fRecord->count() == 0) {
+ if (finishFlags & kReturnNullForEmpty_FinishFlag) {
+ return nullptr;
+ }
+ }
+
if (fBBH.get()) {
SkAutoTMalloc<SkRect> bounds(fRecord->count());
SkRecordFillBounds(fCullRect, *fRecord, bounds);
diff --git a/src/core/SkRecordOpts.cpp b/src/core/SkRecordOpts.cpp
index ea19d6eaa5..aa07facb19 100644
--- a/src/core/SkRecordOpts.cpp
+++ b/src/core/SkRecordOpts.cpp
@@ -276,7 +276,7 @@ void SkRecordOptimize(SkRecord* record) {
// out junk for other optimization passes. Right now, nothing needs it,
// and the bounding box hierarchy will do the work of skipping no-op
// Save-NoDraw-Restore sequences better than we can here.
- //SkRecordNoopSaveRestores(record);
+ SkRecordNoopSaveRestores(record);
SkRecordNoopSaveLayerDrawRestores(record);
SkRecordMergeSvgOpacityAndFilterLayers(record);
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
index f109482311..eb0c6a8f0e 100644
--- a/tests/PictureTest.cpp
+++ b/tests/PictureTest.cpp
@@ -1447,3 +1447,63 @@ DEF_TEST(PictureGpuAnalyzer, r) {
}
#endif // SK_SUPPORT_GPU
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static void empty_ops(SkCanvas* canvas) {
+}
+static void clip_ops(SkCanvas* canvas) {
+ canvas->save();
+ canvas->clipRect(SkRect::MakeWH(20, 20));
+ canvas->restore();
+}
+static void matrix_ops(SkCanvas* canvas) {
+ canvas->save();
+ canvas->scale(2, 3);
+ canvas->restore();
+}
+static void matrixclip_ops(SkCanvas* canvas) {
+ canvas->save();
+ canvas->scale(2, 3);
+ canvas->clipRect(SkRect::MakeWH(20, 20));
+ canvas->restore();
+}
+typedef void (*CanvasProc)(SkCanvas*);
+
+// Test the kReturnNullForEmpty_FinishFlag option when recording
+//
+DEF_TEST(Picture_RecordEmpty, r) {
+ const SkRect cull = SkRect::MakeWH(100, 100);
+
+ CanvasProc procs[] { empty_ops, clip_ops, matrix_ops, matrixclip_ops };
+
+ for (auto proc : procs) {
+ {
+ SkPictureRecorder rec;
+ proc(rec.beginRecording(cull));
+ sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(0);
+ REPORTER_ASSERT(r, pic.get());
+ REPORTER_ASSERT(r, pic->approximateOpCount() == 0);
+ }
+ {
+ SkPictureRecorder rec;
+ proc(rec.beginRecording(cull));
+ sk_sp<SkPicture> pic = rec.finishRecordingAsPicture(
+ SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
+ REPORTER_ASSERT(r, !pic.get());
+ }
+ {
+ SkPictureRecorder rec;
+ proc(rec.beginRecording(cull));
+ sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(0);
+ REPORTER_ASSERT(r, dr.get());
+ }
+ {
+ SkPictureRecorder rec;
+ proc(rec.beginRecording(cull));
+ sk_sp<SkDrawable> dr = rec.finishRecordingAsDrawable(
+ SkPictureRecorder::kReturnNullForEmpty_FinishFlag);
+ REPORTER_ASSERT(r, !dr.get());
+ }
+ }
+}
diff --git a/tests/RecordReplaceDrawTest.cpp b/tests/RecordReplaceDrawTest.cpp
index 0dd804ec9d..388bee8d7b 100644
--- a/tests/RecordReplaceDrawTest.cpp
+++ b/tests/RecordReplaceDrawTest.cpp
@@ -78,6 +78,11 @@ DEF_TEST(RecordReplaceDraw_Unbalanced, r) {
canvas->save();
canvas->scale(2, 2);
pic = recorder.finishRecordingAsPicture();
+
+ // we may have optimized everything away. If so, just return
+ if (pic->approximateOpCount() == 0) {
+ return;
+ }
}
SkRecord rerecord;