blob: 3966aaccc0318afba694f8775f00fdb6617d4eb4 [file] [log] [blame]
/*
* 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;
}
// 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 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& val) {
ID id;
if (!fCache->lookup(val, &id, this)) {
fEncoder->define(id, val);
}
return id;
}
private:
Cache* fCache;
Encoder* fEncoder;
SkSTArray<4, ID> fToUndefine;
};
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, MiscHash> 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);
}
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::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) {
SkPath path;
path.addRRect(rrect);
this->onDrawPath(path, paint);
}
void Client::onDrawDRRect(const SkRRect& outside,
const SkRRect& inside,
const SkPaint& paint) {
SkPath path;
path.addRRect(outside);
path.addRRect(inside, SkPath::kCCW_Direction);
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::onDrawPaint(const SkPaint& paint) {
SkPath path;
path.setFillType(SkPath::kInverseWinding_FillType); // Either inverse FillType works fine.
this->onDrawPath(path, paint);
}
void Client::onDrawText(const void* text, size_t byteLength, SkScalar x,
SkScalar y, const SkPaint& paint) {
// 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 Client::onDrawPosText(const void* text, size_t byteLength,
const SkPoint pos[], const SkPaint& paint) {
// 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 Client::onDrawPosTextH(const void* text, size_t byteLength,
const SkScalar xpos[], SkScalar constY,
const SkPaint& paint) {
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 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