SkRemote
BUG=skia:
Review URL: https://codereview.chromium.org/1391023005
diff --git a/dm/DM.cpp b/dm/DM.cpp
index ccff84c..5d98fe7 100644
--- a/dm/DM.cpp
+++ b/dm/DM.cpp
@@ -611,6 +611,8 @@
VIA("sp", ViaSingletonPictures, wrapped);
VIA("tiles", ViaTiles, 256, 256, nullptr, wrapped);
VIA("tiles_rt", ViaTiles, 256, 256, new SkRTreeFactory, wrapped);
+ VIA("remote", ViaRemote, false, wrapped);
+ VIA("remote_cache", ViaRemote, true, wrapped);
if (FLAGS_matrix.count() == 4) {
SkMatrix m;
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 4137154..394b84f 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -23,6 +23,7 @@
#include "SkRandom.h"
#include "SkRecordDraw.h"
#include "SkRecorder.h"
+#include "SkRemote.h"
#include "SkSVGCanvas.h"
#include "SkScaledCodec.h"
#include "SkStream.h"
@@ -1016,6 +1017,16 @@
});
}
+Error ViaRemote::draw(const Src& src, SkBitmap* bitmap, SkWStream* stream, SkString* log) const {
+ return draw_to_canvas(fSink, bitmap, stream, log, src.size(), [&](SkCanvas* canvas) {
+ SkAutoTDelete<SkRemote::Cache> cache(fCache ? SkRemote::Cache::CreateAlwaysCache()
+ : SkRemote::Cache::CreateNeverCache());
+ SkRemote::Server server(canvas);
+ SkRemote::Client client(cache, &server);
+ return src.draw(&client);
+ });
+}
+
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
Error ViaSerialization::draw(
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index d881137..1beaec9 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -304,10 +304,12 @@
Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
};
-class ViaDeferred : public Via {
+class ViaRemote : public Via {
public:
- explicit ViaDeferred(Sink* sink) : Via(sink) {}
+ ViaRemote(bool cache, Sink* sink) : Via(sink), fCache(cache) {}
Error draw(const Src&, SkBitmap*, SkWStream*, SkString*) const override;
+private:
+ bool fCache;
};
class ViaSerialization : public Via {
diff --git a/gyp/core.gypi b/gyp/core.gypi
index dd00301..089a8c0 100644
--- a/gyp/core.gypi
+++ b/gyp/core.gypi
@@ -221,6 +221,7 @@
'<(skia_src_path)/core/SkRegion.cpp',
'<(skia_src_path)/core/SkRegionPriv.h',
'<(skia_src_path)/core/SkRegion_path.cpp',
+ '<(skia_src_path)/core/SkRemote.cpp',
'<(skia_src_path)/core/SkResourceCache.cpp',
'<(skia_src_path)/core/SkRRect.cpp',
'<(skia_src_path)/core/SkRTree.h',
diff --git a/src/core/SkRemote.cpp b/src/core/SkRemote.cpp
new file mode 100644
index 0000000..ae14a47
--- /dev/null
+++ b/src/core/SkRemote.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "SkPath.h"
+#include "SkRect.h"
+#include "SkRemote.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;
+ }
+
+ 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;
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
+
+ Cache* Cache::CreateNeverCache() {
+ struct NeverCache final : public Cache {
+ NeverCache()
+ : fNextMatrix(Type::kMatrix)
+ , fNextMisc (Type::kMisc)
+ , fNextPath (Type::kPath)
+ , fNextStroke(Type::kStroke)
+ {}
+ void cleanup(Encoder*) override {}
+
+ static bool Helper(ID* next, ID* id, LookupScope* ls) {
+ *id = (*next)++;
+ ls->undefineWhenDone(*id);
+ return false;
+ }
+
+ bool lookup(const SkMatrix&, ID* id, LookupScope* ls) override {
+ return Helper(&fNextMatrix, id, ls);
+ }
+ bool lookup(const Misc&, ID* id, LookupScope* ls) override {
+ return Helper(&fNextMisc, id, ls);
+ }
+ bool lookup(const SkPath&, ID* id, LookupScope* ls) override {
+ return Helper(&fNextPath, id, ls);
+ }
+ bool lookup(const Stroke&, ID* id, LookupScope* ls) override {
+ return Helper(&fNextStroke, id, ls);
+ }
+
+ ID fNextMatrix,
+ fNextMisc,
+ fNextPath,
+ fNextStroke;
+ };
+ return new NeverCache;
+ }
+
+ // Can't be declared locally inside AlwaysCache because of the templating. :(
+ template <typename T, typename Map>
+ static bool always_cache_helper(const T& val, Map* map, ID* next, ID* id) {
+ if (ID* found = map->find(val)) {
+ *id = *found;
+ return true;
+ }
+ *id = (*next)++;
+ map->set(val, *id);
+ return false;
+ }
+
+ Cache* Cache::CreateAlwaysCache() {
+ struct AlwaysCache final : public Cache {
+ AlwaysCache()
+ : fNextMatrix(Type::kMatrix)
+ , fNextMisc (Type::kMisc)
+ , fNextPath (Type::kPath)
+ , fNextStroke(Type::kStroke)
+ {}
+
+ void cleanup(Encoder* encoder) override {
+ fMatrix.foreach([=](const SkMatrix&, ID* id) { encoder->undefine(*id); });
+ fMisc .foreach([=](const Misc&, ID* id) { encoder->undefine(*id); });
+ fPath .foreach([=](const SkPath&, ID* id) { encoder->undefine(*id); });
+ fStroke.foreach([=](const Stroke&, ID* id) { encoder->undefine(*id); });
+ }
+
+
+ bool lookup(const SkMatrix& matrix, ID* id, LookupScope*) override {
+ return always_cache_helper(matrix, &fMatrix, &fNextMatrix, id);
+ }
+ bool lookup(const Misc& misc, ID* id, LookupScope*) override {
+ return always_cache_helper(misc, &fMisc, &fNextMisc, id);
+ }
+ bool lookup(const SkPath& path, ID* id, LookupScope*) override {
+ return always_cache_helper(path, &fPath, &fNextPath, id);
+ }
+ bool lookup(const Stroke& stroke, ID* id, LookupScope*) override {
+ return always_cache_helper(stroke, &fStroke, &fNextStroke, id);
+ }
+
+ SkTHashMap<SkMatrix, ID> fMatrix;
+ SkTHashMap<Misc, ID> fMisc;
+ SkTHashMap<SkPath, ID> fPath;
+ SkTHashMap<Stroke, ID> fStroke;
+ ID fNextMatrix,
+ fNextMisc,
+ fNextPath,
+ fNextStroke;
+ };
+ return new AlwaysCache;
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
+
+ Client::Client(Cache* cache, Encoder* encoder)
+ : SkCanvas(1,1)
+ , fCache(cache)
+ , fEncoder(encoder)
+ {}
+
+ Client::~Client() {
+ fCache->cleanup(fEncoder);
+ }
+
+ template <typename T>
+ ID LookupScope::lookup(const T& val) {
+ ID id;
+ if (!fCache->lookup(val, &id, this)) {
+ fEncoder->define(id, val);
+ }
+ return id;
+ }
+
+ void Client::willSave() { fEncoder->save(); }
+ void Client::didRestore() { fEncoder->restore(); }
+
+ void Client::didConcat (const SkMatrix&) { this->didSetMatrix(this->getTotalMatrix()); }
+ void Client::didSetMatrix(const SkMatrix& matrix) {
+ LookupScope ls(fCache, fEncoder);
+ fEncoder->setMatrix(ls.lookup(matrix));
+ }
+
+ void Client::onDrawOval(const SkRect& oval, const SkPaint& paint) {
+ SkPath path;
+ path.addOval(oval);
+ this->onDrawPath(path, paint);
+ }
+
+ void Client::onDrawRect(const SkRect& rect, const SkPaint& paint) {
+ SkPath path;
+ path.addRect(rect);
+ this->onDrawPath(path, paint);
+ }
+
+ void Client::onDrawPath(const SkPath& path, const SkPaint& paint) {
+ LookupScope ls(fCache, fEncoder);
+ ID p = ls.lookup(path),
+ m = ls.lookup(Misc::CreateFrom(paint));
+
+ if (paint.getStyle() == SkPaint::kFill_Style) {
+ fEncoder->fillPath(p, m);
+ } else {
+ // TODO: handle kStrokeAndFill_Style
+ fEncoder->strokePath(p, m, ls.lookup(Stroke::CreateFrom(paint)));
+ }
+ }
+
+ void Client::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ SkPath path;
+ path.addRect(rect);
+ this->onClipPath(path, op, edgeStyle);
+ }
+
+ void Client::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ SkPath path;
+ path.addRRect(rrect);
+ this->onClipPath(path, op, edgeStyle);
+ }
+
+ void Client::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edgeStyle) {
+ LookupScope ls(fCache, fEncoder);
+ fEncoder->clipPath(ls.lookup(path), op, edgeStyle == kSoft_ClipEdgeStyle);
+ }
+
+ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
+
+ Server::Server(SkCanvas* canvas) : fCanvas(canvas) {}
+
+ void Server::define(ID id, const SkMatrix& v) { fMatrix.set(id, v); }
+ void Server::define(ID id, const Misc& v) { fMisc .set(id, v); }
+ void Server::define(ID id, const SkPath& v) { fPath .set(id, v); }
+ void Server::define(ID id, const Stroke& v) { fStroke.set(id, v); }
+
+ void Server::undefine(ID id) {
+ 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::kNone: SkASSERT(false);
+ };
+ }
+
+ void Server:: save() { fCanvas->save(); }
+ void Server::restore() { fCanvas->restore(); }
+
+ void Server::setMatrix(ID matrix) { fCanvas->setMatrix(fMatrix.find(matrix)); }
+
+ void Server::clipPath(ID path, SkRegion::Op op, bool aa) {
+ fCanvas->clipPath(fPath.find(path), op, aa);
+ }
+ void Server::fillPath(ID path, ID misc) {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kFill_Style);
+ fMisc.find(misc).applyTo(&paint);
+ fCanvas->drawPath(fPath.find(path), paint);
+ }
+ void Server::strokePath(ID path, ID misc, ID stroke) {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ fMisc .find(misc ).applyTo(&paint);
+ fStroke.find(stroke).applyTo(&paint);
+ fCanvas->drawPath(fPath.find(path), paint);
+ }
+
+} // namespace SkRemote
diff --git a/src/core/SkRemote.h b/src/core/SkRemote.h
new file mode 100644
index 0000000..87333d8
--- /dev/null
+++ b/src/core/SkRemote.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRemote_DEFINED
+#define SkRemote_DEFINED
+
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRemote_protocol.h"
+#include "SkTHash.h"
+#include "SkTypes.h"
+
+// TODO: document
+
+namespace SkRemote {
+ // TODO: document
+ struct Misc {
+ SkColor fColor;
+ SkFilterQuality fFilterQuality;
+ bool fAntiAlias, fDither;
+
+ static Misc CreateFrom(const SkPaint&);
+ void applyTo(SkPaint*) const;
+ };
+
+ // TODO: document
+ struct Stroke {
+ SkScalar fWidth, fMiter;
+ SkPaint::Cap fCap;
+ SkPaint::Join fJoin;
+
+ static Stroke CreateFrom(const SkPaint&);
+ void applyTo(SkPaint*) const;
+ };
+
+ // TODO: document
+ struct Encoder {
+ virtual ~Encoder() {}
+
+ virtual void define(ID, const SkMatrix&) = 0;
+ virtual void define(ID, const Misc&) = 0;
+ virtual void define(ID, const SkPath&) = 0;
+ virtual void define(ID, const Stroke&) = 0;
+
+ virtual void undefine(ID) = 0;
+
+ virtual void save() = 0;
+ virtual void restore() = 0;
+
+ virtual void setMatrix(ID matrix) = 0;
+
+ virtual void clipPath(ID path, SkRegion::Op, bool aa) = 0;
+ virtual void fillPath(ID path, ID misc) = 0;
+ virtual void strokePath(ID path, ID misc, ID stroke) = 0;
+ };
+
+ struct Cache;
+
+ // TODO: document
+ class LookupScope {
+ public:
+ LookupScope(Cache* cache, Encoder* encoder) : fCache(cache), fEncoder(encoder) {}
+ ~LookupScope() { for (ID id : fToUndefine) { fEncoder->undefine(id); } }
+ void undefineWhenDone(ID id) { fToUndefine.push_back(id); }
+
+ template <typename T>
+ ID lookup(const T&);
+ private:
+ Cache* fCache;
+ Encoder* fEncoder;
+ SkSTArray<4, ID> fToUndefine;
+ };
+
+ // TODO: document
+ struct Cache {
+ virtual ~Cache() {}
+
+ static Cache* CreateNeverCache();
+ static Cache* CreateAlwaysCache();
+
+ virtual bool lookup(const SkMatrix&, ID*, LookupScope*) = 0;
+ virtual bool lookup(const Misc&, ID*, LookupScope*) = 0;
+ virtual bool lookup(const SkPath&, ID*, LookupScope*) = 0;
+ virtual bool lookup(const Stroke&, ID*, LookupScope*) = 0;
+
+ virtual void cleanup(Encoder*) = 0;
+ };
+
+ // TODO: document
+ class Client final : public SkCanvas {
+ public:
+ Client(Cache*, Encoder*);
+ ~Client();
+
+ private:
+ void willSave() override;
+ void didRestore() override;
+
+ void didConcat(const SkMatrix&) override;
+ void didSetMatrix(const SkMatrix&) override;
+
+ void onClipPath (const SkPath&, SkRegion::Op, ClipEdgeStyle) override;
+ void onClipRRect(const SkRRect&, SkRegion::Op, ClipEdgeStyle) override;
+ void onClipRect (const SkRect&, SkRegion::Op, ClipEdgeStyle) override;
+
+ void onDrawOval(const SkRect&, const SkPaint&) override;
+ void onDrawPath(const SkPath&, const SkPaint&) override;
+ void onDrawRect(const SkRect&, const SkPaint&) override;
+
+ Cache* fCache;
+ Encoder* fEncoder;
+ };
+
+ // TODO: document
+ class Server final : public Encoder {
+ public:
+ explicit Server(SkCanvas*);
+
+ private:
+ void define(ID, const SkMatrix&) override;
+ void define(ID, const Misc&) override;
+ void define(ID, const SkPath&) override;
+ void define(ID, const Stroke&) override;
+
+ void undefine(ID) override;
+
+ void save() override;
+ void restore() override;
+
+ void setMatrix(ID matrix) override;
+
+ void clipPath(ID path, SkRegion::Op, bool aa) override;
+ void fillPath(ID path, ID misc) override;
+ void strokePath(ID path, ID misc, ID stroke) override;
+
+ template <typename T, Type kType>
+ class IDMap {
+ public:
+ 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;
+ };
+
+ IDMap<SkMatrix, Type::kMatrix> fMatrix;
+ IDMap<Misc , Type::kMisc > fMisc;
+ IDMap<SkPath , Type::kPath > fPath;
+ IDMap<Stroke , Type::kStroke> fStroke;
+
+ SkCanvas* fCanvas;
+ };
+
+} // namespace SkRemote
+
+#endif//SkRemote_DEFINED
diff --git a/src/core/SkRemote_protocol.h b/src/core/SkRemote_protocol.h
new file mode 100644
index 0000000..9975213
--- /dev/null
+++ b/src/core/SkRemote_protocol.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRemote_protocol_DEFINED
+#define SkRemote_protocol_DEFINED
+
+// ATTENTION! Changes to this file can break protocol compatibility. Tread carefully.
+
+namespace SkRemote {
+
+ // It is safe to append to this enum without breaking protocol compatibility.
+ // Resorting, deleting, or inserting anywhere but the end will break compatibility.
+ enum class Type : uint8_t {
+ kNone,
+
+ kMatrix,
+ kMisc,
+ kPath,
+ kStroke,
+ };
+
+ class ID {
+ public:
+ explicit ID(Type type = Type::kNone) : fVal((uint64_t)type << 56) {}
+ ID(Type type, uint64_t val) {
+ fVal = (uint64_t)type << 56 | val;
+ SkASSERT(this->type() == type && this->val() == val);
+ }
+
+ Type type() const { return (Type)(fVal >> 56); }
+ uint64_t val() const { return fVal & ~((uint64_t)0xFF << 56); }
+
+ bool operator==(ID o) const { return fVal == o.fVal; }
+ ID operator++(int) {
+ ID prev = *this;
+ fVal++;
+ SkASSERT(this->val() != 0); // Overflow is particularly bad as it'd change our Type.
+ return prev;
+ }
+
+ private:
+ // High 8 bits hold a Type. Low 56 bits are unique within that Type.
+ // Any change to this format will break protocol compatibility.
+ uint64_t fVal;
+ };
+
+} // namespace SkRemote
+
+#endif//SkRemote_protocol_DEFINED