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 "include/core/SkCanvas.h"
9#include "include/core/SkPicture.h"
10#include "include/core/SkTextBlob.h"
11#include "include/private/SkOnce.h"
12#include "src/core/SkMiniRecorder.h"
13#include "src/core/SkPictureCommon.h"
14#include "src/core/SkRecordDraw.h"
15#include "src/core/SkRectPriv.h"
16#include "src/core/SkTLazy.h"
17#include <new>
18
19using namespace SkRecords;
20
21class SkEmptyPicture final : public SkPicture {
22public:
23 void playback(SkCanvas*, AbortCallback*) const override { }
24
25 size_t approximateBytesUsed() const override { return sizeof(*this); }
26 int approximateOpCount() const override { return 0; }
27 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.
32static SkRect adjust_for_paint(SkRect bounds, const SkPaint& paint) {
33 return paint.canComputeFastBounds() ? paint.computeFastBounds(bounds, &bounds)
34 : SkRectPriv::MakeLargest();
35}
36static SkRect bounds(const DrawRect& op) {
37 return adjust_for_paint(op.rect, op.paint);
38}
39static SkRect bounds(const DrawPath& op) {
40 return op.path.isInverseFillType() ? SkRectPriv::MakeLargest()
41 : adjust_for_paint(op.path.getBounds(), op.paint);
42}
43static SkRect bounds(const DrawTextBlob& op) {
44 return adjust_for_paint(op.blob->bounds().makeOffset(op.x, op.y), op.paint);
45}
46
47template <typename T>
48class SkMiniPicture final : public SkPicture {
49public:
50 SkMiniPicture(const SkRect* cull, T&& op)
51 : fCull(cull ? *cull : bounds(op))
52 , fOp(std::move(op)) {}
53
54 void playback(SkCanvas* c, AbortCallback*) const override {
55 SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp);
56 }
57
58 size_t approximateBytesUsed() const override { return sizeof(*this); }
59 int approximateOpCount() const override { return 1; }
60 SkRect cullRect() const override { return fCull; }
61
62private:
63 SkRect fCull;
64 T fOp;
65};
66
67
68SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
69SkMiniRecorder::~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
84bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
85 TRY_TO_STORE(DrawRect, paint, rect);
86}
87
88bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
89 TRY_TO_STORE(DrawPath, paint, path);
90}
91
92bool 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
98sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) {
99#define CASE(T) \
100 case State::k##T: { \
101 T* op = reinterpret_cast<T*>(fBuffer.get()); \
102 auto pic = sk_make_sp<SkMiniPicture<T>>(cull, std::move(*op)); \
103 op->~T(); \
104 fState = State::kEmpty; \
105 return std::move(pic); \
106 }
107
108 static SkOnce once;
109 static SkPicture* empty;
110
111 switch (fState) {
112 case State::kEmpty:
113 once([]{ empty = new SkEmptyPicture; });
114 return sk_ref_sp(empty);
115 CASE(DrawPath)
116 CASE(DrawRect)
117 CASE(DrawTextBlob)
118 }
119 SkASSERT(false);
120 return nullptr;
121#undef CASE
122}
123
124void SkMiniRecorder::flushAndReset(SkCanvas* canvas) {
125#define CASE(Type) \
126 case State::k##Type: { \
127 fState = State::kEmpty; \
128 Type* op = reinterpret_cast<Type*>(fBuffer.get()); \
129 SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
130 op->~Type(); \
131 } return
132
133 switch (fState) {
134 case State::kEmpty: return;
135 CASE(DrawPath);
136 CASE(DrawRect);
137 CASE(DrawTextBlob);
138 }
139 SkASSERT(false);
140#undef CASE
141}
142