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: 21 void playback(SkCanvas*, AbortCallback*) const override { } 22 23 size_t approximateBytesUsed() const override { return sizeof(*this); } 24 int approximateOpCount() const override { return 0; } 25 SkRect cullRect() const override { return SkRect::MakeEmpty(); } 26 int numSlowPaths() const override { return 0; } 27 bool willPlayBackBitmaps() const override { return false; } 28 }; 29 30 template <typename T> 31 class SkMiniPicture final : public SkPicture { 32 public: 33 SkMiniPicture(SkRect cull, T* op) : fCull(cull) { 34 memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts. 35 } 36 37 void playback(SkCanvas* c, AbortCallback*) const override { 38 SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); 39 } 40 41 size_t approximateBytesUsed() const override { return sizeof(*this); } 42 int approximateOpCount() const override { return 1; } 43 SkRect cullRect() const override { return fCull; } 44 bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); } 45 int numSlowPaths() const override { 46 SkPathCounter counter; 47 counter(fOp); 48 return counter.fNumSlowPathsAndDashEffects; 49 } 50 51 private: 52 SkRect fCull; 53 T fOp; 54 }; 55 56 57 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} 58 SkMiniRecorder::~SkMiniRecorder() { 59 if (fState != State::kEmpty) { 60 // We have internal state pending. 61 // Detaching then deleting a picture is an easy way to clean up. 62 (void)this->detachAsPicture(SkRect::MakeEmpty()); 63 } 64 SkASSERT(fState == State::kEmpty); 65 } 66 67 #define TRY_TO_STORE(Type, ...) \ 68 if (fState != State::kEmpty) { return false; } \ 69 fState = State::k##Type; \ 70 new (fBuffer.get()) Type{__VA_ARGS__}; \ 71 return true 72 73 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { 74 TRY_TO_STORE(DrawRect, paint, rect); 75 } 76 77 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { 78 TRY_TO_STORE(DrawPath, paint, path); 79 } 80 81 bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) { 82 TRY_TO_STORE(DrawTextBlob, p, sk_ref_sp(b), x, y); 83 } 84 #undef TRY_TO_STORE 85 86 87 sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect& cull) { 88 #define CASE(Type) \ 89 case State::k##Type: \ 90 fState = State::kEmpty; \ 91 return sk_make_sp<SkMiniPicture<Type>>(cull, reinterpret_cast<Type*>(fBuffer.get())) 92 93 static SkOnce once; 94 static SkPicture* empty; 95 96 switch (fState) { 97 case State::kEmpty: 98 once([]{ empty = new SkEmptyPicture; }); 99 return sk_ref_sp(empty); 100 CASE(DrawPath); 101 CASE(DrawRect); 102 CASE(DrawTextBlob); 103 } 104 SkASSERT(false); 105 return nullptr; 106 #undef CASE 107 } 108 109 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) { 110 #define CASE(Type) \ 111 case State::k##Type: { \ 112 fState = State::kEmpty; \ 113 Type* op = reinterpret_cast<Type*>(fBuffer.get()); \ 114 SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \ 115 op->~Type(); \ 116 } return 117 118 switch (fState) { 119 case State::kEmpty: return; 120 CASE(DrawPath); 121 CASE(DrawRect); 122 CASE(DrawTextBlob); 123 } 124 SkASSERT(false); 125 #undef CASE 126 } 127