blob: 8cbc1c272d76278e06cf00b77503b3f4b162c6de [file] [log] [blame]
/*
* 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 SkParticleSerialization_DEFINED
#define SkParticleSerialization_DEFINED
#include "modules/particles/include/SkReflected.h"
#include "include/core/SkString.h"
#include "include/private/SkTArray.h"
#include "src/utils/SkJSON.h"
#include "src/utils/SkJSONWriter.h"
class SkToJsonVisitor : public SkFieldVisitor {
public:
SkToJsonVisitor(SkJSONWriter& writer) : fWriter(writer) {}
// Primitives
void visit(const char* name, float& f) override {
fWriter.appendFloat(name, f);
}
void visit(const char* name, int& i) override {
fWriter.appendS32(name, i);
}
void visit(const char* name, bool& b) override {
fWriter.appendBool(name, b);
}
void visit(const char* name, SkString& s) override {
if (s.contains('\n')) {
SkTArray<SkString> lines;
SkStrSplit(s.c_str(), "\n", kStrict_SkStrSplitMode, &lines);
fWriter.beginArray(name);
for (const auto& line : lines) {
fWriter.appendString(line.c_str());
}
fWriter.endArray();
} else {
fWriter.appendString(name, s.c_str());
}
}
void visit(const char* name, int& i, const EnumStringMapping* map, int count) override {
fWriter.appendString(name, EnumToString(i, map, count));
}
// Compound types
void visit(const char* name, SkPoint& p) override {
fWriter.beginObject(name, false);
fWriter.appendFloat("x", p.fX);
fWriter.appendFloat("y", p.fY);
fWriter.endObject();
}
void visit(const char* name, SkColor4f& c) override {
fWriter.beginArray(name, false);
fWriter.appendFloat(c.fR);
fWriter.appendFloat(c.fG);
fWriter.appendFloat(c.fB);
fWriter.appendFloat(c.fA);
fWriter.endArray();
}
void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
fWriter.appendString("Type", e ? e->getType()->fName : "Null");
}
void enterObject(const char* name) override { fWriter.beginObject(name); }
void exitObject() override { fWriter.endObject(); }
int enterArray(const char* name, int oldCount) override {
fWriter.beginArray(name);
return oldCount;
}
ArrayEdit exitArray() override {
fWriter.endArray();
return ArrayEdit();
}
private:
SkJSONWriter& fWriter;
};
class SkFromJsonVisitor : public SkFieldVisitor {
public:
SkFromJsonVisitor(const skjson::Value& v) : fRoot(v) {
fStack.push_back(&fRoot);
}
void visit(const char* name, float& f) override {
TryParse(get(name), f);
}
void visit(const char* name, int& i) override {
TryParse(get(name), i);
}
void visit(const char* name, bool& b) override {
TryParse(get(name), b);
}
void visit(const char* name, SkString& s) override {
if (const skjson::ArrayValue* lines = get(name)) {
s.reset();
bool first = true;
for (const skjson::StringValue* line : *lines) {
if (line) {
if (!first) {
s.append("\n");
}
s.append(line->begin(), line->size());
first = false;
}
}
} else {
TryParse(get(name), s);
}
}
void visit(const char* name, int& i, const EnumStringMapping* map, int count) override {
SkString str;
if (TryParse(get(name), str)) {
i = StringToEnum(str.c_str(), map, count);
}
}
void visit(const char* name, SkPoint& p) override {
if (const skjson::ObjectValue* obj = get(name)) {
TryParse((*obj)["x"], p.fX);
TryParse((*obj)["y"], p.fY);
}
}
void visit(const char* name, SkColor4f& c) override {
const skjson::ArrayValue* arr = get(name);
if (arr && arr->size() == 4) {
TryParse((*arr)[0], c.fR);
TryParse((*arr)[1], c.fG);
TryParse((*arr)[2], c.fB);
TryParse((*arr)[3], c.fA);
}
}
void visit(sk_sp<SkReflected>& e, const SkReflected::Type* baseType) override {
const skjson::StringValue* typeString = get("Type");
const char* type = typeString ? typeString->begin() : "Null";
e = SkReflected::CreateInstance(type);
}
void enterObject(const char* name) override {
fStack.push_back((const skjson::ObjectValue*)get(name));
}
void exitObject() override {
fStack.pop_back();
}
int enterArray(const char* name, int oldCount) override {
const skjson::ArrayValue* arrVal = get(name);
fStack.push_back(arrVal);
fArrayIndexStack.push_back(0);
return arrVal ? arrVal->size() : 0;
}
ArrayEdit exitArray() override {
fStack.pop_back();
fArrayIndexStack.pop_back();
return ArrayEdit();
}
private:
const skjson::Value& get(const char* name) {
if (const skjson::Value* cur = fStack.back()) {
if (cur->is<skjson::ArrayValue>()) {
SkASSERT(!name);
return cur->as<skjson::ArrayValue>()[fArrayIndexStack.back()++];
} else if (!name) {
return *cur;
} else if (cur->is<skjson::ObjectValue>()) {
return cur->as<skjson::ObjectValue>()[name];
}
}
static skjson::NullValue gNull;
return gNull;
}
static bool TryParse(const skjson::Value& v, float& f) {
if (const skjson::NumberValue* num = v) {
f = static_cast<float>(**num);
return true;
}
return false;
}
static bool TryParse(const skjson::Value& v, int& i) {
if (const skjson::NumberValue* num = v) {
double dbl = **num;
i = static_cast<int>(dbl);
return static_cast<double>(i) == dbl;
}
return false;
}
static bool TryParse(const skjson::Value& v, SkString& s) {
if (const skjson::StringValue* str = v) {
s.set(str->begin(), str->size());
return true;
}
return false;
}
static bool TryParse(const skjson::Value& v, bool& b) {
switch (v.getType()) {
case skjson::Value::Type::kNumber:
b = SkToBool(*v.as<skjson::NumberValue>());
return true;
case skjson::Value::Type::kBool:
b = *v.as<skjson::BoolValue>();
return true;
default:
break;
}
return false;
}
const skjson::Value& fRoot;
SkSTArray<16, const skjson::Value*, true> fStack;
SkSTArray<16, size_t, true> fArrayIndexStack;
};
#endif // SkParticleSerialization_DEFINED