1cb93a386Sopenharmony_ci/* 2cb93a386Sopenharmony_ci * Copyright 2018 Google Inc. 3cb93a386Sopenharmony_ci * 4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be 5cb93a386Sopenharmony_ci * found in the LICENSE file. 6cb93a386Sopenharmony_ci */ 7cb93a386Sopenharmony_ci 8cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottiePriv.h" 9cb93a386Sopenharmony_ci 10cb93a386Sopenharmony_ci#include "include/core/SkData.h" 11cb93a386Sopenharmony_ci#include "include/core/SkFontMgr.h" 12cb93a386Sopenharmony_ci#include "include/core/SkTypes.h" 13cb93a386Sopenharmony_ci#include "modules/skottie/src/SkottieJson.h" 14cb93a386Sopenharmony_ci#include "modules/skottie/src/text/TextAdapter.h" 15cb93a386Sopenharmony_ci#include "modules/skottie/src/text/TextAnimator.h" 16cb93a386Sopenharmony_ci#include "modules/skottie/src/text/TextValue.h" 17cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGDraw.h" 18cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGGroup.h" 19cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGPaint.h" 20cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGPath.h" 21cb93a386Sopenharmony_ci#include "modules/sksg/include/SkSGText.h" 22cb93a386Sopenharmony_ci#include "src/core/SkTSearch.h" 23cb93a386Sopenharmony_ci 24cb93a386Sopenharmony_ci#include <string.h> 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_cinamespace skottie { 27cb93a386Sopenharmony_cinamespace internal { 28cb93a386Sopenharmony_ci 29cb93a386Sopenharmony_cinamespace { 30cb93a386Sopenharmony_ci 31cb93a386Sopenharmony_citemplate <typename T, typename TMap> 32cb93a386Sopenharmony_ciconst char* parse_map(const TMap& map, const char* str, T* result) { 33cb93a386Sopenharmony_ci // ignore leading whitespace 34cb93a386Sopenharmony_ci while (*str == ' ') ++str; 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci const char* next_tok = strchr(str, ' '); 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci if (const auto len = next_tok ? (next_tok - str) : strlen(str)) { 39cb93a386Sopenharmony_ci for (const auto& e : map) { 40cb93a386Sopenharmony_ci const char* key = std::get<0>(e); 41cb93a386Sopenharmony_ci if (!strncmp(str, key, len) && key[len] == '\0') { 42cb93a386Sopenharmony_ci *result = std::get<1>(e); 43cb93a386Sopenharmony_ci return str + len; 44cb93a386Sopenharmony_ci } 45cb93a386Sopenharmony_ci } 46cb93a386Sopenharmony_ci } 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_ci return str; 49cb93a386Sopenharmony_ci} 50cb93a386Sopenharmony_ci 51cb93a386Sopenharmony_ciSkFontStyle FontStyle(const AnimationBuilder* abuilder, const char* style) { 52cb93a386Sopenharmony_ci static constexpr std::tuple<const char*, SkFontStyle::Weight> gWeightMap[] = { 53cb93a386Sopenharmony_ci { "regular" , SkFontStyle::kNormal_Weight }, 54cb93a386Sopenharmony_ci { "medium" , SkFontStyle::kMedium_Weight }, 55cb93a386Sopenharmony_ci { "bold" , SkFontStyle::kBold_Weight }, 56cb93a386Sopenharmony_ci { "light" , SkFontStyle::kLight_Weight }, 57cb93a386Sopenharmony_ci { "black" , SkFontStyle::kBlack_Weight }, 58cb93a386Sopenharmony_ci { "thin" , SkFontStyle::kThin_Weight }, 59cb93a386Sopenharmony_ci { "extra" , SkFontStyle::kExtraBold_Weight }, 60cb93a386Sopenharmony_ci { "extrabold" , SkFontStyle::kExtraBold_Weight }, 61cb93a386Sopenharmony_ci { "extralight", SkFontStyle::kExtraLight_Weight }, 62cb93a386Sopenharmony_ci { "extrablack", SkFontStyle::kExtraBlack_Weight }, 63cb93a386Sopenharmony_ci { "semibold" , SkFontStyle::kSemiBold_Weight }, 64cb93a386Sopenharmony_ci { "hairline" , SkFontStyle::kThin_Weight }, 65cb93a386Sopenharmony_ci { "normal" , SkFontStyle::kNormal_Weight }, 66cb93a386Sopenharmony_ci { "plain" , SkFontStyle::kNormal_Weight }, 67cb93a386Sopenharmony_ci { "standard" , SkFontStyle::kNormal_Weight }, 68cb93a386Sopenharmony_ci { "roman" , SkFontStyle::kNormal_Weight }, 69cb93a386Sopenharmony_ci { "heavy" , SkFontStyle::kBlack_Weight }, 70cb93a386Sopenharmony_ci { "demi" , SkFontStyle::kSemiBold_Weight }, 71cb93a386Sopenharmony_ci { "demibold" , SkFontStyle::kSemiBold_Weight }, 72cb93a386Sopenharmony_ci { "ultra" , SkFontStyle::kExtraBold_Weight }, 73cb93a386Sopenharmony_ci { "ultrabold" , SkFontStyle::kExtraBold_Weight }, 74cb93a386Sopenharmony_ci { "ultrablack", SkFontStyle::kExtraBlack_Weight }, 75cb93a386Sopenharmony_ci { "ultraheavy", SkFontStyle::kExtraBlack_Weight }, 76cb93a386Sopenharmony_ci { "ultralight", SkFontStyle::kExtraLight_Weight }, 77cb93a386Sopenharmony_ci }; 78cb93a386Sopenharmony_ci static constexpr std::tuple<const char*, SkFontStyle::Slant> gSlantMap[] = { 79cb93a386Sopenharmony_ci { "italic" , SkFontStyle::kItalic_Slant }, 80cb93a386Sopenharmony_ci { "oblique", SkFontStyle::kOblique_Slant }, 81cb93a386Sopenharmony_ci }; 82cb93a386Sopenharmony_ci 83cb93a386Sopenharmony_ci auto weight = SkFontStyle::kNormal_Weight; 84cb93a386Sopenharmony_ci auto slant = SkFontStyle::kUpright_Slant; 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci // style is case insensitive. 87cb93a386Sopenharmony_ci SkAutoAsciiToLC lc_style(style); 88cb93a386Sopenharmony_ci style = lc_style.lc(); 89cb93a386Sopenharmony_ci style = parse_map(gWeightMap, style, &weight); 90cb93a386Sopenharmony_ci style = parse_map(gSlantMap , style, &slant ); 91cb93a386Sopenharmony_ci 92cb93a386Sopenharmony_ci // ignore trailing whitespace 93cb93a386Sopenharmony_ci while (*style == ' ') ++style; 94cb93a386Sopenharmony_ci 95cb93a386Sopenharmony_ci if (*style) { 96cb93a386Sopenharmony_ci abuilder->log(Logger::Level::kWarning, nullptr, "Unknown font style: %s.", style); 97cb93a386Sopenharmony_ci } 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_ci return SkFontStyle(weight, SkFontStyle::kNormal_Width, slant); 100cb93a386Sopenharmony_ci} 101cb93a386Sopenharmony_ci 102cb93a386Sopenharmony_cibool parse_glyph_path(const skjson::ObjectValue* jdata, 103cb93a386Sopenharmony_ci const AnimationBuilder* abuilder, 104cb93a386Sopenharmony_ci SkPath* path) { 105cb93a386Sopenharmony_ci // Glyph path encoding: 106cb93a386Sopenharmony_ci // 107cb93a386Sopenharmony_ci // "data": { 108cb93a386Sopenharmony_ci // "shapes": [ // follows the shape layer format 109cb93a386Sopenharmony_ci // { 110cb93a386Sopenharmony_ci // "ty": "gr", // group shape type 111cb93a386Sopenharmony_ci // "it": [ // group items 112cb93a386Sopenharmony_ci // { 113cb93a386Sopenharmony_ci // "ty": "sh", // actual shape 114cb93a386Sopenharmony_ci // "ks": <path data> // animatable path format, but always static 115cb93a386Sopenharmony_ci // }, 116cb93a386Sopenharmony_ci // ... 117cb93a386Sopenharmony_ci // ] 118cb93a386Sopenharmony_ci // }, 119cb93a386Sopenharmony_ci // ... 120cb93a386Sopenharmony_ci // ] 121cb93a386Sopenharmony_ci // } 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci if (!jdata) { 124cb93a386Sopenharmony_ci return false; 125cb93a386Sopenharmony_ci } 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci const skjson::ArrayValue* jshapes = (*jdata)["shapes"]; 128cb93a386Sopenharmony_ci if (!jshapes) { 129cb93a386Sopenharmony_ci // Space/empty glyph. 130cb93a386Sopenharmony_ci return true; 131cb93a386Sopenharmony_ci } 132cb93a386Sopenharmony_ci 133cb93a386Sopenharmony_ci for (const skjson::ObjectValue* jgrp : *jshapes) { 134cb93a386Sopenharmony_ci if (!jgrp) { 135cb93a386Sopenharmony_ci return false; 136cb93a386Sopenharmony_ci } 137cb93a386Sopenharmony_ci 138cb93a386Sopenharmony_ci const skjson::ArrayValue* jit = (*jgrp)["it"]; 139cb93a386Sopenharmony_ci if (!jit) { 140cb93a386Sopenharmony_ci return false; 141cb93a386Sopenharmony_ci } 142cb93a386Sopenharmony_ci 143cb93a386Sopenharmony_ci for (const skjson::ObjectValue* jshape : *jit) { 144cb93a386Sopenharmony_ci if (!jshape) { 145cb93a386Sopenharmony_ci return false; 146cb93a386Sopenharmony_ci } 147cb93a386Sopenharmony_ci 148cb93a386Sopenharmony_ci // Glyph paths should never be animated. But they are encoded as 149cb93a386Sopenharmony_ci // animatable properties, so we use the appropriate helpers. 150cb93a386Sopenharmony_ci AnimationBuilder::AutoScope ascope(abuilder); 151cb93a386Sopenharmony_ci auto path_node = abuilder->attachPath((*jshape)["ks"]); 152cb93a386Sopenharmony_ci auto animators = ascope.release(); 153cb93a386Sopenharmony_ci 154cb93a386Sopenharmony_ci if (!path_node || !animators.empty()) { 155cb93a386Sopenharmony_ci return false; 156cb93a386Sopenharmony_ci } 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci // Successfully parsed a static path. Whew. 159cb93a386Sopenharmony_ci path->addPath(path_node->getPath()); 160cb93a386Sopenharmony_ci } 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci 163cb93a386Sopenharmony_ci return true; 164cb93a386Sopenharmony_ci} 165cb93a386Sopenharmony_ci 166cb93a386Sopenharmony_ci} // namespace 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_cibool AnimationBuilder::FontInfo::matches(const char family[], const char style[]) const { 169cb93a386Sopenharmony_ci return 0 == strcmp(fFamily.c_str(), family) 170cb93a386Sopenharmony_ci && 0 == strcmp(fStyle.c_str(), style); 171cb93a386Sopenharmony_ci} 172cb93a386Sopenharmony_ci 173cb93a386Sopenharmony_ci#ifdef SK_NO_FONTS 174cb93a386Sopenharmony_civoid AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts, 175cb93a386Sopenharmony_ci const skjson::ArrayValue* jchars) {} 176cb93a386Sopenharmony_ci 177cb93a386Sopenharmony_cisk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& jlayer, 178cb93a386Sopenharmony_ci LayerInfo*) const { 179cb93a386Sopenharmony_ci return nullptr; 180cb93a386Sopenharmony_ci} 181cb93a386Sopenharmony_ci#else 182cb93a386Sopenharmony_civoid AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts, 183cb93a386Sopenharmony_ci const skjson::ArrayValue* jchars) { 184cb93a386Sopenharmony_ci // Optional array of font entries, referenced (by name) from text layer document nodes. E.g. 185cb93a386Sopenharmony_ci // "fonts": { 186cb93a386Sopenharmony_ci // "list": [ 187cb93a386Sopenharmony_ci // { 188cb93a386Sopenharmony_ci // "ascent": 75, 189cb93a386Sopenharmony_ci // "fClass": "", 190cb93a386Sopenharmony_ci // "fFamily": "Roboto", 191cb93a386Sopenharmony_ci // "fName": "Roboto-Regular", 192cb93a386Sopenharmony_ci // "fPath": "https://fonts.googleapis.com/css?family=Roboto", 193cb93a386Sopenharmony_ci // "fPath": "", 194cb93a386Sopenharmony_ci // "fStyle": "Regular", 195cb93a386Sopenharmony_ci // "fWeight": "", 196cb93a386Sopenharmony_ci // "origin": 1 197cb93a386Sopenharmony_ci // } 198cb93a386Sopenharmony_ci // ] 199cb93a386Sopenharmony_ci // }, 200cb93a386Sopenharmony_ci const skjson::ArrayValue* jlist = jfonts 201cb93a386Sopenharmony_ci ? static_cast<const skjson::ArrayValue*>((*jfonts)["list"]) 202cb93a386Sopenharmony_ci : nullptr; 203cb93a386Sopenharmony_ci if (!jlist) { 204cb93a386Sopenharmony_ci return; 205cb93a386Sopenharmony_ci } 206cb93a386Sopenharmony_ci 207cb93a386Sopenharmony_ci // First pass: collect font info. 208cb93a386Sopenharmony_ci for (const skjson::ObjectValue* jfont : *jlist) { 209cb93a386Sopenharmony_ci if (!jfont) { 210cb93a386Sopenharmony_ci continue; 211cb93a386Sopenharmony_ci } 212cb93a386Sopenharmony_ci 213cb93a386Sopenharmony_ci const skjson::StringValue* jname = (*jfont)["fName"]; 214cb93a386Sopenharmony_ci const skjson::StringValue* jfamily = (*jfont)["fFamily"]; 215cb93a386Sopenharmony_ci const skjson::StringValue* jstyle = (*jfont)["fStyle"]; 216cb93a386Sopenharmony_ci const skjson::StringValue* jpath = (*jfont)["fPath"]; 217cb93a386Sopenharmony_ci 218cb93a386Sopenharmony_ci if (!jname || !jname->size() || 219cb93a386Sopenharmony_ci !jfamily || !jfamily->size() || 220cb93a386Sopenharmony_ci !jstyle) { 221cb93a386Sopenharmony_ci this->log(Logger::Level::kError, jfont, "Invalid font."); 222cb93a386Sopenharmony_ci continue; 223cb93a386Sopenharmony_ci } 224cb93a386Sopenharmony_ci 225cb93a386Sopenharmony_ci fFonts.set(SkString(jname->begin(), jname->size()), 226cb93a386Sopenharmony_ci { 227cb93a386Sopenharmony_ci SkString(jfamily->begin(), jfamily->size()), 228cb93a386Sopenharmony_ci SkString( jstyle->begin(), jstyle->size()), 229cb93a386Sopenharmony_ci jpath ? SkString( jpath->begin(), jpath->size()) : SkString(), 230cb93a386Sopenharmony_ci ParseDefault((*jfont)["ascent"] , 0.0f), 231cb93a386Sopenharmony_ci nullptr, // placeholder 232cb93a386Sopenharmony_ci SkCustomTypefaceBuilder() 233cb93a386Sopenharmony_ci }); 234cb93a386Sopenharmony_ci } 235cb93a386Sopenharmony_ci 236cb93a386Sopenharmony_ci // Optional pass. 237cb93a386Sopenharmony_ci if (jchars && (fFlags & Animation::Builder::kPreferEmbeddedFonts) && 238cb93a386Sopenharmony_ci this->resolveEmbeddedTypefaces(*jchars)) { 239cb93a386Sopenharmony_ci return; 240cb93a386Sopenharmony_ci } 241cb93a386Sopenharmony_ci 242cb93a386Sopenharmony_ci // Native typeface resolution. 243cb93a386Sopenharmony_ci if (this->resolveNativeTypefaces()) { 244cb93a386Sopenharmony_ci return; 245cb93a386Sopenharmony_ci } 246cb93a386Sopenharmony_ci 247cb93a386Sopenharmony_ci // Embedded typeface fallback. 248cb93a386Sopenharmony_ci if (jchars && !(fFlags & Animation::Builder::kPreferEmbeddedFonts) && 249cb93a386Sopenharmony_ci this->resolveEmbeddedTypefaces(*jchars)) { 250cb93a386Sopenharmony_ci } 251cb93a386Sopenharmony_ci} 252cb93a386Sopenharmony_ci 253cb93a386Sopenharmony_cibool AnimationBuilder::resolveNativeTypefaces() { 254cb93a386Sopenharmony_ci bool has_unresolved = false; 255cb93a386Sopenharmony_ci 256cb93a386Sopenharmony_ci fFonts.foreach([&](const SkString& name, FontInfo* finfo) { 257cb93a386Sopenharmony_ci SkASSERT(finfo); 258cb93a386Sopenharmony_ci 259cb93a386Sopenharmony_ci if (finfo->fTypeface) { 260cb93a386Sopenharmony_ci // Already resolved from glyph paths. 261cb93a386Sopenharmony_ci return; 262cb93a386Sopenharmony_ci } 263cb93a386Sopenharmony_ci 264cb93a386Sopenharmony_ci const auto& fmgr = fLazyFontMgr.get(); 265cb93a386Sopenharmony_ci 266cb93a386Sopenharmony_ci // Typeface fallback order: 267cb93a386Sopenharmony_ci // 1) externally-loaded font (provided by the embedder) 268cb93a386Sopenharmony_ci // 2) system font (family/style) 269cb93a386Sopenharmony_ci // 3) system default 270cb93a386Sopenharmony_ci 271cb93a386Sopenharmony_ci finfo->fTypeface = fResourceProvider->loadTypeface(name.c_str(), finfo->fPath.c_str()); 272cb93a386Sopenharmony_ci 273cb93a386Sopenharmony_ci // legacy API fallback 274cb93a386Sopenharmony_ci // TODO: remove after client migration 275cb93a386Sopenharmony_ci if (!finfo->fTypeface) { 276cb93a386Sopenharmony_ci finfo->fTypeface = fmgr->makeFromData( 277cb93a386Sopenharmony_ci fResourceProvider->loadFont(name.c_str(), finfo->fPath.c_str())); 278cb93a386Sopenharmony_ci } 279cb93a386Sopenharmony_ci 280cb93a386Sopenharmony_ci if (!finfo->fTypeface) { 281cb93a386Sopenharmony_ci finfo->fTypeface.reset(fmgr->matchFamilyStyle(finfo->fFamily.c_str(), 282cb93a386Sopenharmony_ci FontStyle(this, finfo->fStyle.c_str()))); 283cb93a386Sopenharmony_ci 284cb93a386Sopenharmony_ci if (!finfo->fTypeface) { 285cb93a386Sopenharmony_ci this->log(Logger::Level::kError, nullptr, "Could not create typeface for %s|%s.", 286cb93a386Sopenharmony_ci finfo->fFamily.c_str(), finfo->fStyle.c_str()); 287cb93a386Sopenharmony_ci // Last resort. 288cb93a386Sopenharmony_ci finfo->fTypeface = fmgr->legacyMakeTypeface(nullptr, 289cb93a386Sopenharmony_ci FontStyle(this, finfo->fStyle.c_str())); 290cb93a386Sopenharmony_ci 291cb93a386Sopenharmony_ci has_unresolved |= !finfo->fTypeface; 292cb93a386Sopenharmony_ci } 293cb93a386Sopenharmony_ci } 294cb93a386Sopenharmony_ci }); 295cb93a386Sopenharmony_ci 296cb93a386Sopenharmony_ci return !has_unresolved; 297cb93a386Sopenharmony_ci} 298cb93a386Sopenharmony_ci 299cb93a386Sopenharmony_cibool AnimationBuilder::resolveEmbeddedTypefaces(const skjson::ArrayValue& jchars) { 300cb93a386Sopenharmony_ci // Optional array of glyphs, to be associated with one of the declared fonts. E.g. 301cb93a386Sopenharmony_ci // "chars": [ 302cb93a386Sopenharmony_ci // { 303cb93a386Sopenharmony_ci // "ch": "t", 304cb93a386Sopenharmony_ci // "data": { 305cb93a386Sopenharmony_ci // "shapes": [...] // shape-layer-like geometry 306cb93a386Sopenharmony_ci // }, 307cb93a386Sopenharmony_ci // "fFamily": "Roboto", // part of the font key 308cb93a386Sopenharmony_ci // "size": 50, // apparently ignored 309cb93a386Sopenharmony_ci // "style": "Regular", // part of the font key 310cb93a386Sopenharmony_ci // "w": 32.67 // width/advance (1/100 units) 311cb93a386Sopenharmony_ci // } 312cb93a386Sopenharmony_ci // ] 313cb93a386Sopenharmony_ci FontInfo* current_font = nullptr; 314cb93a386Sopenharmony_ci 315cb93a386Sopenharmony_ci for (const skjson::ObjectValue* jchar : jchars) { 316cb93a386Sopenharmony_ci if (!jchar) { 317cb93a386Sopenharmony_ci continue; 318cb93a386Sopenharmony_ci } 319cb93a386Sopenharmony_ci 320cb93a386Sopenharmony_ci const skjson::StringValue* jch = (*jchar)["ch"]; 321cb93a386Sopenharmony_ci if (!jch) { 322cb93a386Sopenharmony_ci continue; 323cb93a386Sopenharmony_ci } 324cb93a386Sopenharmony_ci 325cb93a386Sopenharmony_ci const skjson::StringValue* jfamily = (*jchar)["fFamily"]; 326cb93a386Sopenharmony_ci const skjson::StringValue* jstyle = (*jchar)["style"]; // "style", not "fStyle"... 327cb93a386Sopenharmony_ci 328cb93a386Sopenharmony_ci const auto* ch_ptr = jch->begin(); 329cb93a386Sopenharmony_ci const auto ch_len = jch->size(); 330cb93a386Sopenharmony_ci 331cb93a386Sopenharmony_ci if (!jfamily || !jstyle || (SkUTF::CountUTF8(ch_ptr, ch_len) != 1)) { 332cb93a386Sopenharmony_ci this->log(Logger::Level::kError, jchar, "Invalid glyph."); 333cb93a386Sopenharmony_ci continue; 334cb93a386Sopenharmony_ci } 335cb93a386Sopenharmony_ci 336cb93a386Sopenharmony_ci const auto uni = SkUTF::NextUTF8(&ch_ptr, ch_ptr + ch_len); 337cb93a386Sopenharmony_ci SkASSERT(uni != -1); 338cb93a386Sopenharmony_ci if (!SkTFitsIn<SkGlyphID>(uni)) { 339cb93a386Sopenharmony_ci // Custom font keys are SkGlyphIDs. We could implement a remapping scheme if needed, 340cb93a386Sopenharmony_ci // but for now direct mapping seems to work well enough. 341cb93a386Sopenharmony_ci this->log(Logger::Level::kError, jchar, "Unsupported glyph ID."); 342cb93a386Sopenharmony_ci continue; 343cb93a386Sopenharmony_ci } 344cb93a386Sopenharmony_ci const auto glyph_id = SkTo<SkGlyphID>(uni); 345cb93a386Sopenharmony_ci 346cb93a386Sopenharmony_ci const auto* family = jfamily->begin(); 347cb93a386Sopenharmony_ci const auto* style = jstyle->begin(); 348cb93a386Sopenharmony_ci 349cb93a386Sopenharmony_ci // Locate (and cache) the font info. Unlike text nodes, glyphs reference the font by 350cb93a386Sopenharmony_ci // (family, style) -- not by name :( For now this performs a linear search over *all* 351cb93a386Sopenharmony_ci // fonts: generally there are few of them, and glyph definitions are font-clustered. 352cb93a386Sopenharmony_ci // If problematic, we can refactor as a two-level hashmap. 353cb93a386Sopenharmony_ci if (!current_font || !current_font->matches(family, style)) { 354cb93a386Sopenharmony_ci current_font = nullptr; 355cb93a386Sopenharmony_ci fFonts.foreach([&](const SkString& name, FontInfo* finfo) { 356cb93a386Sopenharmony_ci if (finfo->matches(family, style)) { 357cb93a386Sopenharmony_ci current_font = finfo; 358cb93a386Sopenharmony_ci // TODO: would be nice to break early here... 359cb93a386Sopenharmony_ci } 360cb93a386Sopenharmony_ci }); 361cb93a386Sopenharmony_ci if (!current_font) { 362cb93a386Sopenharmony_ci this->log(Logger::Level::kError, nullptr, 363cb93a386Sopenharmony_ci "Font not found for codepoint (%d, %s, %s).", uni, family, style); 364cb93a386Sopenharmony_ci continue; 365cb93a386Sopenharmony_ci } 366cb93a386Sopenharmony_ci } 367cb93a386Sopenharmony_ci 368cb93a386Sopenharmony_ci SkPath path; 369cb93a386Sopenharmony_ci if (!parse_glyph_path((*jchar)["data"], this, &path)) { 370cb93a386Sopenharmony_ci continue; 371cb93a386Sopenharmony_ci } 372cb93a386Sopenharmony_ci 373cb93a386Sopenharmony_ci const auto advance = ParseDefault((*jchar)["w"], 0.0f); 374cb93a386Sopenharmony_ci 375cb93a386Sopenharmony_ci // Interestingly, glyph paths are defined in a percentage-based space, 376cb93a386Sopenharmony_ci // regardless of declared glyph size... 377cb93a386Sopenharmony_ci static constexpr float kPtScale = 0.01f; 378cb93a386Sopenharmony_ci 379cb93a386Sopenharmony_ci // Normalize the path and advance for 1pt. 380cb93a386Sopenharmony_ci path.transform(SkMatrix::Scale(kPtScale, kPtScale)); 381cb93a386Sopenharmony_ci 382cb93a386Sopenharmony_ci current_font->fCustomBuilder.setGlyph(glyph_id, advance * kPtScale, path); 383cb93a386Sopenharmony_ci } 384cb93a386Sopenharmony_ci 385cb93a386Sopenharmony_ci // Final pass to commit custom typefaces. 386cb93a386Sopenharmony_ci auto has_unresolved = false; 387cb93a386Sopenharmony_ci fFonts.foreach([&has_unresolved](const SkString&, FontInfo* finfo) { 388cb93a386Sopenharmony_ci if (finfo->fTypeface) { 389cb93a386Sopenharmony_ci return; // already resolved 390cb93a386Sopenharmony_ci } 391cb93a386Sopenharmony_ci 392cb93a386Sopenharmony_ci finfo->fTypeface = finfo->fCustomBuilder.detach(); 393cb93a386Sopenharmony_ci 394cb93a386Sopenharmony_ci has_unresolved |= !finfo->fTypeface; 395cb93a386Sopenharmony_ci }); 396cb93a386Sopenharmony_ci 397cb93a386Sopenharmony_ci return !has_unresolved; 398cb93a386Sopenharmony_ci} 399cb93a386Sopenharmony_ci 400cb93a386Sopenharmony_cisk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& jlayer, 401cb93a386Sopenharmony_ci LayerInfo*) const { 402cb93a386Sopenharmony_ci return this->attachDiscardableAdapter<TextAdapter>(jlayer, 403cb93a386Sopenharmony_ci this, 404cb93a386Sopenharmony_ci fLazyFontMgr.getMaybeNull(), 405cb93a386Sopenharmony_ci fLogger); 406cb93a386Sopenharmony_ci} 407cb93a386Sopenharmony_ci#endif 408cb93a386Sopenharmony_ci 409cb93a386Sopenharmony_ciconst AnimationBuilder::FontInfo* AnimationBuilder::findFont(const SkString& font_name) const { 410cb93a386Sopenharmony_ci return fFonts.find(font_name); 411cb93a386Sopenharmony_ci} 412cb93a386Sopenharmony_ci 413cb93a386Sopenharmony_ci} // namespace internal 414cb93a386Sopenharmony_ci} // namespace skottie 415