| /* | |
| * Copyright 2015 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkPicture.h" | |
| #include "include/core/SkTextBlob.h" | |
| #include "include/private/SkOnce.h" | |
| #include "src/core/SkMiniRecorder.h" | |
| #include "src/core/SkPictureCommon.h" | |
| #include "src/core/SkRecordDraw.h" | |
| #include "src/core/SkRectPriv.h" | |
| #include "src/core/SkTLazy.h" | |
| #include <new> | |
| using namespace SkRecords; | |
| class SkEmptyPicture final : public SkPicture { | |
| public: | |
| void playback(SkCanvas*, AbortCallback*) const override { } | |
| size_t approximateBytesUsed() const override { return sizeof(*this); } | |
| int approximateOpCount(bool) const override { return 0; } | |
| SkRect cullRect() const override { return SkRect::MakeEmpty(); } | |
| }; | |
| // Calculate conservative bounds for each type of draw op that can be its own mini picture. | |
| // These are fairly easy because we know they can't be affected by any matrix or saveLayers. | |
| static SkRect adjust_for_paint(SkRect bounds, const SkPaint& paint) { | |
| return paint.canComputeFastBounds() ? paint.computeFastBounds(bounds, &bounds) | |
| : SkRectPriv::MakeLargest(); | |
| } | |
| static SkRect bounds(const DrawRect& op) { | |
| return adjust_for_paint(op.rect, op.paint); | |
| } | |
| static SkRect bounds(const DrawPath& op) { | |
| return op.path.isInverseFillType() ? SkRectPriv::MakeLargest() | |
| : adjust_for_paint(op.path.getBounds(), op.paint); | |
| } | |
| static SkRect bounds(const DrawTextBlob& op) { | |
| return adjust_for_paint(op.blob->bounds().makeOffset(op.x, op.y), op.paint); | |
| } | |
| template <typename T> | |
| class SkMiniPicture final : public SkPicture { | |
| public: | |
| SkMiniPicture(const SkRect* cull, T&& op) | |
| : fCull(cull ? *cull : bounds(op)) | |
| , fOp(std::move(op)) {} | |
| void playback(SkCanvas* c, AbortCallback*) const override { | |
| SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp); | |
| } | |
| size_t approximateBytesUsed() const override { return sizeof(*this); } | |
| int approximateOpCount(bool) const override { return 1; } | |
| SkRect cullRect() const override { return fCull; } | |
| private: | |
| SkRect fCull; | |
| T fOp; | |
| }; | |
| SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {} | |
| SkMiniRecorder::~SkMiniRecorder() { | |
| if (fState != State::kEmpty) { | |
| // We have internal state pending. | |
| // Detaching then deleting a picture is an easy way to clean up. | |
| (void)this->detachAsPicture(nullptr); | |
| } | |
| SkASSERT(fState == State::kEmpty); | |
| } | |
| #define TRY_TO_STORE(Type, ...) \ | |
| if (fState != State::kEmpty) { return false; } \ | |
| fState = State::k##Type; \ | |
| new (fBuffer) Type{__VA_ARGS__}; \ | |
| return true | |
| bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) { | |
| TRY_TO_STORE(DrawRect, paint, rect); | |
| } | |
| bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) { | |
| TRY_TO_STORE(DrawPath, paint, path); | |
| } | |
| bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) { | |
| TRY_TO_STORE(DrawTextBlob, p, sk_ref_sp(b), x, y); | |
| } | |
| #undef TRY_TO_STORE | |
| sk_sp<SkPicture> SkMiniRecorder::detachAsPicture(const SkRect* cull) { | |
| #define CASE(T) \ | |
| case State::k##T: { \ | |
| T* op = reinterpret_cast<T*>(fBuffer); \ | |
| auto pic = sk_make_sp<SkMiniPicture<T>>(cull, std::move(*op)); \ | |
| op->~T(); \ | |
| fState = State::kEmpty; \ | |
| return std::move(pic); \ | |
| } | |
| static SkOnce once; | |
| static SkPicture* empty; | |
| switch (fState) { | |
| case State::kEmpty: | |
| once([]{ empty = new SkEmptyPicture; }); | |
| return sk_ref_sp(empty); | |
| CASE(DrawPath) | |
| CASE(DrawRect) | |
| CASE(DrawTextBlob) | |
| } | |
| SkASSERT(false); | |
| return nullptr; | |
| #undef CASE | |
| } | |
| void SkMiniRecorder::flushAndReset(SkCanvas* canvas) { | |
| #define CASE(Type) \ | |
| case State::k##Type: { \ | |
| fState = State::kEmpty; \ | |
| Type* op = reinterpret_cast<Type*>(fBuffer); \ | |
| SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \ | |
| op->~Type(); \ | |
| } return | |
| switch (fState) { | |
| case State::kEmpty: return; | |
| CASE(DrawPath); | |
| CASE(DrawRect); | |
| CASE(DrawTextBlob); | |
| } | |
| SkASSERT(false); | |
| #undef CASE | |
| } |