/*
 * 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