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 "SkOncePtr.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(); }
hasText() const26     bool   hasText()              const override { return false; }
numSlowPaths() const27     int    numSlowPaths()         const override { return 0; }
willPlayBackBitmaps() const28     bool   willPlayBackBitmaps()  const override { return false; }
29 };
30 SK_DECLARE_STATIC_ONCE_PTR(SkEmptyPicture, gEmptyPicture);
31 
32 template <typename T>
33 class SkMiniPicture final : public SkPicture {
34 public:
SkMiniPicture(SkRect cull,T * op)35     SkMiniPicture(SkRect cull, T* op) : fCull(cull) {
36         memcpy(&fOp, op, sizeof(fOp));  // We take ownership of op's guts.
37     }
38 
playback(SkCanvas * c,AbortCallback *) const39     void playback(SkCanvas* c, AbortCallback*) const override {
40         SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp);
41     }
42 
approximateBytesUsed() const43     size_t approximateBytesUsed() const override { return sizeof(*this); }
approximateOpCount() const44     int    approximateOpCount()   const override { return 1; }
cullRect() const45     SkRect cullRect()             const override { return fCull; }
hasText() const46     bool   hasText()              const override { return SkTextHunter()(fOp); }
willPlayBackBitmaps() const47     bool   willPlayBackBitmaps()  const override { return SkBitmapHunter()(fOp); }
numSlowPaths() const48     int    numSlowPaths()         const override {
49         SkPathCounter counter;
50         counter(fOp);
51         return counter.fNumSlowPathsAndDashEffects;
52     }
53 
54 private:
55     SkRect fCull;
56     T      fOp;
57 };
58 
59 
SkMiniRecorder()60 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
~SkMiniRecorder()61 SkMiniRecorder::~SkMiniRecorder() {
62     if (fState != State::kEmpty) {
63         // We have internal state pending.
64         // Detaching then deleting a picture is an easy way to clean up.
65         delete this->detachAsPicture(SkRect::MakeEmpty());
66     }
67     SkASSERT(fState == State::kEmpty);
68 }
69 
70 #define TRY_TO_STORE(Type, ...)                    \
71     if (fState != State::kEmpty) { return false; } \
72     fState = State::k##Type;                       \
73     new (fBuffer.get()) Type{__VA_ARGS__};         \
74     return true
75 
drawBitmapRect(const SkBitmap & bm,const SkRect * src,const SkRect & dst,const SkPaint * p,SkCanvas::SrcRectConstraint constraint)76 bool SkMiniRecorder::drawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
77                                     const SkPaint* p, SkCanvas::SrcRectConstraint constraint) {
78     SkRect bounds;
79     if (!src) {
80         bm.getBounds(&bounds);
81         src = &bounds;
82     }
83     SkTLazy<SkPaint> defaultPaint;
84     if (!p) {
85         p = defaultPaint.init();
86     }
87     TRY_TO_STORE(DrawBitmapRectFixedSize, *p, bm, *src, dst, constraint);
88 }
89 
drawRect(const SkRect & rect,const SkPaint & paint)90 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
91     TRY_TO_STORE(DrawRect, paint, rect);
92 }
93 
drawPath(const SkPath & path,const SkPaint & paint)94 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
95     TRY_TO_STORE(DrawPath, paint, path);
96 }
97 
drawTextBlob(const SkTextBlob * b,SkScalar x,SkScalar y,const SkPaint & p)98 bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) {
99     TRY_TO_STORE(DrawTextBlob, p, b, x, y);
100 }
101 #undef TRY_TO_STORE
102 
103 
detachAsPicture(const SkRect & cull)104 SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) {
105 #define CASE(Type)              \
106     case State::k##Type:        \
107         fState = State::kEmpty; \
108         return new SkMiniPicture<Type>(cull, reinterpret_cast<Type*>(fBuffer.get()))
109 
110     switch (fState) {
111         case State::kEmpty: return SkRef(gEmptyPicture.get([]{ return new SkEmptyPicture; }));
112         CASE(DrawBitmapRectFixedSize);
113         CASE(DrawPath);
114         CASE(DrawRect);
115         CASE(DrawTextBlob);
116     }
117     SkASSERT(false);
118     return nullptr;
119 #undef CASE
120 }
121 
flushAndReset(SkCanvas * canvas)122 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) {
123 #define CASE(Type)                                                  \
124     case State::k##Type: {                                          \
125         fState = State::kEmpty;                                     \
126         Type* op = reinterpret_cast<Type*>(fBuffer.get());          \
127         SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
128         op->~Type();                                                \
129     } return
130 
131     switch (fState) {
132         case State::kEmpty: return;
133         CASE(DrawBitmapRectFixedSize);
134         CASE(DrawPath);
135         CASE(DrawRect);
136         CASE(DrawTextBlob);
137     }
138     SkASSERT(false);
139 #undef CASE
140 }
141