skia2/modules/particles/include/SkReflected.h
Brian Osman 5de7ea45bc Particles/SkReflected: Generalize array reflection
This supports arrays of any type, and removes all of the special case
code for arrays of SkReflected objects. (This is extracted from my
rewrite of SkCurve, which needed something like this to work).

Bug: skia:
Change-Id: I55ab942f7922335dca0685d28b3b122bc4d53daa
Reviewed-on: https://skia-review.googlesource.com/c/192620
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2019-02-14 20:04:48 +00:00

205 lines
5.9 KiB
C++

/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkReflected_DEFINED
#define SkReflected_DEFINED
#include "SkColor.h"
#include "SkCurve.h"
#include "SkRefCnt.h"
#include "SkString.h"
#include "SkTArray.h"
class SkFieldVisitor;
class SkRandom;
class SkReflected : public SkRefCnt {
public:
typedef sk_sp<SkReflected>(*Factory)();
struct Type {
const char* fName;
const Type* fBase;
Factory fFactory;
bool isDerivedFrom(const Type* t) const {
const Type* base = fBase;
while (base) {
if (base == t) {
return true;
}
base = base->fBase;
}
return false;
}
};
virtual const Type* getType() const = 0;
static const Type* GetType() {
static Type gType{ "SkReflected", nullptr, nullptr };
return &gType;
}
bool isOfType(const Type* t) const {
const Type* thisType = this->getType();
return thisType == t || thisType->isDerivedFrom(t);
}
static void Register(const Type* type) {
gTypes.push_back(type);
}
static sk_sp<SkReflected> CreateInstance(const char* name) {
for (const Type* type : gTypes) {
if (0 == strcmp(name, type->fName)) {
return type->fFactory();
}
}
return nullptr;
}
virtual void visitFields(SkFieldVisitor*) = 0;
static void VisitTypes(std::function<void(const Type*)> visitor,
const Type* baseType = nullptr);
private:
static SkSTArray<16, const Type*, true> gTypes;
};
#define REFLECTED(TYPE, BASE) \
static sk_sp<SkReflected> CreateProc() { \
return sk_sp<SkReflected>(new TYPE()); \
} \
static const Type* GetType() { \
static Type gType{ #TYPE, BASE::GetType(), CreateProc }; \
return &gType; \
} \
const Type* getType() const override { return GetType(); }
#define REFLECTED_ABSTRACT(TYPE, BASE) \
static const Type* GetType() { \
static Type gType{ #TYPE, BASE::GetType(), nullptr }; \
return &gType; \
} \
const Type* getType() const override { return GetType(); }
#define REGISTER_REFLECTED(TYPE) SkReflected::Register(TYPE::GetType())
///////////////////////////////////////////////////////////////////////////////
struct SkField {
// Other useful flags/properties:
// No UI (to avoid editing dangerous things)
// Point vs. vector
// Range limits
enum SkFieldFlags {
kAngle_Field = 0x1,
};
SkField(uint32_t flags = 0)
: fFlags(flags) {}
uint32_t fFlags;
};
struct SkPoint;
class SkFieldVisitor {
public:
virtual ~SkFieldVisitor() {}
virtual void visit(const char*, float&, SkField = SkField()) = 0;
virtual void visit(const char*, int&, SkField = SkField()) = 0;
virtual void visit(const char*, bool&, SkField = SkField()) = 0;
virtual void visit(const char*, SkString&, SkField = SkField()) = 0;
virtual void visit(const char*, SkPoint&, SkField = SkField()) = 0;
virtual void visit(const char*, SkColor4f&, SkField = SkField()) = 0;
virtual void visit(const char* name, SkCurve& curve, SkField = SkField()) {
this->enterObject(name);
curve.visitFields(this);
this->exitObject();
}
template <typename T>
void visit(const char* name, T& value) {
this->enterObject(name);
value.visitFields(this);
this->exitObject();
}
template <typename T, bool MEM_MOVE>
void visit(const char* name, SkTArray<T, MEM_MOVE>& arr) {
arr.resize_back(this->enterArray(name, arr.count()));
for (int i = 0; i < arr.count(); ++i) {
this->visit(nullptr, arr[i]);
}
this->exitArray().apply(arr);
}
template <typename T>
void visit(const char* name, sk_sp<T>& obj) {
this->enterObject(name);
sk_sp<SkReflected> newObj = obj;
this->visit(newObj, T::GetType());
if (newObj != obj) {
if (!newObj || newObj->isOfType(T::GetType())) {
obj.reset(static_cast<T*>(newObj.release()));
} else {
obj.reset();
}
}
if (obj) {
obj->visitFields(this);
}
this->exitObject();
}
protected:
struct ArrayEdit {
enum class Verb {
kNone,
kRemove,
kMoveForward,
};
Verb fVerb = Verb::kNone;
int fIndex = 0;
template <typename T, bool MEM_MOVE>
void apply(SkTArray<T, MEM_MOVE>& arr) const {
switch (fVerb) {
case Verb::kNone:
break;
case Verb::kRemove:
for (int i = fIndex; i < arr.count() - 1; ++i) {
arr[i] = arr[i + 1];
}
arr.pop_back();
break;
case Verb::kMoveForward:
if (fIndex > 0 && fIndex < arr.count()) {
std::swap(arr[fIndex - 1], arr[fIndex]);
}
break;
}
}
};
virtual void enterObject(const char* name) = 0;
virtual void exitObject() = 0;
virtual int enterArray(const char* name, int oldCount) = 0;
virtual ArrayEdit exitArray() = 0;
virtual void visit(sk_sp<SkReflected>&, const SkReflected::Type* baseType) = 0;
};
#endif // SkReflected_DEFINED