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 "SkRectPriv.h" 16 #include "SkTextBlob.h" 17 18 using namespace SkRecords; 19 20 class SkEmptyPicture final : public SkPicture { 21 public: 22 void playback(SkCanvas*, AbortCallback*) const override { } 23 24 size_t approximateBytesUsed() const override { return sizeof(*this); } 25 int approximateOpCount() const override { return 0; } 26 SkRect cullRect() const override { return SkRect::MakeEmpty(); } 27 }; 28 29 // Calculate conservative bounds for each type of draw op that can be its own mini picture. 30 // These are fairly easy because we know they can't be affected by any matrix or saveLayers. 31 static SkRect adjust_for_paint(SkRect bounds, const SkPaint& paint) { 32 return paint.canComputeFastBounds() ? paint.computeFastBounds(bounds, &bounds) 33 : SkRectPriv::MakeLargest(); 34 } 35 static SkRect bounds(const DrawRect& op) { 36 return adjust_for_paint(op.rect, op.paint); 37 } 38 static SkRect bounds(const DrawPath& op) { 39 return op.path.isInverseFillType() ? SkRectPriv::MakeLargest() 40 : adjust_for_paint(op.path.getBounds(), op.paint); 41 } 42 static SkRect bounds(const DrawTextBlob& op) { 43 return adjust_for_paint(op.blob->bounds().makeOffset(op.x, op.y), op.paint); 44 } 45 46 template <typename T> 47 class SkMiniPicture final : public SkPicture { 48 public: 49 SkMiniPicture(const SkRect* cull, T* op) : fCull(cull ? *cull : bounds(*op)) { 50 memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts. 51 } 52 53 void playback(SkCanvas* c, AbortCallback*) const override { 54 SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); 55 } 56 57 size_t approximateBytesUsed() const override { return sizeof(*this); } 58 int approximateOpCount() const override { return 1; } 59 SkRect cullRect() const override { return fCull; } 60 61 private: 62 SkRect fCull; 63 T fOp; 64 }; 65 66 67 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} 68 SkMiniRecorder::~SkMiniRecorder() { 69 if (fState != State::kEmpty) { 70 // We have internal state pending. 71 // Detaching then deleting a picture is an easy way to clean up. 72 (void)this->detachAsPicture(nullptr); 73 } 74 SkASSERT(fState == State::kEmpty); 75 } 76 77 #define TRY_TO_STORE(Type, ...) \ 78 if (fState != State::kEmpty) { return false; } \ 79 fState = State::k##Type; \ 80 new (fBuffer.get()) Type{__VA_ARGS__}; \ 81 return true 82 83 bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { 84 TRY_TO_STORE(DrawRect, paint, rect); 85 } 86 87 bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { 88 TRY_TO_STORE(DrawPath, paint, path); 89 } 90 91 bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) { 92 TRY_TO_STORE(DrawTextBlob, p, sk_ref_sp(b), x, y); 93 } 94 #undef TRY_TO_STORE 95 96 97 sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) { 98 #define CASE(Type) \ 99 case State::k##Type: \ 100 fState = State::kEmpty; \ 101 return sk_make_sp<SkMiniPicture<Type>>(cull, reinterpret_cast<Type*>(fBuffer.get())) 102 103 static SkOnce once; 104 static SkPicture* empty; 105 106 switch (fState) { 107 case State::kEmpty: 108 once([]{ empty = new SkEmptyPicture; }); 109 return sk_ref_sp(empty); 110 CASE(DrawPath); 111 CASE(DrawRect); 112 CASE(DrawTextBlob); 113 } 114 SkASSERT(false); 115 return nullptr; 116 #undef CASE 117 } 118 119 void SkMiniRecorder::flushAndReset(SkCanvas* canvas) { 120 #define CASE(Type) \ 121 case State::k##Type: { \ 122 fState = State::kEmpty; \ 123 Type* op = reinterpret_cast<Type*>(fBuffer.get()); \ 124 SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \ 125 op->~Type(); \ 126 } return 127 128 switch (fState) { 129 case State::kEmpty: return; 130 CASE(DrawPath); 131 CASE(DrawRect); 132 CASE(DrawTextBlob); 133 } 134 SkASSERT(false); 135 #undef CASE 136 } 137