Home | History | Annotate | Download | only in core
      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