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