Index: src/core/SkMiniRecorder.cpp |
diff --git a/src/core/SkMiniRecorder.cpp b/src/core/SkMiniRecorder.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f851e1cac00196b9fb588d4f4d64215b0cedc80c |
--- /dev/null |
+++ b/src/core/SkMiniRecorder.cpp |
@@ -0,0 +1,104 @@ |
+/* |
+ * Copyright 2015 Google Inc. |
+ * |
+ * Use of this source code is governed by a BSD-style license that can be |
+ * found in the LICENSE file. |
+ */ |
+ |
+#include "SkCanvas.h" |
+#include "SkMiniRecorder.h" |
+#include "SkPicture.h" |
+#include "SkPictureCommon.h" |
+#include "SkRecordDraw.h" |
+#include "SkTextBlob.h" |
+ |
+using namespace SkRecords; |
+ |
+// SkEmptyPicture could logically be a singleton, but that plays badly with Blink's |
+// Debug-only adopted() / requireAdoption() tracking in sk_ref_cnt_ext_debug.h. |
+// TODO(mtklein): modify sk_ref_cnt_ext_debug.h to play better with non-new'd objects. |
+class SkEmptyPicture final : public SkPicture { |
+public: |
+ void playback(SkCanvas*, AbortCallback*) const override { } |
+ |
+ size_t approximateBytesUsed() const override { return sizeof(*this); } |
+ int approximateOpCount() const override { return 0; } |
+ SkRect cullRect() const override { return SkRect::MakeEmpty(); } |
+ bool hasText() const override { return false; } |
+ int numSlowPaths() const override { return 0; } |
+ bool willPlayBackBitmaps() const override { return false; } |
+}; |
+ |
+template <typename T> |
+class SkMiniPicture final : public SkPicture { |
+public: |
+ SkMiniPicture(SkRect cull, T* op) : fCull(cull) { |
+ memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts. |
+ } |
+ |
+ void playback(SkCanvas* c, AbortCallback*) const override { |
+ SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); |
+ } |
+ |
+ size_t approximateBytesUsed() const override { return sizeof(*this); } |
+ int approximateOpCount() const override { return 1; } |
+ SkRect cullRect() const override { return fCull; } |
+ bool hasText() const override { return SkTextHunter()(fOp); } |
+ bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); } |
+ int numSlowPaths() const override { |
+ SkPathCounter counter; |
+ counter(fOp); |
+ return counter.fNumSlowPathsAndDashEffects; |
+ } |
+ |
+private: |
+ SkRect fCull; |
+ T fOp; |
+}; |
+ |
+ |
+SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} |
+SkMiniRecorder::~SkMiniRecorder() { |
+ if (fState != State::kEmpty) { |
+ // We have internal state pending. |
+ // Detaching then deleting a picture is an easy way to clean up. |
+ SkDELETE(this->detachAsPicture(SkRect::MakeEmpty())); |
+ } |
+ SkASSERT(fState == State::kEmpty); |
+} |
+ |
+#define TRY_TO_STORE(Type, ...) \ |
+ if (fState != State::kEmpty) { return false; } \ |
+ fState = State::k##Type; \ |
+ new (fBuffer.get()) Type(__VA_ARGS__); \ |
+ return true |
+ |
+bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { |
+ TRY_TO_STORE(DrawRect, paint, rect); |
+} |
+ |
+bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { |
+ TRY_TO_STORE(DrawPath, paint, path); |
+} |
+ |
+bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) { |
+ TRY_TO_STORE(DrawTextBlob, p, b, x, y); |
+} |
+#undef TRY_TO_STORE |
+ |
+#define CASE(Type) \ |
+ case State::k##Type: \ |
+ fState = State::kEmpty; \ |
+ return SkNEW_ARGS(SkMiniPicture<Type>, (cull, reinterpret_cast<Type*>(fBuffer.get()))) |
+ |
+SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) { |
+ switch (fState) { |
+ case State::kEmpty: return SkNEW(SkEmptyPicture); |
+ CASE(DrawPath); |
+ CASE(DrawRect); |
+ CASE(DrawTextBlob); |
+ } |
+ SkASSERT(false); |
+ return NULL; |
+} |
+#undef CASE |