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 // 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 : SkRect::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() ? SkRect::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 bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); } 62 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 74 SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} 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 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, sk_ref_sp(b), x, y); 100 } 101 #undef TRY_TO_STORE 102 103 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 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