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 "SkRectPriv.h"
16 #include "SkTextBlob.h"
17 
18 using namespace SkRecords;
19 
20 class SkEmptyPicture final : public SkPicture {
21 public:
22     void playback(SkCanvas*, AbortCallback*) const override { }
23 
24     size_t approximateBytesUsed() const override { return sizeof(*this); }
25     int    approximateOpCount()   const override { return 0; }
26     SkRect cullRect()             const override { return SkRect::MakeEmpty(); }
27 };
28 
29 // Calculate conservative bounds for each type of draw op that can be its own mini picture.
30 // These are fairly easy because we know they can't be affected by any matrix or saveLayers.
31 static SkRect adjust_for_paint(SkRect bounds, const SkPaint& paint) {
32     return paint.canComputeFastBounds() ? paint.computeFastBounds(bounds, &bounds)
33                                         : SkRectPriv::MakeLargest();
34 }
35 static SkRect bounds(const DrawRect& op) {
36     return adjust_for_paint(op.rect, op.paint);
37 }
38 static SkRect bounds(const DrawPath& op) {
39     return op.path.isInverseFillType() ? SkRectPriv::MakeLargest()
40                                        : adjust_for_paint(op.path.getBounds(), op.paint);
41 }
42 static SkRect bounds(const DrawTextBlob& op) {
43     return adjust_for_paint(op.blob->bounds().makeOffset(op.x, op.y), op.paint);
44 }
45 
46 template <typename T>
47 class SkMiniPicture final : public SkPicture {
48 public:
49     SkMiniPicture(const SkRect* cull, T* op) : fCull(cull ? *cull : bounds(*op)) {
50         memcpy(&fOp, op, sizeof(fOp));  // We take ownership of op's guts.
51     }
52 
53     void playback(SkCanvas* c, AbortCallback*) const override {
54         SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp);
55     }
56 
57     size_t approximateBytesUsed() const override { return sizeof(*this); }
58     int    approximateOpCount()   const override { return 1; }
59     SkRect cullRect()             const override { return fCull; }
60 
61 private:
62     SkRect fCull;
63     T      fOp;
64 };
65 
66 
67 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
68 SkMiniRecorder::~SkMiniRecorder() {
69     if (fState != State::kEmpty) {
70         // We have internal state pending.
71         // Detaching then deleting a picture is an easy way to clean up.
72         (void)this->detachAsPicture(nullptr);
73     }
74     SkASSERT(fState == State::kEmpty);
75 }
76 
77 #define TRY_TO_STORE(Type, ...)                    \
78     if (fState != State::kEmpty) { return false; } \
79     fState = State::k##Type;                       \
80     new (fBuffer.get()) Type{__VA_ARGS__};         \
81     return true
82 
83 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
84     TRY_TO_STORE(DrawRect, paint, rect);
85 }
86 
87 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
88     TRY_TO_STORE(DrawPath, paint, path);
89 }
90 
91 bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) {
92     TRY_TO_STORE(DrawTextBlob, p, sk_ref_sp(b), x, y);
93 }
94 #undef TRY_TO_STORE
95 
96 
97 sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) {
98 #define CASE(Type)              \
99     case State::k##Type:        \
100         fState = State::kEmpty; \
101         return sk_make_sp<SkMiniPicture<Type>>(cull, reinterpret_cast<Type*>(fBuffer.get()))
102 
103     static SkOnce once;
104     static SkPicture* empty;
105 
106     switch (fState) {
107         case State::kEmpty:
108             once([]{ empty = new SkEmptyPicture; });
109             return sk_ref_sp(empty);
110         CASE(DrawPath);
111         CASE(DrawRect);
112         CASE(DrawTextBlob);
113     }
114     SkASSERT(false);
115     return nullptr;
116 #undef CASE
117 }
118 
119 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) {
120 #define CASE(Type)                                                  \
121     case State::k##Type: {                                          \
122         fState = State::kEmpty;                                     \
123         Type* op = reinterpret_cast<Type*>(fBuffer.get());          \
124         SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
125         op->~Type();                                                \
126     } return
127 
128     switch (fState) {
129         case State::kEmpty: return;
130         CASE(DrawPath);
131         CASE(DrawRect);
132         CASE(DrawTextBlob);
133     }
134     SkASSERT(false);
135 #undef CASE
136 }
137