/*
* 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 "SkCanvas.h"
#include "SkTLazy.h"
#include "SkMiniRecorder.h"
#include "SkOncePtr.h"
#include "SkPicture.h"
#include "SkPictureCommon.h"
#include "SkRecordDraw.h"
#include "SkTextBlob.h"
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() const override { return 0; }
SkRect cullRect() const override { return SkRect::MakeEmpty(); }
bool hasText() const override { return false; }
int numSlowPaths() const override { return 0; }
bool willPlayBackBitmaps() const override { return false; }
};
SK_DECLARE_STATIC_ONCE_PTR(SkEmptyPicture, gEmptyPicture);
template <typename T>
class SkMiniPicture final : public SkPicture {
public:
SkMiniPicture(SkRect cull, T* op) : fCull(cull) {
memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts.
}
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() const override { return 1; }
SkRect cullRect() const override { return fCull; }
bool hasText() const override { return SkTextHunter()(fOp); }
bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); }
int numSlowPaths() const override {
SkPathCounter counter;
counter(fOp);
return counter.fNumSlowPathsAndDashEffects;
}
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.
delete this->detachAsPicture(SkRect::MakeEmpty());
}
SkASSERT(fState == State::kEmpty);
}
#define TRY_TO_STORE(Type, ...) \
if (fState != State::kEmpty) { return false; } \
fState = State::k##Type; \
new (fBuffer.get()) Type{__VA_ARGS__}; \
return true
bool SkMiniRecorder::drawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
const SkPaint* p, SkCanvas::SrcRectConstraint constraint) {
SkRect bounds;
if (!src) {
bm.getBounds(&bounds);
src = &bounds;
}
SkTLazy<SkPaint> defaultPaint;
if (!p) {
p = defaultPaint.init();
}
TRY_TO_STORE(DrawBitmapRectFixedSize, *p, bm, *src, dst, constraint);
}
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, b, x, y);
}
#undef TRY_TO_STORE
SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) {
#define CASE(Type) \
case State::k##Type: \
fState = State::kEmpty; \
return new SkMiniPicture<Type>(cull, reinterpret_cast<Type*>(fBuffer.get()))
switch (fState) {
case State::kEmpty: return SkRef(gEmptyPicture.get([]{ return new SkEmptyPicture; }));
CASE(DrawBitmapRectFixedSize);
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.get()); \
SkRecords::Draw(canvas, nullptr, nullptr, 0, nullptr)(*op); \
op->~Type(); \
} return
switch (fState) {
case State::kEmpty: return;
CASE(DrawBitmapRectFixedSize);
CASE(DrawPath);
CASE(DrawRect);
CASE(DrawTextBlob);
}
SkASSERT(false);
#undef CASE
}