blob: ff658ff20d146f92158d0b6eb00678bbc31124f7 [file]
/*
* 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 "SkPath.h"
#include "SkRect.h"
#include "SkRemote.h"
#include "SkShader.h"
#include "SkTHash.h"
namespace SkRemote {
Misc Misc::CreateFrom(const SkPaint& paint) {
Misc misc = {
paint.getColor(),
paint.getFilterQuality(),
paint.isAntiAlias(),
paint.isDither(),
};
return misc;
}
void Misc::applyTo(SkPaint* paint) const {
paint->setColor (fColor);
paint->setFilterQuality(fFilterQuality);
paint->setAntiAlias (fAntiAlias);
paint->setDither (fDither);
}
static bool operator==(const Misc& a, const Misc& b) {
return a.fColor == b.fColor
&& a.fFilterQuality == b.fFilterQuality
&& a.fAntiAlias == b.fAntiAlias
&& a.fDither == b.fDither;
}
// Misc carries 10 bytes of data in a 12 byte struct, so we need a custom hash.
static_assert(sizeof(Misc) > offsetof(Misc, fDither) + sizeof(Misc().fDither), "");
struct MiscHash {
uint32_t operator()(const Misc& misc) {
return SkChecksum::Murmur3(&misc, offsetof(Misc, fDither) + sizeof(Misc().fDither));
}
};
Stroke Stroke::CreateFrom(const SkPaint& paint) {
Stroke stroke = {
paint.getStrokeWidth(),
paint.getStrokeMiter(),
paint.getStrokeCap(),
paint.getStrokeJoin(),
};
return stroke;
}
void Stroke::applyTo(SkPaint* paint) const {
paint->setStrokeWidth(fWidth);
paint->setStrokeMiter(fMiter);
paint->setStrokeCap (fCap);
paint->setStrokeJoin (fJoin);
}
static bool operator==(const Stroke& a, const Stroke& b) {
return a.fWidth == b.fWidth
&& a.fMiter == b.fMiter
&& a.fCap == b.fCap
&& a.fJoin == b.fJoin;
}
// The default SkGoodHash works fine for Stroke, as it's dense.
static_assert(sizeof(Stroke) == offsetof(Stroke, fJoin) + sizeof(Stroke().fJoin), "");
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
class Canvas final : public SkCanvas {
public:
explicit Canvas(Encoder* encoder)
: SkCanvas(1,1)
, fEncoder(encoder) {}
private:
// Calls Encoder::define() when created, Encoder::undefine() when destroyed.
class AutoID : ::SkNoncopyable {
public:
template <typename T>
explicit AutoID(Encoder* encoder, const T& val)
: fEncoder(encoder)
, fID(encoder->define(val)) {}
~AutoID() { if (fEncoder) fEncoder->undefine(fID); }
AutoID(AutoID&& o) : fEncoder(o.fEncoder), fID(o.fID) {
o.fEncoder = nullptr;
}
AutoID& operator=(AutoID&&) = delete;
operator ID () const { return fID; }
private:
Encoder* fEncoder;
const ID fID;
};
template <typename T>
AutoID id(const T& val) { return AutoID(fEncoder, val); }
void willSave() override { fEncoder-> save(); }
void didRestore() override { fEncoder->restore(); }
void didConcat(const SkMatrix&) override { this->didSetMatrix(this->getTotalMatrix()); }
void didSetMatrix(const SkMatrix& matrix) override {
fEncoder->setMatrix(this->id(matrix));
}
void onDrawOval(const SkRect& oval, const SkPaint& paint) override {
SkPath path;
path.addOval(oval);
this->onDrawPath(path, paint);
}
void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
SkPath path;
path.addRect(rect);
this->onDrawPath(path, paint);
}
void onDrawRRect(const SkRRect& rrect, const SkPaint& paint) override {
SkPath path;
path.addRRect(rrect);
this->onDrawPath(path, paint);
}
void onDrawDRRect(const SkRRect& outside, const SkRRect& inside,
const SkPaint& paint) override {
SkPath path;
path.addRRect(outside);
path.addRRect(inside, SkPath::kCCW_Direction);
this->onDrawPath(path, paint);
}
void onDrawPath(const SkPath& path, const SkPaint& paint) override {
auto p = this->id(path),
m = this->id(Misc::CreateFrom(paint)),
s = this->id(paint.getShader()),
x = this->id(paint.getXfermode());
if (paint.getStyle() == SkPaint::kFill_Style) {
fEncoder->fillPath(p, m, s, x);
} else {
// TODO: handle kStrokeAndFill_Style
fEncoder->strokePath(p, m, s, x, this->id(Stroke::CreateFrom(paint)));
}
}
void onDrawPaint(const SkPaint& paint) override {
SkPath path;
path.setFillType(SkPath::kInverseWinding_FillType); // Either inverse FillType is fine.
this->onDrawPath(path, paint);
}
void onDrawText(const void* text, size_t byteLength,
SkScalar x, SkScalar y, const SkPaint& paint) override {
// Text-as-paths is a temporary hack.
// TODO: send SkTextBlobs and SkTypefaces
SkPath path;
paint.getTextPath(text, byteLength, x, y, &path);
this->onDrawPath(path, paint);
}
void onDrawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint) override {
// Text-as-paths is a temporary hack.
// TODO: send SkTextBlobs and SkTypefaces
SkPath path;
paint.getPosTextPath(text, byteLength, pos, &path);
this->onDrawPath(path, paint);
}
void onDrawPosTextH(const void* text, size_t byteLength,
const SkScalar xpos[], SkScalar constY, const SkPaint& paint) override {
size_t length = paint.countText(text, byteLength);
SkAutoTArray<SkPoint> pos(length);
for(size_t i = 0; i < length; ++i) {
pos[i].set(xpos[i], constY);
}
this->onDrawPosText(text, byteLength, &pos[0], paint);
}
void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
SkPath path;
path.addRect(rect);
this->onClipPath(path, op, edgeStyle);
}
void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
SkPath path;
path.addRRect(rrect);
this->onClipPath(path, op, edgeStyle);
}
void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
fEncoder->clipPath(this->id(path), op, edgeStyle == kSoft_ClipEdgeStyle);
}
Encoder* fEncoder;
};
SkCanvas* NewCanvas(Encoder* encoder) { return new Canvas(encoder); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
class Decoder final : public Encoder {
public:
explicit Decoder(SkCanvas* canvas) : fCanvas(canvas) {}
private:
template <typename Map, typename T>
ID define(Type type, Map* map, const T& val) {
ID id(type, fNextID++);
map->set(id, val);
return id;
}
ID define(const SkMatrix& v) override {return this->define(Type::kMatrix, &fMatrix, v);}
ID define(const Misc& v) override {return this->define(Type::kMisc, &fMisc, v);}
ID define(const SkPath& v) override {return this->define(Type::kPath, &fPath, v);}
ID define(const Stroke& v) override {return this->define(Type::kStroke, &fStroke, v);}
ID define(SkShader* v) override {return this->define(Type::kShader, &fShader, v);}
ID define(SkXfermode* v) override {return this->define(Type::kXfermode, &fXfermode, v);}
void undefine(ID id) override {
switch(id.type()) {
case Type::kMatrix: return fMatrix .remove(id);
case Type::kMisc: return fMisc .remove(id);
case Type::kPath: return fPath .remove(id);
case Type::kStroke: return fStroke .remove(id);
case Type::kShader: return fShader .remove(id);
case Type::kXfermode: return fXfermode.remove(id);
};
}
void save() override { fCanvas->save(); }
void restore() override { fCanvas->restore(); }
void setMatrix(ID matrix) override { fCanvas->setMatrix(fMatrix.find(matrix)); }
void clipPath(ID path, SkRegion::Op op, bool aa) override {
fCanvas->clipPath(fPath.find(path), op, aa);
}
void fillPath(ID path, ID misc, ID shader, ID xfermode) override {
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
fMisc.find(misc).applyTo(&paint);
paint.setShader (fShader .find(shader));
paint.setXfermode(fXfermode.find(xfermode));
fCanvas->drawPath(fPath.find(path), paint);
}
void strokePath(ID path, ID misc, ID shader, ID xfermode, ID stroke) override {
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
fMisc .find(misc ).applyTo(&paint);
fStroke.find(stroke).applyTo(&paint);
paint.setShader (fShader .find(shader));
paint.setXfermode(fXfermode.find(xfermode));
fCanvas->drawPath(fPath.find(path), paint);
}
// Maps ID -> T.
template <typename T, Type kType>
class IDMap {
public:
~IDMap() {
// A well-behaved client always cleans up its definitions.
SkASSERT(fMap.count() == 0);
}
void set(const ID& id, const T& val) {
SkASSERT(id.type() == kType);
fMap.set(id, val);
}
void remove(const ID& id) {
SkASSERT(id.type() == kType);
fMap.remove(id);
}
const T& find(const ID& id) const {
SkASSERT(id.type() == kType);
T* val = fMap.find(id);
SkASSERT(val != nullptr);
return *val;
}
private:
SkTHashMap<ID, T> fMap;
};
// Maps ID -> T*, and keeps the T alive by reffing it.
template <typename T, Type kType>
class ReffedIDMap {
public:
ReffedIDMap() {}
~ReffedIDMap() {
// A well-behaved client always cleans up its definitions.
SkASSERT(fMap.count() == 0);
}
void set(const ID& id, T* val) {
SkASSERT(id.type() == kType);
fMap.set(id, SkSafeRef(val));
}
void remove(const ID& id) {
SkASSERT(id.type() == kType);
T** val = fMap.find(id);
SkASSERT(val);
SkSafeUnref(*val);
fMap.remove(id);
}
T* find(const ID& id) const {
SkASSERT(id.type() == kType);
T** val = fMap.find(id);
SkASSERT(val);
return *val;
}
private:
SkTHashMap<ID, T*> fMap;
};
IDMap<SkMatrix , Type::kMatrix> fMatrix;
IDMap<Misc , Type::kMisc > fMisc;
IDMap<SkPath , Type::kPath > fPath;
IDMap<Stroke , Type::kStroke> fStroke;
ReffedIDMap<SkShader , Type::kShader> fShader;
ReffedIDMap<SkXfermode, Type::kXfermode> fXfermode;
SkCanvas* fCanvas;
uint64_t fNextID = 0;
};
Encoder* NewDecoder(SkCanvas* canvas) { return new Decoder(canvas); }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
class CachingEncoder final : public Encoder {
public:
explicit CachingEncoder(Encoder* wrapped) : fWrapped(wrapped) {}
private:
struct Undef {
Encoder* fEncoder;
template <typename T>
void operator()(const T&, ID* id) const { fEncoder->undefine(*id); }
};
~CachingEncoder() override {
Undef undef{fWrapped};
fMatrix .foreach(undef);
fMisc .foreach(undef);
fPath .foreach(undef);
fStroke .foreach(undef);
fShader .foreach(undef);
fXfermode.foreach(undef);
}
template <typename Map, typename T>
ID define(Map* map, const T& v) {
if (const ID* id = map->find(v)) {
return *id;
}
ID id = fWrapped->define(v);
map->set(v, id);
return id;
}
ID define(const SkMatrix& v) override { return this->define(&fMatrix, v); }
ID define(const Misc& v) override { return this->define(&fMisc, v); }
ID define(const SkPath& v) override { return this->define(&fPath, v); }
ID define(const Stroke& v) override { return this->define(&fStroke, v); }
ID define(SkShader* v) override { return this->define(&fShader, v); }
ID define(SkXfermode* v) override { return this->define(&fXfermode, v); }
void undefine(ID) override {}
void save() override { fWrapped-> save(); }
void restore() override { fWrapped->restore(); }
void setMatrix(ID matrix) override { fWrapped->setMatrix(matrix); }
void clipPath(ID path, SkRegion::Op op, bool aa) override {
fWrapped->clipPath(path, op, aa);
}
void fillPath(ID path, ID misc, ID shader, ID xfermode) override {
fWrapped->fillPath(path, misc, shader, xfermode);
}
void strokePath(ID path, ID misc, ID shader, ID xfermode, ID stroke) override {
fWrapped->strokePath(path, misc, shader, xfermode, stroke);
}
// Maps const T* -> ID, and refs the key.
template <typename T, Type kType>
class RefKeyMap {
public:
RefKeyMap() {}
~RefKeyMap() { fMap.foreach([](const T* key, ID*) { SkSafeUnref(key); }); }
void set(const T* key, ID id) {
SkASSERT(id.type() == kType);
fMap.set(SkSafeRef(key), id);
}
void remove(const T* key) {
fMap.remove(key);
SkSafeUnref(key);
}
const ID* find(const T* key) const {
return fMap.find(key);
}
template <typename Fn>
void foreach(const Fn& fn) {
fMap.foreach(fn);
}
private:
SkTHashMap<const T*, ID> fMap;
};
SkTHashMap<SkMatrix, ID> fMatrix;
SkTHashMap<Misc, ID, MiscHash> fMisc;
SkTHashMap<SkPath, ID> fPath;
SkTHashMap<Stroke, ID> fStroke;
RefKeyMap<SkShader, Type::kShader> fShader;
RefKeyMap<SkXfermode, Type::kXfermode> fXfermode;
Encoder* fWrapped;
};
Encoder* NewCachingEncoder(Encoder* wrapped) { return new CachingEncoder(wrapped); }
} // namespace SkRemote