From 58cb491df763b9d56dac430b53e9ad7babed962b Mon Sep 17 00:00:00 2001 From: Luigi Rosso Date: Wed, 2 Nov 2022 14:50:09 -0700 Subject: [PATCH] Fallback fonts for wasm --- lib/src/rive_text_wasm.dart | 21 +++++++++++++++-- macos/rive_text/rive_text.cpp | 2 -- premake5_rive_plugin.lua | 2 +- wasm/js/rive_text.js | 14 +++++------ wasm/rive_text_bindings.cpp | 44 ++++++++++++++++++++++++++++++++++- 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/lib/src/rive_text_wasm.dart b/lib/src/rive_text_wasm.dart index bbf9e74..7a6830b 100644 --- a/lib/src/rive_text_wasm.dart +++ b/lib/src/rive_text_wasm.dart @@ -15,6 +15,7 @@ late js.JsFunction _deleteFont; late js.JsFunction _makeGlyphPath; late js.JsFunction _deleteGlyphPath; late js.JsFunction _shapeText; +late js.JsFunction _setFallbackFonts; late js.JsFunction _deleteShapeResult; late js.JsFunction _breakLines; late js.JsFunction _deleteLines; @@ -505,18 +506,21 @@ Future initFont() async { html.document.body!.append(script); await script.onLoad.first; - var init = js.context['RiveText'] as js.JsFunction; - var promise = init.apply([]) as js.JsObject; + var initWasm = js.context['RiveText'] as js.JsFunction; + var promise = initWasm.apply([]) as js.JsObject; var thenFunction = promise['then'] as js.JsFunction; var completer = Completer(); thenFunction.apply( [ (js.JsObject module) { + var init = module['init'] as js.JsFunction; + init.apply([]); _makeFont = module['makeFont'] as js.JsFunction; _deleteFont = module['deleteFont'] as js.JsFunction; _makeGlyphPath = module['makeGlyphPath'] as js.JsFunction; _deleteGlyphPath = module['deleteGlyphPath'] as js.JsFunction; _shapeText = module['shapeText'] as js.JsFunction; + _setFallbackFonts = module['setFallbackFonts'] as js.JsFunction; _deleteShapeResult = module['deleteShapeResult'] as js.JsFunction; _breakLines = module['breakLines'] as js.JsFunction; _deleteLines = module['deleteLines'] as js.JsFunction; @@ -527,3 +531,16 @@ Future initFont() async { ); return completer.future; } + +void setFallbackFonts(List fonts) { + _setFallbackFonts.apply( + [ + Uint32List.fromList( + fonts + .cast() + .map((font) => font.fontPtr) + .toList(growable: false), + ), + ], + ); +} diff --git a/macos/rive_text/rive_text.cpp b/macos/rive_text/rive_text.cpp index 9bc46b0..5bae090 100644 --- a/macos/rive_text/rive_text.cpp +++ b/macos/rive_text/rive_text.cpp @@ -118,8 +118,6 @@ static rive::rcp pickFallbackFont(rive::Span mi HBFont* font = static_cast(fallbackFonts[i]); if (i == length - 1 || font->hasGlyph(missing)) { - fprintf(stderr, "FONT COUNT IS: %d\n", font->debugging_refcnt()); - rive::rcp rcFont = rive::rcp(font); // because the font was released at load time, we need to give it an // extra ref whenever we bump it to a reference counted pointer. diff --git a/premake5_rive_plugin.lua b/premake5_rive_plugin.lua index 22efde4..ca4bb8f 100644 --- a/premake5_rive_plugin.lua +++ b/premake5_rive_plugin.lua @@ -219,7 +219,7 @@ do '-DANSI_DECLARATORS' } - filter {'system:macosx'} + filter {'not options:arch=wasm', 'system:macosx'} do files { 'macos/rive_text/rive_text.cpp' diff --git a/wasm/js/rive_text.js b/wasm/js/rive_text.js index 36c0be7..ac1b5c8 100644 --- a/wasm/js/rive_text.js +++ b/wasm/js/rive_text.js @@ -1,7 +1,4 @@ RiveText["onRuntimeInitialized"] = function () { - var HEAPU8 = RiveText["HEAPU8"]; - var HEAPU32 = RiveText["HEAPU32"]; - var HEAPF32 = RiveText["HEAPF32"]; var nativeMakeGlyphPath = RiveText["makeGlyphPath"]; var move = 0; var line = 1; @@ -13,7 +10,7 @@ RiveText["onRuntimeInitialized"] = function () { var verbCount = glyph[3]; var ptsPtr = glyph[1]; var verbPtr = glyph[2]; - var verbs = HEAPU8["subarray"](verbPtr, verbPtr + verbCount); + var verbs = RiveText["HEAPU8"]["subarray"](verbPtr, verbPtr + verbCount); let pointCount = 0; for (var verb of verbs) { @@ -37,7 +34,10 @@ RiveText["onRuntimeInitialized"] = function () { return { "rawPath": glyph[0], "verbs": verbs, - "points": HEAPF32["subarray"](ptsStart, ptsStart + pointCount * 2), + "points": RiveText["HEAPF32"]["subarray"]( + ptsStart, + ptsStart + pointCount * 2 + ), }; }; @@ -46,7 +46,7 @@ RiveText["onRuntimeInitialized"] = function () { var shapeResult = nativeShapeText(codeUnits, runsList); return { "rawResult": shapeResult, - "results": HEAPU8["subarray"](shapeResult), + "results": RiveText["HEAPU8"]["subarray"](shapeResult), }; }; @@ -55,7 +55,7 @@ RiveText["onRuntimeInitialized"] = function () { var breakResult = nativeBreakLines(shape, width, align); return { "rawResult": breakResult, - "results": HEAPU8["subarray"](breakResult), + "results": RiveText["HEAPU8"]["subarray"](breakResult), }; }; }; diff --git a/wasm/rive_text_bindings.cpp b/wasm/rive_text_bindings.cpp index 10a33dc..cfe1b98 100644 --- a/wasm/rive_text_bindings.cpp +++ b/wasm/rive_text_bindings.cpp @@ -99,6 +99,41 @@ void deleteLines(WasmPtr lines) delete reinterpret_cast>*>(lines); } +std::vector fallbackFonts; + +void setFallbackFonts(emscripten::val fontsList) +{ + std::vector fonts(fontsList["length"].as()); + { + emscripten::val memoryView{emscripten::typed_memory_view(fonts.size(), fonts.data())}; + memoryView.call("set", fontsList); + } + + fallbackFonts = std::vector(); + for (auto fontPtr : fonts) + { + fallbackFonts.push_back(reinterpret_cast(fontPtr)); + } +} + +static rive::rcp pickFallbackFont(rive::Span missing) +{ + size_t length = fallbackFonts.size(); + for (size_t i = 0; i < length; i++) + { + HBFont* font = static_cast(fallbackFonts[i]); + if (i == length - 1 || font->hasGlyph(missing)) + { + rive::rcp rcFont = rive::rcp(font); + // because the font was released at load time, we need to give it an + // extra ref whenever we bump it to a reference counted pointer. + rcFont->ref(); + return rcFont; + } + } + return nullptr; +} + WasmPtr shapeText(emscripten::val codeUnits, emscripten::val runsList) { std::vector runsBytes(runsList["byteLength"].as()); @@ -121,12 +156,17 @@ WasmPtr shapeText(emscripten::val codeUnits, emscripten::val runsList) { auto result = (WasmPtr) new rive::SimpleArray( runs[0].font->shapeText(codeUnitArray, rive::Span(runs, runCount))); - return result; } return {}; } +void init() +{ + fallbackFonts.clear(); + HBFont::gFallbackProc = pickFallbackFont; +} + #ifdef DEBUG #define OFFSET_OF(type, member) ((int)(intptr_t) & (((type*)(void*)0)->member)) void assertSomeAssumptions() @@ -177,10 +217,12 @@ EMSCRIPTEN_BINDINGS(RiveText) function("deleteGlyphPath", &deleteGlyphPath); function("shapeText", &shapeText); + function("setFallbackFonts", &setFallbackFonts); function("deleteShapeResult", &deleteShapeResult); function("breakLines", &breakLines); function("deleteLines", &deleteLines); + function("init", &init); #ifdef DEBUG function("assertSomeAssumptions", &assertSomeAssumptions);