Fallback fonts for wasm

This commit is contained in:
Luigi Rosso
2022-11-02 14:50:09 -07:00
parent 9a16ae4efa
commit 58cb491df7
5 changed files with 70 additions and 13 deletions

View File

@ -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<void> initFont() async {
html.document.body!.append(script);
await script.onLoad.first;
var init = js.context['RiveText'] as js.JsFunction;
var promise = init.apply(<dynamic>[]) as js.JsObject;
var initWasm = js.context['RiveText'] as js.JsFunction;
var promise = initWasm.apply(<dynamic>[]) as js.JsObject;
var thenFunction = promise['then'] as js.JsFunction;
var completer = Completer<void>();
thenFunction.apply(
<dynamic>[
(js.JsObject module) {
var init = module['init'] as js.JsFunction;
init.apply(<dynamic>[]);
_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<void> initFont() async {
);
return completer.future;
}
void setFallbackFonts(List<Font> fonts) {
_setFallbackFonts.apply(
<dynamic>[
Uint32List.fromList(
fonts
.cast<FontWasm>()
.map((font) => font.fontPtr)
.toList(growable: false),
),
],
);
}

View File

@ -118,8 +118,6 @@ static rive::rcp<rive::Font> pickFallbackFont(rive::Span<const rive::Unichar> mi
HBFont* font = static_cast<HBFont*>(fallbackFonts[i]);
if (i == length - 1 || font->hasGlyph(missing))
{
fprintf(stderr, "FONT COUNT IS: %d\n", font->debugging_refcnt());
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(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.

View File

@ -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'

View File

@ -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),
};
};
};

View File

@ -99,6 +99,41 @@ void deleteLines(WasmPtr lines)
delete reinterpret_cast<rive::SimpleArray<rive::SimpleArray<rive::GlyphLine>>*>(lines);
}
std::vector<rive::Font*> fallbackFonts;
void setFallbackFonts(emscripten::val fontsList)
{
std::vector<int> fonts(fontsList["length"].as<unsigned>());
{
emscripten::val memoryView{emscripten::typed_memory_view(fonts.size(), fonts.data())};
memoryView.call<void>("set", fontsList);
}
fallbackFonts = std::vector<rive::Font*>();
for (auto fontPtr : fonts)
{
fallbackFonts.push_back(reinterpret_cast<rive::Font*>(fontPtr));
}
}
static rive::rcp<rive::Font> pickFallbackFont(rive::Span<const rive::Unichar> missing)
{
size_t length = fallbackFonts.size();
for (size_t i = 0; i < length; i++)
{
HBFont* font = static_cast<HBFont*>(fallbackFonts[i]);
if (i == length - 1 || font->hasGlyph(missing))
{
rive::rcp<rive::Font> rcFont = rive::rcp<rive::Font>(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<uint8_t> runsBytes(runsList["byteLength"].as<unsigned>());
@ -121,12 +156,17 @@ WasmPtr shapeText(emscripten::val codeUnits, emscripten::val runsList)
{
auto result = (WasmPtr) new rive::SimpleArray<rive::Paragraph>(
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);