From a5c9e9520a1c1261fdc4b25442e709431fa84d07 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 12 May 2023 13:27:41 -0700 Subject: [PATCH 1/3] Use new `unresolvedCodePoints` API from skia. --- DEPS | 2 +- lib/web_ui/lib/src/engine/font_fallbacks.dart | 25 +++++----- .../engine/skwasm/skwasm_impl/paragraph.dart | 49 ++++++++++--------- .../skwasm_impl/raw/text/raw_paragraph.dart | 11 +++++ lib/web_ui/skwasm/text/paragraph.cpp | 18 +++++++ 5 files changed, 69 insertions(+), 36 deletions(-) diff --git a/DEPS b/DEPS index b234a56e7034a..4cf79b7418ef5 100644 --- a/DEPS +++ b/DEPS @@ -18,7 +18,7 @@ vars = { 'llvm_git': 'https://llvm.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '539fb10d7cfb3e73ae43bdedba7d11b2012f5446', + 'skia_revision': '787af31b18ff9ba2e3627758c95accdd390776eb', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/lib/web_ui/lib/src/engine/font_fallbacks.dart b/lib/web_ui/lib/src/engine/font_fallbacks.dart index 27b7f2269be36..fc7939b73c611 100644 --- a/lib/web_ui/lib/src/engine/font_fallbacks.dart +++ b/lib/web_ui/lib/src/engine/font_fallbacks.dart @@ -120,15 +120,19 @@ class FontFallbackManager { registry.getMissingCodePoints(codePoints, fontFamilies); if (missingCodePoints.isNotEmpty) { - _codePointsToCheckAgainstFallbackFonts.addAll(missingCodePoints); - if (!_scheduledCodePointCheck) { - _scheduledCodePointCheck = true; - _idleFuture = Future.delayed(Duration.zero, () async { - _ensureFallbackFonts(); - _scheduledCodePointCheck = false; - await downloadQueue.waitForIdle(); - }); - } + addMissingCodePoints(codePoints); + } + } + + void addMissingCodePoints(List codePoints) { + _codePointsToCheckAgainstFallbackFonts.addAll(codePoints); + if (!_scheduledCodePointCheck) { + _scheduledCodePointCheck = true; + _idleFuture = Future.delayed(Duration.zero, () async { + _ensureFallbackFonts(); + _scheduledCodePointCheck = false; + await downloadQueue.waitForIdle(); + }); } } @@ -144,8 +148,7 @@ class FontFallbackManager { } final List codePoints = _codePointsToCheckAgainstFallbackFonts.toList(); _codePointsToCheckAgainstFallbackFonts.clear(); - final List missingCodePoints = registry.getMissingCodePoints(codePoints, globalFontFallbacks); - findFontsForMissingCodePoints(missingCodePoints); + findFontsForMissingCodePoints(codePoints); } void registerFallbackFont(String family) { diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart index 4d0d027aa409a..0f02c37ee8f4b 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart @@ -74,8 +74,9 @@ class SkwasmLineMetrics implements ui.LineMetrics { class SkwasmParagraph implements ui.Paragraph { SkwasmParagraph(this.handle); - ParagraphHandle handle; + final ParagraphHandle handle; bool _isDisposed = false; + bool _hasCheckedForMissingCodePoints = false; @override double get width => paragraphGetWidth(handle); @@ -102,8 +103,30 @@ class SkwasmParagraph implements ui.Paragraph { bool get didExceedMaxLines => paragraphGetDidExceedMaxLines(handle); @override - void layout(ui.ParagraphConstraints constraints) => + void layout(ui.ParagraphConstraints constraints) { paragraphLayout(handle, constraints.width); + if (!_hasCheckedForMissingCodePoints) { + _hasCheckedForMissingCodePoints = true; + final int missingCodePointCount = paragraphGetUnresolvedCodePoints(handle, nullptr, 0); + if (missingCodePointCount > 0) { + withStackScope((StackScope scope) { + final Pointer codePointBuffer = scope.allocUint32Array(missingCodePointCount); + final int returnedCodePointCount = paragraphGetUnresolvedCodePoints( + handle, + codePointBuffer, + missingCodePointCount + ); + assert(missingCodePointCount == returnedCodePointCount); + renderer.fontCollection.fontFallbackManager!.addMissingCodePoints( + List.generate( + missingCodePointCount, + (int index) => codePointBuffer[index] + ) + ); + }); + } + } + } List _convertTextBoxList(TextBoxListHandle listHandle) { final int length = textBoxListGetLength(listHandle); @@ -562,30 +585,8 @@ class SkwasmParagraphBuilder implements ui.ParagraphBuilder { placeholderScales.add(scale); } - List _getEffectiveFonts() { - final List fallbackFonts = renderer.fontCollection.fontFallbackManager!.globalFontFallbacks; - final List? currentFonts = - textStyleStack.isEmpty ? null : textStyleStack.last.style.fontFamilies; - if (currentFonts != null) { - return [ - ...currentFonts, - ...fallbackFonts, - ]; - } else if (style.defaultFontFamily != null) { - return [ - style.defaultFontFamily!, - ...fallbackFonts, - ]; - } else { - return fallbackFonts; - } - } - @override void addText(String text) { - renderer.fontCollection.fontFallbackManager!.ensureFontsSupportText( - text, _getEffectiveFonts() - ); final SkString16Handle stringHandle = skString16FromDartString(text); paragraphBuilderAddText(handle, stringHandle); skString16Free(stringHandle); diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart index c255dd27ebdd0..990424c39349e 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart @@ -119,3 +119,14 @@ external TextBoxListHandle paragraphGetBoxesForRange( @Native( symbol: 'paragraph_getBoxesForPlaceholders', isLeaf: true) external TextBoxListHandle paragraphGetBoxesForPlaceholders(ParagraphHandle handle); + +@Native, + Int, +)>(symbol: 'paragraph_getUnresolvedCodePoints', isLeaf: true) +external int paragraphGetUnresolvedCodePoints( + ParagraphHandle handle, + Pointer outCodePoints, + int outLength, +); diff --git a/lib/web_ui/skwasm/text/paragraph.cpp b/lib/web_ui/skwasm/text/paragraph.cpp index 34fe9af8d93e5..39a257e8dbdce 100644 --- a/lib/web_ui/skwasm/text/paragraph.cpp +++ b/lib/web_ui/skwasm/text/paragraph.cpp @@ -118,3 +118,21 @@ SKWASM_EXPORT TextBoxList* paragraph_getBoxesForPlaceholders( Paragraph* paragraph) { return new TextBoxList{paragraph->getRectsForPlaceholders()}; } + +SKWASM_EXPORT int paragraph_getUnresolvedCodePoints(Paragraph* paragraph, + SkUnichar* outCodePoints, + int outLength) { + if (!outCodePoints) { + return paragraph->unresolvedCodepoints().size(); + } + int outIndex = 0; + for (SkUnichar character : paragraph->unresolvedCodepoints()) { + if (outIndex < outLength) { + outCodePoints[outIndex] = character; + outIndex++; + } else { + break; + } + } + return outIndex; +} From 291cb398089724fcd4e5b21cb8d0d02cfe42bb7a Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 12 May 2023 13:32:00 -0700 Subject: [PATCH 2/3] Add some comments. --- .../engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart | 7 +++++++ lib/web_ui/skwasm/text/paragraph.cpp | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart index 990424c39349e..ac98e21eddf50 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/text/raw_paragraph.dart @@ -120,6 +120,13 @@ external TextBoxListHandle paragraphGetBoxesForRange( symbol: 'paragraph_getBoxesForPlaceholders', isLeaf: true) external TextBoxListHandle paragraphGetBoxesForPlaceholders(ParagraphHandle handle); +// Returns a list of the code points that were unable to be rendered with the +// selected fonts. The list is deduplicated, so each code point in the output +// is unique. +// If `nullptr` is passed in for `outCodePoints`, we simply return the count +// of the code points. +// Note: This must be called after the paragraph has been laid out at least +// once in order to get valid data. @Native, diff --git a/lib/web_ui/skwasm/text/paragraph.cpp b/lib/web_ui/skwasm/text/paragraph.cpp index 39a257e8dbdce..dcd1cd2e01b83 100644 --- a/lib/web_ui/skwasm/text/paragraph.cpp +++ b/lib/web_ui/skwasm/text/paragraph.cpp @@ -119,6 +119,13 @@ SKWASM_EXPORT TextBoxList* paragraph_getBoxesForPlaceholders( return new TextBoxList{paragraph->getRectsForPlaceholders()}; } +// Returns a list of the code points that were unable to be rendered with the +// selected fonts. The list is deduplicated, so each code point in the output +// is unique. +// If `nullptr` is passed in for `outCodePoints`, we simply return the count +// of the code points. +// Note: This must be called after the paragraph has been laid out at least +// once in order to get valid data. SKWASM_EXPORT int paragraph_getUnresolvedCodePoints(Paragraph* paragraph, SkUnichar* outCodePoints, int outLength) { From f46a25e433750ac699612ce7b91627e0c575b629 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 12 May 2023 14:53:36 -0700 Subject: [PATCH 3/3] Fix unit tests. --- .../skwasm/skwasm_impl/font_collection.dart | 3 ++- .../skwasm/skwasm_impl/raw/raw_fonts.dart | 5 +++++ lib/web_ui/skwasm/fonts.cpp | 5 +++++ .../test/ui/fallback_fonts_golden_test.dart | 22 +++++++++++++++---- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart index 60a2d4399ed00..091de8c029384 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart @@ -188,8 +188,9 @@ class SkwasmFontCollection implements FlutterFontCollection { @override void debugResetFallbackFonts() { - setDefaultFontFamilies([]); + setDefaultFontFamilies(['Roboto']); fontFallbackManager = FontFallbackManager(SkwasmFallbackRegistry(this)); + fontCollectionClearCaches(handle); } } diff --git a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart index 83ce54e125456..855fdeb399a35 100644 --- a/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart +++ b/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart @@ -50,3 +50,8 @@ external void fontCollectionRegisterTypeface( TypefaceHandle typeface, SkStringHandle fontName, ); + +@Native(symbol: 'fontCollection_clearCaches', isLeaf: true) +external void fontCollectionClearCaches(FontCollectionHandle handle); diff --git a/lib/web_ui/skwasm/fonts.cpp b/lib/web_ui/skwasm/fonts.cpp index 313e24dd63eb6..4a21c8ed84463 100644 --- a/lib/web_ui/skwasm/fonts.cpp +++ b/lib/web_ui/skwasm/fonts.cpp @@ -84,3 +84,8 @@ SKWASM_EXPORT void fontCollection_registerTypeface( collection->provider->registerTypeface(sk_sp(typeface)); } } + +SKWASM_EXPORT void fontCollection_clearCaches( + FlutterFontCollection* collection) { + collection->collection->clearCaches(); +} diff --git a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart index 8d84501cb1926..58dd4526bbe45 100644 --- a/lib/web_ui/test/ui/fallback_fonts_golden_test.dart +++ b/lib/web_ui/test/ui/fallback_fonts_golden_test.dart @@ -62,6 +62,7 @@ void testMain() { ui.ParagraphStyle(), ); pb.addText('مرحبا'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); @@ -100,6 +101,7 @@ void testMain() { ui.ParagraphStyle(), ); pb.addText('مرحبا'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); @@ -134,6 +136,7 @@ void testMain() { ui.ParagraphStyle(), ); pb.addText('Hello 😊'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); @@ -170,7 +173,10 @@ void testMain() { () async { // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - ui.ParagraphBuilder(ui.ParagraphStyle()).addText('ヽಠ'); + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('ヽಠ'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect( downloadedFontFamilies, @@ -182,7 +188,9 @@ void testMain() { // Do the same thing but this time with loaded fonts. downloadedFontFamilies.clear(); - ui.ParagraphBuilder(ui.ParagraphStyle()).addText('ヽಠ'); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('ヽಠ'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect(downloadedFontFamilies, isEmpty); }); @@ -190,7 +198,10 @@ void testMain() { test('can find glyph for 2/3 symbol', () async { // Try rendering text that requires fallback fonts, initially before the fonts are loaded. - ui.ParagraphBuilder(ui.ParagraphStyle()).addText('⅔'); + ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('⅔'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect( downloadedFontFamilies, @@ -201,7 +212,10 @@ void testMain() { // Do the same thing but this time with loaded fonts. downloadedFontFamilies.clear(); - ui.ParagraphBuilder(ui.ParagraphStyle()).addText('⅔'); + pb = ui.ParagraphBuilder(ui.ParagraphStyle()); + pb.addText('⅔'); + pb.build().layout(const ui.ParagraphConstraints(width: 1000)); + await renderer.fontCollection.fontFallbackManager!.debugWhenIdle(); expect(downloadedFontFamilies, isEmpty); });