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 "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