1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "SkCanvas.h"
9 #include "SkTLazy.h"
10 #include "SkMiniRecorder.h"
11 #include "SkOnce.h"
12 #include "SkPicture.h"
13 #include "SkPictureCommon.h"
14 #include "SkRecordDraw.h"
15 #include "SkTextBlob.h"
16 
17 using namespace SkRecords;
18 
19 class SkEmptyPicture final : public SkPicture {
20 public:
playback(SkCanvas *,AbortCallback *) const21     void playback(SkCanvas*, AbortCallback*) const override { }
22 
approximateBytesUsed() const23     size_t approximateBytesUsed() const override { return sizeof(*this); }
approximateOpCount() const24     int    approximateOpCount()   const override { return 0; }
cullRect() const25     SkRect cullRect()             const override { return SkRect::MakeEmpty(); }
numSlowPaths() const26     int    numSlowPaths()         const override { return 0; }
willPlayBackBitmaps() const27     bool   willPlayBackBitmaps()  const override { return false; }
28 };
29 
30 template <typename T>
31 class SkMiniPicture final : public SkPicture {
32 public:
SkMiniPicture(SkRect cull,T * op)33     SkMiniPicture(SkRect cull, T* op) : fCull(cull) {
34         memcpy(&fOp, op, sizeof(fOp));  // We take ownership of op's guts.
35     }
36 
playback(SkCanvas * c,AbortCallback *) const37     void playback(SkCanvas* c, AbortCallback*) const override {
38         SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp);
39     }
40 
approximateBytesUsed() const41     size_t approximateBytesUsed() const override { return sizeof(*this); }
approximateOpCount() const42     int    approximateOpCount()   const override { return 1; }
cullRect() const43     SkRect cullRect()             const override { return fCull; }
willPlayBackBitmaps() const44     bool   willPlayBackBitmaps()  const override { return SkBitmapHunter()(fOp); }
numSlowPaths() const45     int    numSlowPaths()         const override {
46         SkPathCounter counter;
47         counter(fOp);
48         return counter.fNumSlowPathsAndDashEffects;
49     }
50 
51 private:
52     SkRect fCull;
53     T      fOp;
54 };
55 
56 
SkMiniRecorder()57 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
~SkMiniRecorder()58 SkMiniRecorder::~SkMiniRecorder() {
59     if (fState != State::kEmpty) {
60         // We have internal state pending.
61         // Detaching then deleting a picture is an easy way to clean up.
62         (void)this->detachAsPicture(SkRect::MakeEmpty());
63     }
64     SkASSERT(fState == State::kEmpty);
65 }
66 
67 #define TRY_TO_STORE(Type, ...)                    \
68     if (fState != State::kEmpty) { return false; } \
69     fState = State::k##Type;                       \
70     new (fBuffer.get()) Type{__VA_ARGS__};         \
71     return true
72 
drawRect(const SkRect & rect,const SkPaint & paint)73 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
74     TRY_TO_STORE(DrawRect, paint, rect);
75 }
76 
drawPath(const SkPath & path,const SkPaint & paint)77 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
78     TRY_TO_STORE(DrawPath, paint, path);
79 }
80 
drawTextBlob(const SkTextBlob * b,SkScalar x,SkScalar y,const SkPaint & p)81 bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) {
82     TRY_TO_STORE(DrawTextBlob, p, sk_ref_sp(b), x, y);
83 }
84 #undef TRY_TO_STORE
85 
86 
detachAsPicture(const SkRect & cull)87 sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect& cull) {
88 #define CASE(Type)              \
89     case State::k##Type:        \
90         fState = State::kEmpty; \
91         return sk_make_sp<SkMiniPicture<Type>>(cull, reinterpret_cast<Type*>(fBuffer.get()))
92 
93     static SkOnce once;
94     static SkPicture* empty;
95 
96     switch (fState) {
97         case State::kEmpty:
98             once([]{ empty = new SkEmptyPicture; });
99             return sk_ref_sp(empty);
100         CASE(DrawPath);
101         CASE(DrawRect);
102         CASE(DrawTextBlob);
103     }
104     SkASSERT(false);
105     return nullptr;
106 #undef CASE
107 }
108 
flushAndReset(SkCanvas * canvas)109 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) {
110 #define CASE(Type)                                                  \
111     case State::k##Type: {                                          \
112         fState = State::kEmpty;                                     \
113         Type* op = reinterpret_cast<Type*>(fBuffer.get());          \
114         SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
115         op->~Type();                                                \
116     } return
117 
118     switch (fState) {
119         case State::kEmpty: return;
120         CASE(DrawPath);
121         CASE(DrawRect);
122         CASE(DrawTextBlob);
123     }
124     SkASSERT(false);
125 #undef CASE
126 }
127