|  | /* | 
|  | * 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()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Compound types | 
|  | 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(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 |