/*
* 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 "SkAnnotation.h"
#include "SkCanvas.h"
#include "SkColorFilter.h"
#include "SkDrawLooper.h"
#include "SkImage.h"
#include "SkImageFilter.h"
#include "SkMaskFilter.h"
#include "SkNinePatchIter.h"
#include "SkPath.h"
#include "SkPathEffect.h"
#include "SkRasterizer.h"
#include "SkRect.h"
#include "SkRemote.h"
#include "SkShader.h"
#include "SkTHash.h"
#include "SkTextBlob.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;
};
// Like AutoID, but for CommonIDs.
class AutoCommonIDs : ::SkNoncopyable {
public:
explicit AutoCommonIDs(Encoder* encoder, const SkPaint& paint)
: fEncoder(encoder) {
fIDs.misc = fEncoder->define(Misc::CreateFrom(paint));
fIDs.patheffect = fEncoder->define(paint.getPathEffect());
fIDs.shader = fEncoder->define(paint.getShader());
fIDs.xfermode = fEncoder->define(paint.getXfermode());
fIDs.maskfilter = fEncoder->define(paint.getMaskFilter());
fIDs.colorfilter = fEncoder->define(paint.getColorFilter());
fIDs.rasterizer = fEncoder->define(paint.getRasterizer());
fIDs.looper = fEncoder->define(paint.getLooper());
fIDs.imagefilter = fEncoder->define(paint.getImageFilter());
fIDs.annotation = fEncoder->define(paint.getAnnotation());
}
~AutoCommonIDs() {
if (fEncoder) {
fEncoder->undefine(fIDs.misc);
fEncoder->undefine(fIDs.patheffect);
fEncoder->undefine(fIDs.shader);
fEncoder->undefine(fIDs.xfermode);
fEncoder->undefine(fIDs.maskfilter);
fEncoder->undefine(fIDs.colorfilter);
fEncoder->undefine(fIDs.rasterizer);
fEncoder->undefine(fIDs.looper);
fEncoder->undefine(fIDs.imagefilter);
fEncoder->undefine(fIDs.annotation);
}
}
AutoCommonIDs(AutoCommonIDs&& o) : fEncoder(o.fEncoder), fIDs(o.fIDs) {
o.fEncoder = nullptr;
}
AutoID& operator=(AutoID&&) = delete;
operator Encoder::CommonIDs () const { return fIDs; }
private:
Encoder* fEncoder;
Encoder::CommonIDs fIDs;
};
template <typename T>
AutoID id(const T& val) { return AutoID(fEncoder, val); }
AutoCommonIDs commonIDs(const SkPaint& paint) { return AutoCommonIDs(fEncoder, paint); }
void willSave() override { fEncoder-> save(); }
void didRestore() override { fEncoder->restore(); }
SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override {
SkPath path;
if (rec.fBounds) {
path.addRect(*rec.fBounds);
}
const SkPaint defaultPaint;
const SkPaint* paint = rec.fPaint;
if (!paint) {
paint = &defaultPaint;
}
fEncoder->saveLayer(this->id(path), this->commonIDs(*paint), rec.fSaveLayerFlags);
return kNoLayer_SaveLayerStrategy;
}
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 common = this->commonIDs(paint);
auto p = this->id(path);
if (paint.getStyle() == SkPaint::kFill_Style) {
fEncoder->fillPath(p, common);
} else {
// TODO: handle kStrokeAndFill_Style
fEncoder->strokePath(p, common, 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 onDrawPoints(PointMode mode,
size_t count,
const SkPoint pts[],
const SkPaint& paint) override {
// TODO
}
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// TODO
this->INHERITED::onDrawDrawable(drawable, matrix);
}
void onDrawPicture(const SkPicture* pic,
const SkMatrix* matrix,
const SkPaint* paint) override {
// TODO
this->INHERITED::onDrawPicture(pic, matrix, paint);
}
void onDrawVertices(VertexMode vmode,
int vertexCount,
const SkPoint vertices[],
const SkPoint texs[],
const SkColor colors[],
SkXfermode* xmode,
const uint16_t indices[],
int indexCount,
const SkPaint& paint) override {
// TODO
}
void onDrawPatch(const SkPoint cubics[12],
const SkColor colors[4],
const SkPoint texCoords[4],
SkXfermode* xmode,
const SkPaint& paint) override {
// TODO
}
void onDrawAtlas(const SkImage* atlas,
const SkRSXform xform[],
const SkRect tex[],
const SkColor colors[],
int count,
SkXfermode::Mode mode,
const SkRect* cull,
const SkPaint* paint) override {
// TODO
}
void onDrawBitmap(const SkBitmap& bitmap,
SkScalar left,
SkScalar top,
const SkPaint* paint) override {
auto src = SkRect::MakeWH(bitmap.width(), bitmap.height()),
dst = src.makeOffset(left, top);
this->onDrawBitmapRect(bitmap, &src, dst, paint, kStrict_SrcRectConstraint);
}
void onDrawBitmapRect(const SkBitmap& bitmap,
const SkRect* src,
const SkRect& dst,
const SkPaint* paint,
SrcRectConstraint constraint) override {
SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(bitmap));
this->onDrawImageRect(image, src, dst, paint, constraint);
}
void onDrawImage(const SkImage* image,
SkScalar left,
SkScalar top,
const SkPaint* paint) override {
if (!image) {
return;
}
auto src = SkRect::MakeWH(image->width(), image->height()),
dst = src.makeOffset(left, top);
this->onDrawImageRect(image, &src, dst, paint, kStrict_SrcRectConstraint);
}
void onDrawImageRect(const SkImage* image,
const SkRect* src,
const SkRect& dst,
const SkPaint* paint,
SrcRectConstraint constraint) override {
// TODO: this is all a (likely buggy) hack to get images drawing quickly.
if (!image) {
return;
}
auto bounds = SkRect::MakeWH(image->width(), image->height());
if (!src) {
src = &bounds;
}
auto matrix = SkMatrix::MakeRectToRect(*src, dst, SkMatrix::kFill_ScaleToFit);
SkAutoTUnref<SkImage> subset;
if (src) {
if (!bounds.intersect(*src)) {
return;
}
subset.reset(image->newSubset(bounds.roundOut()));
image = subset;
}
auto paintWithShader = paint ? *paint : SkPaint();
SkAutoTUnref<SkShader> shader(
image->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix));
paintWithShader.setShader(shader);
this->onDrawRect(dst, paintWithShader);
}
void onDrawBitmapNine(const SkBitmap& bitmap,
const SkIRect& center,
const SkRect& dst,
const SkPaint* paint) override {
SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(bitmap));
this->onDrawImageNine(image, center, dst, paint);
}
void onDrawImageNine(const SkImage* image,
const SkIRect& center,
const SkRect& dst,
const SkPaint* paint) override {
SkNinePatchIter iter(image->width(), image->height(), center, dst);
SkRect s,d;
while (iter.next(&s, &d)) {
this->onDrawImageRect(image, &s, d, paint, kStrict_SrcRectConstraint);
}
}
void onDrawTextBlob(const SkTextBlob* text,
SkScalar x,
SkScalar y,
const SkPaint& paint) override {
SkPoint offset{x,y};
auto t = this->id(text);
auto common = this->commonIDs(paint);
if (paint.getStyle() == SkPaint::kFill_Style) {
fEncoder->fillText(t, offset, common);
} else {
// TODO: handle kStrokeAndFill_Style
fEncoder->strokeText(t, offset, common, this->id(Stroke::CreateFrom(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);
}
// All clip calls need to call their parent method or we'll not get any quick rejects.
void onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
this->INHERITED::onClipRect(rect, op, edgeStyle);
SkPath path;
path.addRect(rect);
this->onClipPath(path, op, edgeStyle);
}
void onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
this->INHERITED::onClipRRect(rrect, op, edgeStyle);
SkPath path;
path.addRRect(rrect);
this->onClipPath(path, op, edgeStyle);
}
void onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) override {
this->INHERITED::onClipPath(path, op, edgeStyle);
fEncoder->clipPath(this->id(path), op, edgeStyle == kSoft_ClipEdgeStyle);
}
void onClipRegion(const SkRegion& region, SkRegion::Op op) override {
this->INHERITED::onClipRegion(region, op);
// TODO
}
Encoder* fEncoder;
typedef SkCanvas INHERITED;
};
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;
}
#define O override
ID define(const SkMatrix& v)O{return this->define(Type::kMatrix, &fMatrix, v);}
ID define(const Misc& v)O{return this->define(Type::kMisc, &fMisc, v);}
ID define(const SkPath& v)O{return this->define(Type::kPath, &fPath, v);}
ID define(const Stroke& v)O{return this->define(Type::kStroke, &fStroke, v);}
ID define(const SkTextBlob* v)O{return this->define(Type::kTextBlob, &fTextBlob, v);}
ID define(SkPathEffect* v)O{return this->define(Type::kPathEffect, &fPathEffect, v);}
ID define(SkShader* v)O{return this->define(Type::kShader, &fShader, v);}
ID define(SkXfermode* v)O{return this->define(Type::kXfermode, &fXfermode, v);}
ID define(SkMaskFilter* v)O{return this->define(Type::kMaskFilter, &fMaskFilter, v);}
ID define(SkColorFilter* v)O{return this->define(Type::kColorFilter, &fColorFilter, v);}
ID define(SkRasterizer* v)O{return this->define(Type::kRasterizer, &fRasterizer, v);}
ID define(SkDrawLooper* v)O{return this->define(Type::kDrawLooper, &fDrawLooper, v);}
ID define(SkImageFilter* v)O{return this->define(Type::kImageFilter, &fImageFilter, v);}
ID define(SkAnnotation* v)O{return this->define(Type::kAnnotation, &fAnnotation, v);}
#undef O
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::kTextBlob: return fTextBlob .remove(id);
case Type::kPathEffect: return fPathEffect .remove(id);
case Type::kShader: return fShader .remove(id);
case Type::kXfermode: return fXfermode .remove(id);
case Type::kMaskFilter: return fMaskFilter .remove(id);
case Type::kColorFilter: return fColorFilter.remove(id);
case Type::kRasterizer: return fRasterizer .remove(id);
case Type::kDrawLooper: return fDrawLooper .remove(id);
case Type::kImageFilter: return fImageFilter.remove(id);
case Type::kAnnotation: return fAnnotation .remove(id);
};
}
void applyCommon(const CommonIDs& common, SkPaint* paint) const {
fMisc.find(common.misc).applyTo(paint);
paint->setPathEffect (fPathEffect .find(common.patheffect));
paint->setShader (fShader .find(common.shader));
paint->setXfermode (fXfermode .find(common.xfermode));
paint->setMaskFilter (fMaskFilter .find(common.maskfilter));
paint->setColorFilter(fColorFilter.find(common.colorfilter));
paint->setRasterizer (fRasterizer .find(common.rasterizer));
paint->setLooper (fDrawLooper .find(common.looper));
paint->setImageFilter(fImageFilter.find(common.imagefilter));
paint->setAnnotation (fAnnotation .find(common.annotation));
}
void save() override { fCanvas->save(); }
void restore() override { fCanvas->restore(); }
void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveLayerFlags flags) override {
SkPaint paint;
this->applyCommon(common, &paint);
SkRect rect;
fCanvas->saveLayer({ fPath.find(bounds).isRect(&rect) ? &rect : nullptr,
&paint, flags });
}
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, CommonIDs common) override {
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
this->applyCommon(common, &paint);
fCanvas->drawPath(fPath.find(path), paint);
}
void strokePath(ID path, CommonIDs common, ID stroke) override {
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
this->applyCommon(common, &paint);
fStroke.find(stroke).applyTo(&paint);
fCanvas->drawPath(fPath.find(path), paint);
}
void fillText(ID text, SkPoint offset, CommonIDs common) override {
SkPaint paint;
paint.setStyle(SkPaint::kFill_Style);
this->applyCommon(common, &paint);
fCanvas->drawTextBlob(fTextBlob.find(text), offset.x(), offset.y(), paint);
}
void strokeText(ID text, SkPoint offset, CommonIDs common, ID stroke) override {
SkPaint paint;
this->applyCommon(common, &paint);
fStroke.find(stroke).applyTo(&paint);
fCanvas->drawTextBlob(fTextBlob.find(text), offset.x(), offset.y(), 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<const SkTextBlob, Type::kTextBlob > fTextBlob;
ReffedIDMap<SkPathEffect , Type::kPathEffect > fPathEffect;
ReffedIDMap<SkShader , Type::kShader > fShader;
ReffedIDMap<SkXfermode , Type::kXfermode > fXfermode;
ReffedIDMap<SkMaskFilter , Type::kMaskFilter > fMaskFilter;
ReffedIDMap<SkColorFilter , Type::kColorFilter> fColorFilter;
ReffedIDMap<SkRasterizer , Type::kRasterizer > fRasterizer;
ReffedIDMap<SkDrawLooper , Type::kDrawLooper > fDrawLooper;
ReffedIDMap<SkImageFilter , Type::kImageFilter> fImageFilter;
ReffedIDMap<SkAnnotation , Type::kAnnotation > fAnnotation;
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);
fTextBlob .foreach(undef);
fPathEffect .foreach(undef);
fShader .foreach(undef);
fXfermode .foreach(undef);
fMaskFilter .foreach(undef);
fColorFilter.foreach(undef);
fRasterizer .foreach(undef);
fDrawLooper .foreach(undef);
fImageFilter.foreach(undef);
fAnnotation .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(const SkTextBlob* v) override { return this->define(&fTextBlob , v); }
ID define(SkPathEffect* v) override { return this->define(&fPathEffect , v); }
ID define(SkShader* v) override { return this->define(&fShader , v); }
ID define(SkXfermode* v) override { return this->define(&fXfermode , v); }
ID define(SkMaskFilter* v) override { return this->define(&fMaskFilter , v); }
ID define(SkColorFilter* v) override { return this->define(&fColorFilter, v); }
ID define(SkRasterizer* v) override { return this->define(&fRasterizer , v); }
ID define(SkDrawLooper* v) override { return this->define(&fDrawLooper , v); }
ID define(SkImageFilter* v) override { return this->define(&fImageFilter, v); }
ID define(SkAnnotation* v) override { return this->define(&fAnnotation , v); }
void undefine(ID) override {}
void save() override { fWrapped-> save(); }
void restore() override { fWrapped->restore(); }
void saveLayer(ID bounds, CommonIDs common, SkCanvas::SaveLayerFlags flags) override {
fWrapped->saveLayer(bounds, common, flags);
}
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, CommonIDs common) override {
fWrapped->fillPath(path, common);
}
void strokePath(ID path, CommonIDs common, ID stroke) override {
fWrapped->strokePath(path, common, stroke);
}
void fillText(ID text, SkPoint offset, CommonIDs common) override {
fWrapped->fillText(text, offset, common);
}
void strokeText(ID text, SkPoint offset, CommonIDs common, ID stroke) override {
fWrapped->strokeText(text, offset, common, 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<const SkTextBlob, Type::kTextBlob > fTextBlob;
RefKeyMap<SkPathEffect , Type::kPathEffect > fPathEffect;
RefKeyMap<SkShader , Type::kShader > fShader;
RefKeyMap<SkXfermode , Type::kXfermode > fXfermode;
RefKeyMap<SkMaskFilter , Type::kMaskFilter > fMaskFilter;
RefKeyMap<SkColorFilter , Type::kColorFilter> fColorFilter;
RefKeyMap<SkRasterizer , Type::kRasterizer > fRasterizer;
RefKeyMap<SkDrawLooper , Type::kDrawLooper > fDrawLooper;
RefKeyMap<SkImageFilter , Type::kImageFilter> fImageFilter;
RefKeyMap<SkAnnotation , Type::kAnnotation > fAnnotation;
Encoder* fWrapped;
};
Encoder* NewCachingEncoder(Encoder* wrapped) { return new CachingEncoder(wrapped); }
} // namespace SkRemote