[skottie] Initial text layer plumbing
Still loads to do. For now just draws trivial text nodes using a default fontmgr. Change-Id: I7343b648726d2c4f376f43437f6ae1377ad8ba86 Reviewed-on: https://skia-review.googlesource.com/147465 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
e65dd586c1
commit
f9c5063fc4
@ -17,6 +17,8 @@ skia_skottie_sources = [
|
|||||||
"$_src/SkottieAnimator.h",
|
"$_src/SkottieAnimator.h",
|
||||||
"$_src/SkottieJson.cpp",
|
"$_src/SkottieJson.cpp",
|
||||||
"$_src/SkottieJson.h",
|
"$_src/SkottieJson.h",
|
||||||
|
"$_src/SkottiePriv.h",
|
||||||
|
"$_src/SkottieTextLayer.cpp",
|
||||||
"$_src/SkottieValue.cpp",
|
"$_src/SkottieValue.cpp",
|
||||||
"$_src/SkottieValue.h",
|
"$_src/SkottieValue.h",
|
||||||
]
|
]
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "SkCanvas.h"
|
#include "SkCanvas.h"
|
||||||
#include "SkData.h"
|
#include "SkData.h"
|
||||||
|
#include "SkFontMgr.h"
|
||||||
#include "SkImage.h"
|
#include "SkImage.h"
|
||||||
#include "SkMakeUnique.h"
|
#include "SkMakeUnique.h"
|
||||||
#include "SkOSPath.h"
|
#include "SkOSPath.h"
|
||||||
@ -42,6 +43,7 @@
|
|||||||
#include "SkottieAdapter.h"
|
#include "SkottieAdapter.h"
|
||||||
#include "SkottieAnimator.h"
|
#include "SkottieAnimator.h"
|
||||||
#include "SkottieJson.h"
|
#include "SkottieJson.h"
|
||||||
|
#include "SkottiePriv.h"
|
||||||
#include "SkottieValue.h"
|
#include "SkottieValue.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
@ -51,31 +53,20 @@
|
|||||||
|
|
||||||
namespace skottie {
|
namespace skottie {
|
||||||
|
|
||||||
#define LOG SkDebugf
|
using internal::AttachContext;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
void LogJSON(const skjson::Value& json, const char msg[]) {
|
||||||
|
const auto dump = json.toString();
|
||||||
|
LOG("%s: %s\n", msg, dump.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct AssetInfo {
|
// DEPRECATED: replace w/ LogJSON.
|
||||||
const skjson::ObjectValue* fAsset;
|
|
||||||
mutable bool fIsAttaching; // Used for cycle detection
|
|
||||||
};
|
|
||||||
|
|
||||||
using AssetMap = SkTHashMap<SkString, AssetInfo>;
|
|
||||||
using AssetCache = SkTHashMap<SkString, sk_sp<sksg::RenderNode>>;
|
|
||||||
|
|
||||||
struct AttachContext {
|
|
||||||
AttachContext makeScoped(sksg::AnimatorList& animators) const {
|
|
||||||
return { fResources, fAssets, fDuration, fFrameRate, fAssetCache, animators };
|
|
||||||
}
|
|
||||||
|
|
||||||
const ResourceProvider& fResources;
|
|
||||||
const AssetMap& fAssets;
|
|
||||||
const float fDuration,
|
|
||||||
fFrameRate;
|
|
||||||
AssetCache& fAssetCache;
|
|
||||||
sksg::AnimatorList& fAnimators;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool LogFail(const skjson::Value& json, const char* msg) {
|
bool LogFail(const skjson::Value& json, const char* msg) {
|
||||||
const auto dump = json.toString();
|
const auto dump = json.toString();
|
||||||
LOG("!! %s: %s\n", msg, dump.c_str());
|
LOG("!! %s: %s\n", msg, dump.c_str());
|
||||||
@ -926,11 +917,6 @@ sk_sp<sksg::RenderNode> AttachShapeLayer(const skjson::ObjectValue& layer, Attac
|
|||||||
return shapeNode;
|
return shapeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue& layer, AttachContext*) {
|
|
||||||
LOG("?? Text layer stub\n");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct AttachLayerContext {
|
struct AttachLayerContext {
|
||||||
AttachLayerContext(const skjson::ArrayValue& jlayers, AttachContext* ctx)
|
AttachLayerContext(const skjson::ArrayValue& jlayers, AttachContext* ctx)
|
||||||
: fLayerList(jlayers), fCtx(ctx) {}
|
: fLayerList(jlayers), fCtx(ctx) {}
|
||||||
@ -1164,6 +1150,7 @@ sk_sp<sksg::RenderNode> AttachLayer(const skjson::ObjectValue* jlayer,
|
|||||||
AttachLayerContext* layerCtx) {
|
AttachLayerContext* layerCtx) {
|
||||||
if (!jlayer) return nullptr;
|
if (!jlayer) return nullptr;
|
||||||
|
|
||||||
|
using internal::AttachTextLayer;
|
||||||
using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const skjson::ObjectValue&, AttachContext*);
|
using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const skjson::ObjectValue&, AttachContext*);
|
||||||
static constexpr LayerAttacher gLayerAttachers[] = {
|
static constexpr LayerAttacher gLayerAttachers[] = {
|
||||||
AttachCompLayer, // 'ty': 0
|
AttachCompLayer, // 'ty': 0
|
||||||
@ -1404,7 +1391,7 @@ Animation::Animation(const ResourceProvider& resources,
|
|||||||
, fInPoint(ParseDefault<float>(json["ip"], 0.0f))
|
, fInPoint(ParseDefault<float>(json["ip"], 0.0f))
|
||||||
, fOutPoint(SkTMax(ParseDefault<float>(json["op"], SK_ScalarMax), fInPoint)) {
|
, fOutPoint(SkTMax(ParseDefault<float>(json["op"], SK_ScalarMax), fInPoint)) {
|
||||||
|
|
||||||
AssetMap assets;
|
internal::AssetMap assets;
|
||||||
if (const skjson::ArrayValue* jassets = json["assets"]) {
|
if (const skjson::ArrayValue* jassets = json["assets"]) {
|
||||||
for (const skjson::ObjectValue* asset : *jassets) {
|
for (const skjson::ObjectValue* asset : *jassets) {
|
||||||
if (asset) {
|
if (asset) {
|
||||||
@ -1413,9 +1400,19 @@ Animation::Animation(const ResourceProvider& resources,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetCache asset_cache;
|
// TODO: plumb external font mgr.
|
||||||
|
const auto fontmgr = SkFontMgr::RefDefault();
|
||||||
|
const auto fonts = internal::ParseFonts(json["fonts"], json["chars"], fontmgr.get());
|
||||||
|
|
||||||
|
internal::AssetCache asset_cache;
|
||||||
sksg::AnimatorList animators;
|
sksg::AnimatorList animators;
|
||||||
AttachContext ctx = { resources, assets, this->duration(), fFrameRate, asset_cache, animators };
|
AttachContext ctx = { resources,
|
||||||
|
assets,
|
||||||
|
fonts,
|
||||||
|
this->duration(),
|
||||||
|
fFrameRate,
|
||||||
|
asset_cache,
|
||||||
|
animators };
|
||||||
auto root = AttachComposition(json, &ctx);
|
auto root = AttachComposition(json, &ctx);
|
||||||
|
|
||||||
stats->fAnimatorCount = animators.size();
|
stats->fAnimatorCount = animators.size();
|
||||||
|
80
modules/skottie/src/SkottiePriv.h
Normal file
80
modules/skottie/src/SkottiePriv.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SkottiePriv_DEFINED
|
||||||
|
#define SkottiePriv_DEFINED
|
||||||
|
|
||||||
|
#include "SkFontStyle.h"
|
||||||
|
#include "SkSGScene.h"
|
||||||
|
#include "SkString.h"
|
||||||
|
#include "SkTHash.h"
|
||||||
|
#include "SkTypeface.h"
|
||||||
|
#include "SkUTF.h"
|
||||||
|
|
||||||
|
#define LOG SkDebugf
|
||||||
|
|
||||||
|
class SkFontMgr;
|
||||||
|
|
||||||
|
namespace skjson {
|
||||||
|
class ArrayValue;
|
||||||
|
class ObjectValue;
|
||||||
|
class Value;
|
||||||
|
} // namespace skjson
|
||||||
|
|
||||||
|
namespace sksg {
|
||||||
|
class RenderNode;
|
||||||
|
} // namespace sksg
|
||||||
|
|
||||||
|
namespace skottie {
|
||||||
|
|
||||||
|
class ResourceProvider;
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
struct AssetInfo {
|
||||||
|
const skjson::ObjectValue* fAsset;
|
||||||
|
mutable bool fIsAttaching; // Used for cycle detection
|
||||||
|
};
|
||||||
|
using AssetMap = SkTHashMap<SkString, AssetInfo>;
|
||||||
|
using AssetCache = SkTHashMap<SkString, sk_sp<sksg::RenderNode>>;
|
||||||
|
|
||||||
|
struct FontInfo {
|
||||||
|
SkString fFamily,
|
||||||
|
fStyle;
|
||||||
|
SkScalar fAscent;
|
||||||
|
sk_sp<SkTypeface> fTypeface;
|
||||||
|
|
||||||
|
bool matches(const char family[], const char style[]) const;
|
||||||
|
};
|
||||||
|
using FontMap = SkTHashMap<SkString, FontInfo>;
|
||||||
|
|
||||||
|
struct AttachContext {
|
||||||
|
AttachContext makeScoped(sksg::AnimatorList& animators) const {
|
||||||
|
return { fResources, fAssets, fFonts, fDuration, fFrameRate, fAssetCache, animators };
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResourceProvider& fResources;
|
||||||
|
const AssetMap& fAssets;
|
||||||
|
const FontMap& fFonts;
|
||||||
|
const float fDuration,
|
||||||
|
fFrameRate;
|
||||||
|
AssetCache& fAssetCache;
|
||||||
|
sksg::AnimatorList& fAnimators;
|
||||||
|
};
|
||||||
|
|
||||||
|
void LogJSON(const skjson::Value&, const char[]);
|
||||||
|
|
||||||
|
FontMap ParseFonts(const skjson::ObjectValue* jfonts,
|
||||||
|
const skjson::ArrayValue* jchars,
|
||||||
|
const SkFontMgr*);
|
||||||
|
|
||||||
|
sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue&, AttachContext*);
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace skottie
|
||||||
|
|
||||||
|
#endif // SkottiePriv_DEFINED
|
346
modules/skottie/src/SkottieTextLayer.cpp
Normal file
346
modules/skottie/src/SkottieTextLayer.cpp
Normal file
@ -0,0 +1,346 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 Google Inc.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license that can be
|
||||||
|
* found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SkottiePriv.h"
|
||||||
|
|
||||||
|
#include "SkFontMgr.h"
|
||||||
|
#include "SkMakeUnique.h"
|
||||||
|
#include "SkottieJson.h"
|
||||||
|
#include "SkottieValue.h"
|
||||||
|
#include "SkSGColor.h"
|
||||||
|
#include "SkSGDraw.h"
|
||||||
|
#include "SkSGGroup.h"
|
||||||
|
#include "SkSGText.h"
|
||||||
|
#include "SkTypes.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace skottie {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool ParseGlyph(const skjson::ObjectValue* jglyph, FontInfo* finfo) {
|
||||||
|
// TODO: add glyphs support.
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SkFontStyle FontStyle(const char* style) {
|
||||||
|
static constexpr struct {
|
||||||
|
const char* fName;
|
||||||
|
const SkFontStyle::Weight fWeight;
|
||||||
|
} gWeightMap[] = {
|
||||||
|
{ "ExtraLight", SkFontStyle::kExtraLight_Weight },
|
||||||
|
{ "Light" , SkFontStyle::kLight_Weight },
|
||||||
|
{ "Regular" , SkFontStyle::kNormal_Weight },
|
||||||
|
{ "Medium" , SkFontStyle::kMedium_Weight },
|
||||||
|
{ "SemiBold" , SkFontStyle::kSemiBold_Weight },
|
||||||
|
{ "Bold" , SkFontStyle::kBold_Weight },
|
||||||
|
{ "ExtraBold" , SkFontStyle::kExtraBold_Weight },
|
||||||
|
};
|
||||||
|
|
||||||
|
SkFontStyle::Weight weight = SkFontStyle::kNormal_Weight;
|
||||||
|
for (const auto& w : gWeightMap) {
|
||||||
|
const auto name_len = strlen(w.fName);
|
||||||
|
if (!strncmp(style, w.fName, name_len)) {
|
||||||
|
weight = w.fWeight;
|
||||||
|
style += name_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr struct {
|
||||||
|
const char* fName;
|
||||||
|
const SkFontStyle::Slant fSlant;
|
||||||
|
} gSlantMap[] = {
|
||||||
|
{ "Italic" , SkFontStyle::kItalic_Slant },
|
||||||
|
{ "Oblique", SkFontStyle::kOblique_Slant },
|
||||||
|
};
|
||||||
|
|
||||||
|
SkFontStyle::Slant slant = SkFontStyle::kUpright_Slant;
|
||||||
|
if (*style != '\0') {
|
||||||
|
for (const auto& s : gSlantMap) {
|
||||||
|
if (!strcmp(style, s.fName)) {
|
||||||
|
slant = s.fSlant;
|
||||||
|
style += strlen(s.fName);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*style != '\0') {
|
||||||
|
LOG("?? Unknown font style: %s\n", style);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SkFontStyle(weight, SkFontStyle::kNormal_Width, slant);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool FontInfo::matches(const char family[], const char style[]) const {
|
||||||
|
return 0 == strcmp(fFamily.c_str(), family)
|
||||||
|
&& 0 == strcmp(fStyle.c_str(), style);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontMap ParseFonts(const skjson::ObjectValue* jfonts, const skjson::ArrayValue* jchars,
|
||||||
|
const SkFontMgr* fontmgr) {
|
||||||
|
FontMap fonts;
|
||||||
|
|
||||||
|
// Optional array of font entries, referenced (by name) from text layer document nodes. E.g.
|
||||||
|
// "fonts": {
|
||||||
|
// "list": [
|
||||||
|
// {
|
||||||
|
// "ascent": 75,
|
||||||
|
// "fClass": "",
|
||||||
|
// "fFamily": "Roboto",
|
||||||
|
// "fName": "Roboto-Regular",
|
||||||
|
// "fPath": "",
|
||||||
|
// "fStyle": "Regular",
|
||||||
|
// "fWeight": "",
|
||||||
|
// "origin": 1
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
if (jfonts) {
|
||||||
|
if (const skjson::ArrayValue* jlist = (*jfonts)["list"]) {
|
||||||
|
for (const skjson::ObjectValue* jfont : *jlist) {
|
||||||
|
if (!jfont) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skjson::StringValue* jname = (*jfont)["fName"];
|
||||||
|
const skjson::StringValue* jfamily = (*jfont)["fFamily"];
|
||||||
|
const skjson::StringValue* jstyle = (*jfont)["fStyle"];
|
||||||
|
|
||||||
|
if (!jname || !jname->size() ||
|
||||||
|
!jfamily || !jfamily->size() ||
|
||||||
|
!jstyle || !jstyle->size()) {
|
||||||
|
LogJSON(*jfont, "!! Ignoring invalid font");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<SkTypeface> tf(fontmgr->matchFamilyStyle(jfamily->begin(),
|
||||||
|
FontStyle(jstyle->begin())));
|
||||||
|
if (!tf) {
|
||||||
|
LOG("!! Could not create typeface for %s|%s\n",
|
||||||
|
jfamily->begin(), jstyle->begin());
|
||||||
|
// Last resort.
|
||||||
|
tf.reset(fontmgr->matchFamilyStyle("Arial", SkFontStyle::Normal()));
|
||||||
|
if (!tf) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fonts.set(SkString(jname->begin(), jname->size()),
|
||||||
|
{
|
||||||
|
SkString(jfamily->begin(), jfamily->size()),
|
||||||
|
SkString(jstyle->begin(), jstyle->size()),
|
||||||
|
ParseDefault((*jfont)["ascent"] , 0.0f),
|
||||||
|
std::move(tf)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optional array of glyphs, to be associated with one of the declared fonts. E.g.
|
||||||
|
// "chars": [
|
||||||
|
// {
|
||||||
|
// "ch": "t",
|
||||||
|
// "data": {
|
||||||
|
// "shapes": [...]
|
||||||
|
// },
|
||||||
|
// "fFamily": "Roboto",
|
||||||
|
// "size": 50,
|
||||||
|
// "style": "Regular",
|
||||||
|
// "w": 32.67
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
if (jchars) {
|
||||||
|
FontInfo* current_font = nullptr;
|
||||||
|
|
||||||
|
for (const skjson::ObjectValue* jchar : *jchars) {
|
||||||
|
if (!jchar) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skjson::StringValue* jch = (*jchar)["ch"];
|
||||||
|
if (!jch) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skjson::StringValue* jfamily = (*jchar)["fFamily"];
|
||||||
|
const skjson::StringValue* jstyle = (*jchar)["style"]; // "style", not "fStyle"...
|
||||||
|
|
||||||
|
const auto* ch_ptr = jch->begin();
|
||||||
|
const auto ch_len = jch->size();
|
||||||
|
|
||||||
|
if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) {
|
||||||
|
LogJSON(*jchar, "!! Invalid glyph");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len);
|
||||||
|
SkASSERT(uni != -1);
|
||||||
|
|
||||||
|
const auto* family = jfamily->begin();
|
||||||
|
const auto* style = jstyle->begin();
|
||||||
|
|
||||||
|
// Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by
|
||||||
|
// (family, style) -- not by name :( For now this performs a linear search over *all*
|
||||||
|
// fonts: generally there are few of them, and glyph definitions are font-clustered.
|
||||||
|
// If problematic, we can refactor as a two-level hashmap.
|
||||||
|
if (!current_font || !current_font->matches(family, style)) {
|
||||||
|
current_font = nullptr;
|
||||||
|
fonts.foreach([&](const SkString& name, FontInfo* finfo) {
|
||||||
|
if (finfo->matches(family, style)) {
|
||||||
|
current_font = finfo;
|
||||||
|
// TODO: would be nice to break early here...
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!current_font) {
|
||||||
|
LOG("!! Font not found for codepoint (%d, %s, %s)\n", uni, family, style);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ParseGlyph(*jchar, current_font)) {
|
||||||
|
LogJSON(*jchar, "!! Invalid glyph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fonts;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue& layer, AttachContext* ctx) {
|
||||||
|
// General text node format:
|
||||||
|
// "t": {
|
||||||
|
// "a": [], // animators (TODO)
|
||||||
|
// "d": {
|
||||||
|
// "k": [
|
||||||
|
// {
|
||||||
|
// "s": {
|
||||||
|
// "f": "Roboto-Regular",
|
||||||
|
// "fc": [
|
||||||
|
// 0.42,
|
||||||
|
// 0.15,
|
||||||
|
// 0.15
|
||||||
|
// ],
|
||||||
|
// "j": 1,
|
||||||
|
// "lh": 60,
|
||||||
|
// "ls": 0,
|
||||||
|
// "s": 50,
|
||||||
|
// "t": "text align right",
|
||||||
|
// "tr": 0
|
||||||
|
// },
|
||||||
|
// "t": 0
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// },
|
||||||
|
// "m": {}, // "more options" (TODO)
|
||||||
|
// "p": {} // "path options" (TODO)
|
||||||
|
// },
|
||||||
|
const skjson::ObjectValue* jt = layer["t"];
|
||||||
|
if (!jt) {
|
||||||
|
LogJSON(layer, "!! Missing text layer \"t\" property");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skjson::ArrayValue* animated_props = (*jt)["a"];
|
||||||
|
if (animated_props && animated_props->size() > 0) {
|
||||||
|
LOG("?? Unsupported animated text properties.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The "d" node is keyframed, not static. Add a new animated value type and parse as such.
|
||||||
|
const skjson::ObjectValue* jd = (*jt)["d"];
|
||||||
|
const skjson::ArrayValue* jk = jd
|
||||||
|
? (*jd)["k"].operator const skjson::ArrayValue*() : nullptr;
|
||||||
|
const skjson::ObjectValue* jv0 = jk && jk->size() == 1
|
||||||
|
? (*jk)[0].operator const skjson::ObjectValue*() : nullptr;
|
||||||
|
const skjson::ObjectValue* jprops = jv0
|
||||||
|
? (*jv0)["s"].operator const skjson::ObjectValue*() : nullptr;
|
||||||
|
|
||||||
|
if (!jprops) {
|
||||||
|
LogJSON(*jt, "!! Unexpected text property");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const skjson::StringValue* font_name = (*jprops)["f"];
|
||||||
|
const skjson::StringValue* text = (*jprops)["t"];
|
||||||
|
const skjson::NumberValue* text_size = (*jprops)["s"];
|
||||||
|
if (!font_name || !text || !text_size) {
|
||||||
|
LogJSON(*jprops, "!! Invalid text properties");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* font = ctx->fFonts.find(SkString(font_name->begin(), font_name->size()));
|
||||||
|
if (!font) {
|
||||||
|
LOG("!! Unknown font: \"%s\"\n", font_name->begin());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr SkPaint::Align gAlignMap[] = {
|
||||||
|
SkPaint::kLeft_Align, // 'j': 0
|
||||||
|
SkPaint::kRight_Align, // 'j': 1
|
||||||
|
SkPaint::kCenter_Align // 'j': 2
|
||||||
|
};
|
||||||
|
const auto align = gAlignMap[SkTMin<size_t>(ParseDefault<size_t>((*jprops)["j"], 0),
|
||||||
|
SK_ARRAY_COUNT(gAlignMap))];
|
||||||
|
|
||||||
|
// Emit a SG fragment with the following general format:
|
||||||
|
//
|
||||||
|
// [Group]
|
||||||
|
// [Draw]
|
||||||
|
// [FillPaint]
|
||||||
|
// [Text]
|
||||||
|
// [Draw]
|
||||||
|
// [StrokePaint]
|
||||||
|
// [Text]
|
||||||
|
//
|
||||||
|
auto text_node = sksg::Text::Make(font->fTypeface, SkString(text->begin(), text->size()));
|
||||||
|
text_node->setSize(**text_size);
|
||||||
|
text_node->setAlign(align);
|
||||||
|
|
||||||
|
const auto parse_color = [](const skjson::ArrayValue* jcolor) -> sk_sp<sksg::Color> {
|
||||||
|
VectorValue color_vec;
|
||||||
|
if (!jcolor || !Parse(*jcolor, &color_vec)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto paint = sksg::Color::Make(ValueTraits<VectorValue>::As<SkColor>(color_vec));
|
||||||
|
paint->setAntiAlias(true);
|
||||||
|
|
||||||
|
return paint;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto fill_paint = parse_color((*jprops)["fc"]),
|
||||||
|
stroke_paint = parse_color((*jprops)["sc"]);
|
||||||
|
auto fill_node = sksg::Draw::Make(text_node, fill_paint),
|
||||||
|
stroke_node = sksg::Draw::Make(text_node, stroke_paint);
|
||||||
|
|
||||||
|
if (!stroke_node) {
|
||||||
|
return std::move(fill_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
stroke_paint->setStyle(SkPaint::kStroke_Style);
|
||||||
|
stroke_paint->setStrokeWidth(ParseDefault((*jprops)["sw"], 0.0f));
|
||||||
|
|
||||||
|
if (!fill_node) {
|
||||||
|
return std::move(stroke_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill & stroke
|
||||||
|
auto group_node = sksg::Group::Make();
|
||||||
|
group_node->addChild(std::move(fill_node));
|
||||||
|
group_node->addChild(std::move(stroke_node));
|
||||||
|
|
||||||
|
return std::move(group_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace skottie
|
Loading…
Reference in New Issue
Block a user