• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // 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                                         : SkRect::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() ? SkRect::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; }
willPlayBackBitmaps() const61     bool   willPlayBackBitmaps()  const override { return SkBitmapHunter()(fOp); }
numSlowPaths() const62     int    numSlowPaths()         const override {
63         SkPathCounter counter;
64         counter(fOp);
65         return counter.fNumSlowPathsAndDashEffects;
66     }
67 
68 private:
69     SkRect fCull;
70     T      fOp;
71 };
72 
73 
SkMiniRecorder()74 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
~SkMiniRecorder()75 SkMiniRecorder::~SkMiniRecorder() {
76     if (fState != State::kEmpty) {
77         // We have internal state pending.
78         // Detaching then deleting a picture is an easy way to clean up.
79         (void)this->detachAsPicture(nullptr);
80     }
81     SkASSERT(fState == State::kEmpty);
82 }
83 
84 #define TRY_TO_STORE(Type, ...)                    \
85     if (fState != State::kEmpty) { return false; } \
86     fState = State::k##Type;                       \
87     new (fBuffer.get()) Type{__VA_ARGS__};         \
88     return true
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, sk_ref_sp(b), x, y);
100 }
101 #undef TRY_TO_STORE
102 
103 
detachAsPicture(const SkRect * cull)104 sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) {
105 #define CASE(Type)              \
106     case State::k##Type:        \
107         fState = State::kEmpty; \
108         return sk_make_sp<SkMiniPicture<Type>>(cull, reinterpret_cast<Type*>(fBuffer.get()))
109 
110     static SkOnce once;
111     static SkPicture* empty;
112 
113     switch (fState) {
114         case State::kEmpty:
115             once([]{ empty = new SkEmptyPicture; });
116             return sk_ref_sp(empty);
117         CASE(DrawPath);
118         CASE(DrawRect);
119         CASE(DrawTextBlob);
120     }
121     SkASSERT(false);
122     return nullptr;
123 #undef CASE
124 }
125 
flushAndReset(SkCanvas * canvas)126 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) {
127 #define CASE(Type)                                                  \
128     case State::k##Type: {                                          \
129         fState = State::kEmpty;                                     \
130         Type* op = reinterpret_cast<Type*>(fBuffer.get());          \
131         SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
132         op->~Type();                                                \
133     } return
134 
135     switch (fState) {
136         case State::kEmpty: return;
137         CASE(DrawPath);
138         CASE(DrawRect);
139         CASE(DrawTextBlob);
140     }
141     SkASSERT(false);
142 #undef CASE
143 }
144