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