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: 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 bool hasText() const override { return false; } 27 int numSlowPaths() const override { return 0; } 28 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: 35 SkMiniPicture(SkRect cull, T* op) : fCull(cull) { 36 memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts. 37 } 38 39 void playback(SkCanvas* c, AbortCallback*) const override { 40 SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); 41 } 42 43 size_t approximateBytesUsed() const override { return sizeof(*this); } 44 int approximateOpCount() const override { return 1; } 45 SkRect cullRect() const override { return fCull; } 46 bool hasText() const override { return SkTextHunter()(fOp); } 47 bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); } 48 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 60 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} 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 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 90 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { 91 TRY_TO_STORE(DrawRect, paint, rect); 92 } 93 94 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { 95 TRY_TO_STORE(DrawPath, paint, path); 96 } 97 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 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 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