From 1b6f869b49105e6fc5241051785f3f1ab45f9e76 Mon Sep 17 00:00:00 2001 From: Harry Terkelsen Date: Wed, 30 Dec 2020 15:23:36 -0800 Subject: [PATCH] Revert "[CanvasKit] Automatically fall back to Noto fonts (#23096)" This reverts commit eff27c74bf8c74781786f3c3724347e12764018a. Reason for revert: Incorrect logic breaks Gallery --- ci/licenses_golden/licenses_flutter | 1 - lib/web_ui/lib/src/engine.dart | 1 - .../lib/src/engine/canvaskit/canvas.dart | 51 +- .../src/engine/canvaskit/canvaskit_api.dart | 29 +- .../src/engine/canvaskit/font_fallbacks.dart | 867 ------------------ .../lib/src/engine/canvaskit/fonts.dart | 29 +- .../lib/src/engine/canvaskit/image.dart | 2 +- .../src/engine/canvaskit/initialization.dart | 21 +- lib/web_ui/lib/src/engine/canvaskit/path.dart | 4 +- lib/web_ui/lib/src/engine/canvaskit/text.dart | 115 +-- .../test/canvaskit/canvaskit_api_test.dart | 10 +- 11 files changed, 81 insertions(+), 1049 deletions(-) delete mode 100644 lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1be248830ed8c..177517471df03 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1209,7 +1209,6 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_canvas.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/color_filter.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/embedded_views.dart -FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/fonts.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image.dart FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/image_filter.dart diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 1069a34fe6fba..42040eb1afa56 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -32,7 +32,6 @@ part 'engine/canvaskit/canvaskit_api.dart'; part 'engine/canvaskit/color_filter.dart'; part 'engine/canvaskit/embedded_views.dart'; part 'engine/canvaskit/fonts.dart'; -part 'engine/canvaskit/font_fallbacks.dart'; part 'engine/canvaskit/image.dart'; part 'engine/canvaskit/image_filter.dart'; part 'engine/canvaskit/initialization.dart'; diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart index 5c372178b7489..4333a15c80631 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart @@ -176,8 +176,7 @@ class CkCanvas { void drawPoints(CkPaint paint, ui.PointMode pointMode, Float32List points) { skCanvas.drawPoints( toSkPointMode(pointMode), - // TODO(hterkelsen): Don't convert this to 2d after we move to CK 0.21. - rawPointsToSkPoints2d(points), + points, paint.skiaObject, ); } @@ -193,10 +192,10 @@ class CkCanvas { skCanvas.drawRect(toSkRect(rect), paint.skiaObject); } - void drawShadow( - CkPath path, ui.Color color, double elevation, bool transparentOccluder) { - drawSkShadow(skCanvas, path, color, elevation, transparentOccluder, - ui.window.devicePixelRatio); + void drawShadow(CkPath path, ui.Color color, double elevation, + bool transparentOccluder) { + drawSkShadow(skCanvas, path, color, elevation, + transparentOccluder, ui.window.devicePixelRatio); } void drawVertices( @@ -238,8 +237,7 @@ class CkCanvas { } void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { - final _CkManagedSkImageFilterConvertible convertible = - filter as _CkManagedSkImageFilterConvertible; + final _CkManagedSkImageFilterConvertible convertible = filter as _CkManagedSkImageFilterConvertible; return skCanvas.saveLayer( null, toSkRect(bounds), @@ -269,8 +267,8 @@ class CkCanvas { class RecordingCkCanvas extends CkCanvas { RecordingCkCanvas(SkCanvas skCanvas, ui.Rect bounds) - : pictureSnapshot = CkPictureSnapshot(bounds), - super(skCanvas); + : pictureSnapshot = CkPictureSnapshot(bounds), + super(skCanvas); @override final CkPictureSnapshot pictureSnapshot; @@ -312,8 +310,7 @@ class RecordingCkCanvas extends CkCanvas { CkPaint paint, ) { super.drawArc(oval, startAngle, sweepAngle, useCenter, paint); - _addCommand( - CkDrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint)); + _addCommand(CkDrawArcCommand(oval, startAngle, sweepAngle, useCenter, paint)); } @override @@ -326,8 +323,7 @@ class RecordingCkCanvas extends CkCanvas { ui.BlendMode blendMode, ) { super.drawAtlasRaw(paint, atlas, rstTransforms, rects, colors, blendMode); - _addCommand(CkDrawAtlasCommand( - paint, atlas, rstTransforms, rects, colors, blendMode)); + _addCommand(CkDrawAtlasCommand(paint, atlas, rstTransforms, rects, colors, blendMode)); } @override @@ -422,11 +418,10 @@ class RecordingCkCanvas extends CkCanvas { } @override - void drawShadow( - CkPath path, ui.Color color, double elevation, bool transparentOccluder) { + void drawShadow(CkPath path, ui.Color color, double elevation, + bool transparentOccluder) { super.drawShadow(path, color, elevation, transparentOccluder); - _addCommand( - CkDrawShadowCommand(path, color, elevation, transparentOccluder)); + _addCommand(CkDrawShadowCommand(path, color, elevation, transparentOccluder)); } @override @@ -632,7 +627,7 @@ class CkTransformCommand extends CkPaintCommand { @override void apply(SkCanvas canvas) { canvas.concat(toSkMatrixFromFloat32(matrix4)); - } + } } class CkSkewCommand extends CkPaintCommand { @@ -665,8 +660,7 @@ class CkClipRectCommand extends CkPaintCommand { } class CkDrawArcCommand extends CkPaintCommand { - CkDrawArcCommand( - this.oval, this.startAngle, this.sweepAngle, this.useCenter, this.paint); + CkDrawArcCommand(this.oval, this.startAngle, this.sweepAngle, this.useCenter, this.paint); final ui.Rect oval; final double startAngle; @@ -688,8 +682,7 @@ class CkDrawArcCommand extends CkPaintCommand { } class CkDrawAtlasCommand extends CkPaintCommand { - CkDrawAtlasCommand(this.paint, this.atlas, this.rstTransforms, this.rects, - this.colors, this.blendMode); + CkDrawAtlasCommand(this.paint, this.atlas, this.rstTransforms, this.rects, this.colors, this.blendMode); final CkPaint paint; final CkImage atlas; @@ -814,8 +807,7 @@ class CkDrawPointsCommand extends CkPaintCommand { void apply(SkCanvas canvas) { canvas.drawPoints( toSkPointMode(pointMode), - // TODO(hterkelsen): Don't convert this to 2d after we move to CK 0.21. - rawPointsToSkPoints2d(points), + points, paint.skiaObject, ); } @@ -932,7 +924,7 @@ class CkDrawImageCommand extends CkPaintCommand { final CkPaint paint; CkDrawImageCommand(CkImage image, this.offset, this.paint) - : this.image = image.clone(); + : this.image = image.clone(); @override void apply(SkCanvas canvas) { @@ -957,7 +949,7 @@ class CkDrawImageRectCommand extends CkPaintCommand { final CkPaint paint; CkDrawImageRectCommand(CkImage image, this.src, this.dst, this.paint) - : this.image = image.clone(); + : this.image = image.clone(); @override void apply(SkCanvas canvas) { @@ -978,7 +970,7 @@ class CkDrawImageRectCommand extends CkPaintCommand { class CkDrawImageNineCommand extends CkPaintCommand { CkDrawImageNineCommand(CkImage image, this.center, this.dst, this.paint) - : this.image = image.clone(); + : this.image = image.clone(); final CkImage image; final ui.Rect center; @@ -1069,8 +1061,7 @@ class CkSaveLayerWithFilterCommand extends CkPaintCommand { @override void apply(SkCanvas canvas) { - final _CkManagedSkImageFilterConvertible convertible = - filter as _CkManagedSkImageFilterConvertible; + final _CkManagedSkImageFilterConvertible convertible = filter as _CkManagedSkImageFilterConvertible; return canvas.saveLayer( null, toSkRect(bounds), diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart index eca86072b0d96..f4bf266ee1252 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -49,7 +49,7 @@ class CanvasKit { external SkMaskFilterNamespace get MaskFilter; external SkColorFilterNamespace get ColorFilter; external SkImageFilterNamespace get ImageFilter; - external SkPathNamespace get Path; + external SkPath MakePathFromOp(SkPath path1, SkPath path2, SkPathOp pathOp); external SkTonalColors computeTonalColors(SkTonalColors inTonalColors); external SkVertices MakeVertices( SkVertexMode mode, @@ -96,6 +96,10 @@ class CanvasKit { external SkSurface MakeSWCanvasSurface(html.CanvasElement canvas); external void setCurrentContext(int glContext); + /// Creates an [SkPath] using commands obtained from [SkPath.toCmds]. + // TODO(yjbanov): switch to CanvasKit.Path.MakeFromCmds when it's available. + external SkPath MakePathFromCmds(List pathCommands); + /// Creates an image from decoded pixels represented as a list of bytes. /// /// The pixel data must match the [width], [height], [alphaType], [colorType], @@ -719,7 +723,7 @@ class SkImage { SkTileMode tileModeY, Float32List? matrix, // 3x3 matrix ); - external Uint8List readPixels(int srcX, int srcY, SkImageInfo imageInfo); + external Uint8List readPixels(SkImageInfo imageInfo, int srcX, int srcY); external SkData encodeToData(); external bool isAliasOf(SkImage other); external bool isDeleted(); @@ -779,7 +783,7 @@ class SkShader { @JS() class SkMaskFilterNamespace { external SkMaskFilter MakeBlur( - SkBlurStyle blurStyle, double sigma, bool respectCTM); + SkBlurStyle blurStyle, double sigma, bool respectCTM); } // This needs to be bound to top-level because SkPaint is initialized @@ -860,14 +864,6 @@ class SkImageFilter { external void delete(); } -@JS() -class SkPathNamespace { - external SkPath MakeFromOp(SkPath path1, SkPath path2, SkPathOp pathOp); - - /// Creates an [SkPath] using commands obtained from [SkPath.toCmds]. - external SkPath MakeFromCmds(List pathCommands); -} - // Mappings from SkMatrix-index to input-index. const List _skMatrixIndexToMatrix4Index = [ 0, 4, 12, // Row 1 @@ -1187,8 +1183,7 @@ class SkPath { /// Serializes the path into a list of commands. /// - /// The list can be used to create a new [SkPath] using - /// [CanvasKit.Path.MakeFromCmds]. + /// The list can be used to create a new [SkPath] using [CanvasKit.MakePathFromCmds]. external List toCmds(); external void delete(); @@ -1409,7 +1404,7 @@ class SkCanvas { ); external void drawPoints( SkPointMode pointMode, - List points, + Float32List points, SkPaint paint, ); external void drawRRect( @@ -1791,8 +1786,7 @@ abstract class Collector { /// Uses timers to delete objects in batches and outside the animation frame. class ProductionCollector implements Collector { ProductionCollector() { - _skObjectFinalizationRegistry = - SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { + _skObjectFinalizationRegistry = SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { // This is called when GC decides to collect the wrapper object and // notify us, which may happen after the object is already deleted // explicitly, e.g. when its ref count drops to zero. When that happens @@ -1973,8 +1967,7 @@ bool browserSupportsFinalizationRegistry = /// Sets the value of [browserSupportsFinalizationRegistry] to its true value. void debugResetBrowserSupportsFinalizationRegistry() { - browserSupportsFinalizationRegistry = - _finalizationRegistryConstructor != null; + browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null; } @JS() diff --git a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart b/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart deleted file mode 100644 index 6a86f53971142..0000000000000 --- a/lib/web_ui/lib/src/engine/canvaskit/font_fallbacks.dart +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// @dart = 2.12 -part of engine; - -/// Whether or not "Noto Sans Symbols" and "Noto Color Emoji" fonts have been -/// downloaded. We download these as fallbacks when no other font covers the -/// given code units. -bool _downloadedSymbolsAndEmoji = false; - -final Set codeUnitsWithNoKnownFont = {}; - -Future _findFontsForMissingCodeunits(List codeunits) async { - _ensureNotoFontTreeCreated(); - // If all of the code units are known to have no Noto Font which covers them, - // then just give up. We have already logged a warning. - if (codeunits.every((u) => codeUnitsWithNoKnownFont.contains(u))) { - return; - } - Set<_NotoFont> fonts = <_NotoFont>{}; - Set coveredCodeUnits = {}; - Set missingCodeUnits = {}; - for (int codeunit in codeunits) { - List<_NotoFont> fontsForUnit = _lookupNotoFontsForCodeunit(codeunit); - fonts.addAll(fontsForUnit); - if (fontsForUnit.isNotEmpty) { - coveredCodeUnits.add(codeunit); - } else { - missingCodeUnits.add(codeunit); - } - } - fonts = _findMinimumFontsForCodeunits(coveredCodeUnits, fonts); - for (_NotoFont font in fonts) { - if (_resolvedNotoFonts[font] == null) { - String googleFontCss = await html.window - .fetch(font.googleFontsCssUrl) - .then((dynamic response) => - response.text().then((dynamic x) => x as String)); - final _ResolvedNotoFont resolvedFont = - _makeResolvedNotoFontFromCss(googleFontCss, font.name); - _registerResolvedFont(font, resolvedFont); - } - } - - Set<_ResolvedNotoSubset> resolvedFonts = <_ResolvedNotoSubset>{}; - for (int codeunit in coveredCodeUnits) { - resolvedFonts.addAll(_lookupResolvedFontsForCodeunit(codeunit)); - } - - for (_ResolvedNotoSubset resolvedFont in resolvedFonts) { - skiaFontCollection.registerFallbackFont( - resolvedFont.url, resolvedFont.name); - } - - if (missingCodeUnits.isNotEmpty) { - if (!_downloadedSymbolsAndEmoji) { - await _registerSymbolsAndEmoji(); - } else { - html.window.console - .log('Could not find a Noto font to display all missing characters. ' - 'Please add a font asset for the missing characters.'); - codeUnitsWithNoKnownFont.addAll(missingCodeUnits); - } - } - await skiaFontCollection.ensureFontsLoaded(); - sendFontChangeMessage(); -} - -/// Parse the CSS file for a font and make a list of resolved subsets. -/// -/// A CSS file from Google Fonts looks like this: -/// -/// /* [0] */ -/// @font-face { -/// font-family: 'Noto Sans KR'; -/// font-style: normal; -/// font-weight: 400; -/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.0.woff2) format('woff2'); -/// unicode-range: U+f9ca-fa0b, U+ff03-ff05, U+ff07, U+ff0a-ff0b, U+ff0d-ff19, U+ff1b, U+ff1d, U+ff20-ff5b, U+ff5d, U+ffe0-ffe3, U+ffe5-ffe6; -/// } -/// /* [1] */ -/// @font-face { -/// font-family: 'Noto Sans KR'; -/// font-style: normal; -/// font-weight: 400; -/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.1.woff2) format('woff2'); -/// unicode-range: U+f92f-f980, U+f982-f9c9; -/// } -/// /* [2] */ -/// @font-face { -/// font-family: 'Noto Sans KR'; -/// font-style: normal; -/// font-weight: 400; -/// src: url(https://fonts.gstatic.com/s/notosanskr/v13/PbykFmXiEBPT4ITbgNA5Cgm20xz64px_1hVWr0wuPNGmlQNMEfD4.2.woff2) format('woff2'); -/// unicode-range: U+d723-d728, U+d72a-d733, U+d735-d748, U+d74a-d74f, U+d752-d753, U+d755-d757, U+d75a-d75f, U+d762-d764, U+d766-d768, U+d76a-d76b, U+d76d-d76f, U+d771-d787, U+d789-d78b, U+d78d-d78f, U+d791-d797, U+d79a, U+d79c, U+d79e-d7a3, U+f900-f909, U+f90b-f92e; -/// } -_ResolvedNotoFont _makeResolvedNotoFontFromCss(String css, String name) { - List<_ResolvedNotoSubset> subsets = <_ResolvedNotoSubset>[]; - bool resolvingFontFace = false; - String? fontFaceUrl; - List<_UnicodeRange>? fontFaceUnicodeRanges; - for (final String line in LineSplitter.split(css)) { - // Search for the beginning of a @font-face. - if (!resolvingFontFace) { - if (line == '@font-face {') { - resolvingFontFace = true; - } else { - continue; - } - } else { - // We are resolving a @font-face, read out the url and ranges. - if (line.startsWith(' src:')) { - int urlStart = line.indexOf('url('); - if (urlStart == -1) { - throw new Exception('Unable to resolve Noto font URL: $line'); - } - int urlEnd = line.indexOf(')'); - fontFaceUrl = line.substring(urlStart + 4, urlEnd); - } else if (line.startsWith(' unicode-range:')) { - fontFaceUnicodeRanges = <_UnicodeRange>[]; - String rangeString = line.substring(17, line.length - 1); - List rawRanges = rangeString.split(', '); - for (final String rawRange in rawRanges) { - List startEnd = rawRange.split('-'); - if (startEnd.length == 1) { - String singleRange = startEnd.single; - assert(singleRange.startsWith('U+')); - int rangeValue = int.parse(singleRange.substring(2), radix: 16); - fontFaceUnicodeRanges.add(_UnicodeRange(rangeValue, rangeValue)); - } else { - assert(startEnd.length == 2); - String startRange = startEnd[0]; - String endRange = startEnd[1]; - assert(startRange.startsWith('U+')); - int startValue = int.parse(startRange.substring(2), radix: 16); - int endValue = int.parse(endRange, radix: 16); - fontFaceUnicodeRanges.add(_UnicodeRange(startValue, endValue)); - } - } - } else if (line == '}') { - subsets.add( - _ResolvedNotoSubset(fontFaceUrl!, name, fontFaceUnicodeRanges!)); - resolvingFontFace = false; - } else { - continue; - } - } - } - - return _ResolvedNotoFont(name, subsets); -} - -void _registerResolvedFont(_NotoFont font, _ResolvedNotoFont resolvedFont) { - _resolvedNotoFonts[font] = resolvedFont; - - for (_ResolvedNotoSubset subset in resolvedFont.subsets) { - for (_UnicodeRange range in subset.ranges) { - _resolvedNotoTreeRoot = _insertNotoFontRange<_ResolvedNotoSubset>( - range, subset, _resolvedNotoTreeRoot); - } - } - - assert( - _verifyNotoTree(_resolvedNotoTreeRoot), - 'Resolved Noto tree is invalid: ' - '${_verifyNotoSubtree(_resolvedNotoTreeRoot).reason}'); -} - -/// In the case where none of the known Noto Fonts cover a set of code units, -/// try the Symbols and Emoji fonts. We don't know the exact range of code units -/// that are covered by these fonts, so we download them and hope for the best. -Future _registerSymbolsAndEmoji() async { - const String symbolsUrl = - 'https://fonts.googleapis.com/css2?family=Noto+Sans+Symbols'; - const String emojiUrl = - 'https://fonts.googleapis.com/css2?family=Noto+Color+Emoji+Compat'; - - String symbolsCss = await html.window.fetch(symbolsUrl).then( - (dynamic response) => - response.text().then((dynamic x) => x as String)); - String emojiCss = await html.window.fetch(emojiUrl).then((dynamic response) => - response.text().then((dynamic x) => x as String)); - - String extractUrlFromCss(String css) { - for (final String line in LineSplitter.split(css)) { - if (line.startsWith(' src:')) { - int urlStart = line.indexOf('url('); - if (urlStart == -1) { - throw new Exception('Unable to resolve Noto font URL: $line'); - } - int urlEnd = line.indexOf(')'); - return line.substring(urlStart + 4, urlEnd); - } - } - throw Exception('Unable to determine URL for Noto font'); - } - - String symbolsFontUrl = extractUrlFromCss(symbolsCss); - String emojiFontUrl = extractUrlFromCss(emojiCss); - - skiaFontCollection.registerFallbackFont(symbolsFontUrl, 'Noto Sans Symbols'); - skiaFontCollection.registerFallbackFont( - emojiFontUrl, 'Noto Color Emoji Compat'); - _downloadedSymbolsAndEmoji = true; -} - -/// Finds the minimum set of fonts which covers all of the [codeunits]. -/// -/// Since set cover is NP-complete, we approximate using a greedy algorithm -/// which finds the font which covers the most codeunits. If multiple CJK -/// fonts match the same number of codeunits, we choose one based on the user's -/// locale. -Set<_NotoFont> _findMinimumFontsForCodeunits( - Iterable codeunits, Set<_NotoFont> fonts) { - List unmatchedCodeunits = List.from(codeunits); - Set<_NotoFont> minimumFonts = <_NotoFont>{}; - List<_NotoFont> bestFonts = <_NotoFont>[]; - int maxCodeunitsCovered = 0; - - String language = html.window.navigator.language; - - // This is guaranteed to terminate because [codeunits] is a list of fonts - // which we've already determined are covered by [fonts]. - while (unmatchedCodeunits.isNotEmpty) { - for (var font in fonts) { - int codeunitsCovered = 0; - for (int codeunit in unmatchedCodeunits) { - if (font.matchesCodeunit(codeunit)) { - codeunitsCovered++; - } - } - if (codeunitsCovered > maxCodeunitsCovered) { - bestFonts.clear(); - bestFonts.add(font); - maxCodeunitsCovered = codeunitsCovered; - } else if (codeunitsCovered == maxCodeunitsCovered) { - bestFonts.add(font); - } - } - assert(bestFonts.isNotEmpty); - // If the list of best fonts are all CJK fonts, choose the best one based - // on locale. Otherwise just choose the first font. - _NotoFont bestFont = bestFonts.first; - if (bestFonts.length > 1) { - if (bestFonts.every((font) => _cjkFonts.contains(font))) { - if (language == 'zh-Hans' || - language == 'zh-CN' || - language == 'zh-SG' || - language == 'zh-MY') { - if (bestFonts.contains(_notoSansSC)) { - bestFont = _notoSansSC; - } - } else if (language == 'zh-Hant' || - language == 'zh-TW' || - language == 'zh-MO') { - if (bestFonts.contains(_notoSansTC)) { - bestFont = _notoSansTC; - } - } else if (language == 'zh-HK') { - if (bestFonts.contains(_notoSansHK)) { - bestFont = _notoSansHK; - } - } else if (language == 'ja') { - if (bestFonts.contains(_notoSansJP)) { - bestFont = _notoSansJP; - } - } - } - } - unmatchedCodeunits - .removeWhere((codeunit) => bestFont.matchesCodeunit(codeunit)); - minimumFonts.add(bestFont); - } - return minimumFonts; -} - -void _ensureNotoFontTreeCreated() { - if (_notoTreeRoot != null) { - return; - } - - for (_NotoFont font in _notoFonts) { - for (_UnicodeRange range in font.unicodeRanges) { - _notoTreeRoot = - _insertNotoFontRange<_NotoFont>(range, font, _notoTreeRoot); - } - } - - assert( - _verifyNotoTree(_notoTreeRoot), - 'The Noto font tree is invalid: ' - '${_verifyNotoSubtree(_notoTreeRoot).reason}'); -} - -List<_NotoFont> _lookupNotoFontsForCodeunit(int codeunit) { - List<_NotoFont> lookupHelper(_NotoTreeNode<_NotoFont> node) { - if (node.range.contains(codeunit)) { - return node.fonts; - } - if (node.range.start > codeunit) { - if (node.left != null) { - return lookupHelper(node.left!); - } else { - return const <_NotoFont>[]; - } - } else { - if (node.right != null) { - return lookupHelper(node.right!); - } else { - return const <_NotoFont>[]; - } - } - } - - return lookupHelper(_notoTreeRoot!); -} - -List<_ResolvedNotoSubset> _lookupResolvedFontsForCodeunit(int codeunit) { - List<_ResolvedNotoSubset> lookupHelper( - _NotoTreeNode<_ResolvedNotoSubset> node) { - if (node.range.contains(codeunit)) { - return node.fonts; - } - if (node.range.start > codeunit) { - if (node.left != null) { - return lookupHelper(node.left!); - } else { - return const <_ResolvedNotoSubset>[]; - } - } else { - if (node.right != null) { - return lookupHelper(node.right!); - } else { - return const <_ResolvedNotoSubset>[]; - } - } - } - - return lookupHelper(_resolvedNotoTreeRoot!); -} - -class _NotoFont { - final String name; - final List<_UnicodeRange> unicodeRanges; - - const _NotoFont(this.name, this.unicodeRanges); - - bool matchesCodeunit(int codeunit) { - for (_UnicodeRange range in unicodeRanges) { - if (range.contains(codeunit)) { - return true; - } - } - return false; - } - - String get googleFontsCssUrl => - 'https://fonts.googleapis.com/css2?family=${name.replaceAll(' ', '+')}'; -} - -class _UnicodeRange { - final int start; - final int end; - - const _UnicodeRange(this.start, this.end); - - bool contains(int codeUnit) { - return start <= codeUnit && codeUnit <= end; - } - - @override - bool operator ==(dynamic other) { - if (other is! _UnicodeRange) { - return false; - } - _UnicodeRange range = other; - return range.start == start && range.end == end; - } - - @override - int get hashCode => ui.hashValues(start, end); -} - -class _ResolvedNotoFont { - final String name; - final List<_ResolvedNotoSubset> subsets; - - const _ResolvedNotoFont(this.name, this.subsets); -} - -class _ResolvedNotoSubset { - final String url; - final String name; - final List<_UnicodeRange> ranges; - - const _ResolvedNotoSubset(this.url, this.name, this.ranges); -} - -const _NotoFont _notoSansSC = _NotoFont('Noto Sans SC', <_UnicodeRange>[ - _UnicodeRange(12288, 12591), - _UnicodeRange(12800, 13311), - _UnicodeRange(19968, 40959), - _UnicodeRange(65072, 65135), - _UnicodeRange(65280, 65519), -]); - -const _NotoFont _notoSansTC = _NotoFont('Noto Sans TC', <_UnicodeRange>[ - _UnicodeRange(12288, 12351), - _UnicodeRange(12549, 12585), - _UnicodeRange(19968, 40959), -]); - -const _NotoFont _notoSansHK = _NotoFont('Noto Sans HK', <_UnicodeRange>[ - _UnicodeRange(12288, 12351), - _UnicodeRange(12549, 12585), - _UnicodeRange(19968, 40959), -]); - -const _NotoFont _notoSansJP = _NotoFont('Noto Sans JP', <_UnicodeRange>[ - _UnicodeRange(12288, 12543), - _UnicodeRange(19968, 40959), - _UnicodeRange(65280, 65519), -]); - -const List<_NotoFont> _cjkFonts = <_NotoFont>[ - _notoSansSC, - _notoSansTC, - _notoSansHK, - _notoSansJP, -]; - -const List<_NotoFont> _notoFonts = <_NotoFont>[ - _notoSansSC, - _notoSansTC, - _notoSansHK, - _notoSansJP, - _NotoFont('Noto Naskh Arabic UI', <_UnicodeRange>[ - _UnicodeRange(1536, 1791), - _UnicodeRange(8204, 8206), - _UnicodeRange(8208, 8209), - _UnicodeRange(8271, 8271), - _UnicodeRange(11841, 11841), - _UnicodeRange(64336, 65023), - _UnicodeRange(65132, 65276), - ]), - _NotoFont('Noto Sans Armenian', <_UnicodeRange>[ - _UnicodeRange(1328, 1424), - _UnicodeRange(64275, 64279), - ]), - _NotoFont('Noto Sans Bengali UI', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(2433, 2555), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Myanmar UI', <_UnicodeRange>[ - _UnicodeRange(4096, 4255), - _UnicodeRange(8204, 8205), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Egyptian Hieroglyphs', <_UnicodeRange>[ - _UnicodeRange(77824, 78894), - ]), - _NotoFont('Noto Sans Ethiopic', <_UnicodeRange>[ - _UnicodeRange(4608, 5017), - _UnicodeRange(11648, 11742), - _UnicodeRange(43777, 43822), - ]), - _NotoFont('Noto Sans Georgian', <_UnicodeRange>[ - _UnicodeRange(1417, 1417), - _UnicodeRange(4256, 4351), - _UnicodeRange(11520, 11567), - ]), - _NotoFont('Noto Sans Gujarati UI', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(2688, 2815), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - _UnicodeRange(43056, 43065), - ]), - _NotoFont('Noto Sans Gurmukhi UI', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(2561, 2677), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - _UnicodeRange(9772, 9772), - _UnicodeRange(43056, 43065), - ]), - _NotoFont('Noto Sans Hebrew', <_UnicodeRange>[ - _UnicodeRange(1424, 1535), - _UnicodeRange(8362, 8362), - _UnicodeRange(9676, 9676), - _UnicodeRange(64285, 64335), - ]), - _NotoFont('Noto Sans Devanagari UI', <_UnicodeRange>[ - _UnicodeRange(2304, 2431), - _UnicodeRange(7376, 7414), - _UnicodeRange(7416, 7417), - _UnicodeRange(8204, 9205), - _UnicodeRange(8360, 8360), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - _UnicodeRange(43056, 43065), - _UnicodeRange(43232, 43259), - ]), - _NotoFont('Noto Sans Kannada UI', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(3202, 3314), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Khmer UI', <_UnicodeRange>[ - _UnicodeRange(6016, 6143), - _UnicodeRange(8204, 8204), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans KR', <_UnicodeRange>[ - _UnicodeRange(12593, 12686), - _UnicodeRange(12800, 12828), - _UnicodeRange(12896, 12923), - _UnicodeRange(44032, 55215), - ]), - _NotoFont('Noto Sans Lao UI', <_UnicodeRange>[ - _UnicodeRange(3713, 3807), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Malayalam UI', <_UnicodeRange>[ - _UnicodeRange(775, 775), - _UnicodeRange(803, 803), - _UnicodeRange(2404, 2405), - _UnicodeRange(3330, 3455), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Sinhala', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(3458, 3572), - _UnicodeRange(8204, 8205), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Tamil UI', <_UnicodeRange>[ - _UnicodeRange(2404, 2405), - _UnicodeRange(2946, 3066), - _UnicodeRange(8204, 8205), - _UnicodeRange(8377, 8377), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Telugu UI', <_UnicodeRange>[ - _UnicodeRange(2385, 2386), - _UnicodeRange(2404, 2405), - _UnicodeRange(3072, 3199), - _UnicodeRange(7386, 7386), - _UnicodeRange(8204, 8205), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans Thai UI', <_UnicodeRange>[ - _UnicodeRange(3585, 3675), - _UnicodeRange(8204, 8205), - _UnicodeRange(9676, 9676), - ]), - _NotoFont('Noto Sans', <_UnicodeRange>[ - _UnicodeRange(0, 255), - _UnicodeRange(305, 305), - _UnicodeRange(338, 339), - _UnicodeRange(699, 700), - _UnicodeRange(710, 710), - _UnicodeRange(730, 730), - _UnicodeRange(732, 732), - _UnicodeRange(8192, 8303), - _UnicodeRange(8308, 8308), - _UnicodeRange(8364, 8364), - _UnicodeRange(8482, 8482), - _UnicodeRange(8593, 8593), - _UnicodeRange(8595, 8595), - _UnicodeRange(8722, 8722), - _UnicodeRange(8725, 8725), - _UnicodeRange(65279, 65279), - _UnicodeRange(65533, 65533), - _UnicodeRange(1024, 1119), - _UnicodeRange(1168, 1169), - _UnicodeRange(1200, 1201), - _UnicodeRange(8470, 8470), - _UnicodeRange(1120, 1327), - _UnicodeRange(7296, 7304), - _UnicodeRange(8372, 8372), - _UnicodeRange(11744, 11775), - _UnicodeRange(42560, 42655), - _UnicodeRange(65070, 65071), - _UnicodeRange(880, 1023), - _UnicodeRange(7936, 8191), - _UnicodeRange(256, 591), - _UnicodeRange(601, 601), - _UnicodeRange(7680, 7935), - _UnicodeRange(8224, 8224), - _UnicodeRange(8352, 8363), - _UnicodeRange(8365, 8399), - _UnicodeRange(8467, 8467), - _UnicodeRange(11360, 11391), - _UnicodeRange(42784, 43007), - _UnicodeRange(258, 259), - _UnicodeRange(272, 273), - _UnicodeRange(296, 297), - _UnicodeRange(360, 361), - _UnicodeRange(416, 417), - _UnicodeRange(431, 432), - _UnicodeRange(7840, 7929), - _UnicodeRange(8363, 8363), - ]), -]; - -// TODO(hterkelsen): Add unit tests for the Red-Black tree code. - -/// A node in a red-black tree for Noto Fonts. -class _NotoTreeNode { - _NotoTreeNode? parent; - _NotoTreeNode? left; - _NotoTreeNode? right; - - /// If `true`, then this node is black. Otherwise it is red. - bool isBlack = false; - bool get isRed => !isBlack; - - final _UnicodeRange range; - final List fonts; - - _NotoTreeNode(this.range) : this.fonts = []; -} - -/// Associates [range] with [font] in the Noto Font tree. -/// -/// Returns the root node. -_NotoTreeNode _insertNotoFontRange( - _UnicodeRange range, T font, _NotoTreeNode? root) { - _NotoTreeNode? newNode = _insertNotoFontRangeHelper(root, range, font); - if (newNode != null) { - _repairNotoFontTree(newNode); - - // Make sure the root node is correctly set. - _NotoTreeNode newRoot = newNode; - while (newRoot.parent != null) { - newRoot = newRoot.parent!; - } - - return newRoot; - } - return root!; -} - -/// Recurses the font tree and associates [range] with [font]. -/// -/// If a new node is created, it is returned so we can repair the tree. -_NotoTreeNode? _insertNotoFontRangeHelper( - _NotoTreeNode? root, _UnicodeRange range, T font) { - if (root != null) { - if (root.range == range) { - // The root node range is the same as the range we're inserting. - root.fonts.add(font); - return null; - } - if (range.start < root.range.start) { - assert(range.end < root.range.start, - 'Overlapping Unicode range in Noto Tree'); - if (root.left != null) { - return _insertNotoFontRangeHelper(root.left, range, font); - } else { - _NotoTreeNode newNode = _NotoTreeNode(range); - newNode.fonts.add(font); - newNode.parent = root; - root.left = newNode; - return newNode; - } - } else { - assert(root.range.end < range.start, - 'Overlapping Unicode range in Noto Tree'); - if (root.right != null) { - return _insertNotoFontRangeHelper(root.right, range, font); - } else { - _NotoTreeNode newNode = _NotoTreeNode(range); - newNode.fonts.add(font); - newNode.parent = root; - root.right = newNode; - return newNode; - } - } - } else { - // If [root] is null, then the tree is empty. Create a new root. - _NotoTreeNode newRoot = _NotoTreeNode(range); - newRoot.fonts.add(font); - return newRoot; - } -} - -void _rotateLeft(_NotoTreeNode node) { - // We will only ever call this on nodes which have a right child. - _NotoTreeNode newNode = node.right!; - _NotoTreeNode? parent = node.parent; - - node.right = newNode.left; - newNode.left = node; - node.parent = newNode; - if (node.right != null) { - node.right!.parent = node; - } - - if (parent != null) { - if (node == parent.left) { - parent.left = newNode; - } else { - parent.right = newNode; - } - } - - newNode.parent = parent; -} - -void _rotateRight(_NotoTreeNode node) { - // We will only ever call this on nodes which have a left child. - _NotoTreeNode newNode = node.left!; - _NotoTreeNode? parent = node.parent; - - node.left = newNode.right; - newNode.right = node; - node.parent = newNode; - - if (node.left != null) { - node.left!.parent = node; - } - - if (parent != null) { - if (node == parent.left) { - parent.left = newNode; - } else { - parent.right = newNode; - } - } - - newNode.parent = parent; -} - -void _repairNotoFontTree(_NotoTreeNode node) { - if (node.parent == null) { - // This is the root node. The root node must be black. - node.isBlack = true; - return; - } else if (node.parent!.isBlack) { - // Do nothing. - return; - } - - // If we've reached here, then (1) node's parent is non-null and (2) node's - // parent is red, which means that node's parent is not the root node and - // therefore (3) node's grandparent is not null; - - _NotoTreeNode parent = node.parent!; - _NotoTreeNode grandparent = parent.parent!; - - _NotoTreeNode? uncle; - if (parent == grandparent.left) { - uncle = grandparent.right; - } else { - uncle = grandparent.left; - } - if (uncle != null && uncle.isRed) { - parent.isBlack = true; - uncle.isBlack = true; - grandparent.isBlack = false; - _repairNotoFontTree(grandparent); - return; - } - - if (node == parent.right && parent == grandparent.left) { - _rotateLeft(parent); - node = node.left!; - } else if (node == parent.left && parent == grandparent.right) { - _rotateRight(parent); - node = node.right!; - } - - parent = node.parent!; - grandparent = parent.parent!; - - if (node == parent.left) { - _rotateRight(grandparent); - } else { - _rotateLeft(grandparent); - } - - parent.isBlack = true; - grandparent.isBlack = false; -} - -bool _verifyNotoTree(_NotoTreeNode? root) { - _VerifyNotoTreeResult result = _verifyNotoSubtree(root); - return result.isValid; -} - -_VerifyNotoTreeResult _verifyNotoSubtree(_NotoTreeNode? node) { - if (node == null) { - // Leaves of the tree are represented as null nodes. Leaf nodes are black. - return _VerifyNotoTreeResult(true, 1); - } - if (node.parent == null) { - // This is the root node of the tree. The root node must be black. - if (!node.isBlack) { - return _VerifyNotoTreeResult(false, 0, 'Root node is red'); - } - } - - int blackNodesOnPath = 0; - if (node.isRed) { - // Both of a red tree node's children must be black. - if ((node.left != null && !node.left!.isBlack) || - (node.right != null && !node.right!.isBlack)) { - return _VerifyNotoTreeResult(false, -1, 'Red node has a red child'); - } - } else { - blackNodesOnPath = 1; - } - - _VerifyNotoTreeResult leftResult = _verifyNotoSubtree(node.left); - _VerifyNotoTreeResult rightResult = _verifyNotoSubtree(node.right); - - if (!leftResult.isValid) { - return leftResult; - } else if (!rightResult.isValid) { - return rightResult; - } else if (leftResult.blackNodesOnPath != rightResult.blackNodesOnPath) { - return _VerifyNotoTreeResult( - false, - -1, - "The number of black nodes on the path from " - "root to leaf isn't the same for all leaves"); - } - - return _VerifyNotoTreeResult( - true, blackNodesOnPath + leftResult.blackNodesOnPath); -} - -class _VerifyNotoTreeResult { - /// Whether or not the tree conforms to the Red-Black tree invariants. - final bool isValid; - - /// The number of black nodes on the path from the node to the root. - final int blackNodesOnPath; - - /// A human-readable reason why the tree is invalid. - final String? reason; - - const _VerifyNotoTreeResult(this.isValid, this.blackNodesOnPath, - [this.reason]); -} - -/// The root of the unresolved Noto font Red-Black Tree. -_NotoTreeNode<_NotoFont>? _notoTreeRoot; - -/// The root of the resolved Noto font Red-Black Tree. -_NotoTreeNode<_ResolvedNotoSubset>? _resolvedNotoTreeRoot; - -Map<_NotoFont, _ResolvedNotoFont> _resolvedNotoFonts = - <_NotoFont, _ResolvedNotoFont>{}; diff --git a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart index 6a6c84c8322eb..2a5589d61ba80 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/fonts.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/fonts.dart @@ -24,24 +24,13 @@ class SkiaFontCollection { final Set registeredFamilies = {}; - final Map> familyToTypefaceMap = - >{}; - - final List globalFontFallbacks = []; - - final Map _fontFallbackCounts = {}; - Future ensureFontsLoaded() async { await _loadFonts(); fontProvider = canvasKit.TypefaceFontProvider.Make(); - familyToTypefaceMap.clear(); for (var font in _registeredFonts) { fontProvider.registerFont(font.bytes, font.flutterFamily); - familyToTypefaceMap - .putIfAbsent(font.flutterFamily, () => []) - .add(font.typeface); } } @@ -151,15 +140,6 @@ class SkiaFontCollection { return _RegisteredFont(bytes, family, actualFamily); } - void registerFallbackFont(String url, String family) { - _fontFallbackCounts.putIfAbsent(family, () => 0); - int fontFallbackTag = _fontFallbackCounts[family]!; - _fontFallbackCounts[family] = _fontFallbackCounts[family]! + 1; - String countedFamily = '$family $fontFallbackTag'; - _unloadedFonts.add(_registerFont(url, countedFamily)); - globalFontFallbacks.add(countedFamily); - } - String? _readActualFamilyName(Uint8List bytes) { final SkFontMgr tmpFontMgr = canvasKit.FontMgr.FromData([bytes])!; String? actualFamily = tmpFontMgr.getFamilyName(0); @@ -189,12 +169,5 @@ class _RegisteredFont { /// The font family that was parsed from the font's bytes. final String actualFamily; - /// The [SkTypeface] created from this font's [bytes]. - /// - /// This is used to determine which code points are supported by this font. - final SkTypeface typeface; - - _RegisteredFont(this.bytes, this.flutterFamily, this.actualFamily) - : this.typeface = - canvasKit.FontMgr.RefDefault().MakeTypefaceFromData(bytes); + _RegisteredFont(this.bytes, this.flutterFamily, this.actualFamily); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/image.dart b/lib/web_ui/lib/src/engine/canvaskit/image.dart index 3e7875b4b77be..3617924da5540 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/image.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/image.dart @@ -300,7 +300,7 @@ class CkImage implements ui.Image, StackTraceDebugger { width: skImage.width(), height: skImage.height(), ); - bytes = skImage.readPixels(0, 0, imageInfo); + bytes = skImage.readPixels(imageInfo, 0, 0); } else { final SkData skData = skImage.encodeToData(); //defaults to PNG 100% // make a copy that we can return diff --git a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart index b62eb96acebbe..20a9ecd952c2e 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart @@ -11,7 +11,8 @@ part of engine; external String? get requestedRendererType; /// Whether to use CanvasKit as the rendering backend. -bool get useCanvasKit => _autoDetect ? _detectRenderer() : _useSkia; +bool get useCanvasKit => + _autoDetect ? _detectRenderer() : _useSkia; /// Returns true if CanvasKit is used. /// @@ -40,13 +41,9 @@ const bool _autoDetect = const bool _useSkia = bool.fromEnvironment('FLUTTER_WEB_USE_SKIA', defaultValue: false); -/// If set to true, forces CPU-only rendering (i.e. no WebGL). -/// -/// This is mainly used for testing or for apps that want to ensure they -/// run on devices which don't support WebGL. -const bool canvasKitForceCpuOnly = bool.fromEnvironment( - 'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', - defaultValue: false); +// If set to true, forces CPU-only rendering (i.e. no WebGL). +const bool canvasKitForceCpuOnly = + bool.fromEnvironment('FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY', defaultValue: false); /// The URL to use when downloading the CanvasKit script and associated wasm. /// @@ -55,7 +52,7 @@ const bool canvasKitForceCpuOnly = bool.fromEnvironment( /// NPM, update this URL to `https://unpkg.com/canvaskit-wasm@0.34.0/bin/`. const String canvasKitBaseUrl = String.fromEnvironment( 'FLUTTER_WEB_CANVASKIT_URL', - defaultValue: 'https://unpkg.com/canvaskit-wasm@0.20.0/bin/', + defaultValue: 'https://unpkg.com/canvaskit-wasm@0.19.0/bin/', ); /// Initialize CanvasKit. @@ -66,10 +63,8 @@ Future initializeCanvasKit() { late StreamSubscription loadSubscription; loadSubscription = domRenderer.canvasKitScript!.onLoad.listen((_) { loadSubscription.cancel(); - final CanvasKitInitPromise canvasKitInitPromise = - CanvasKitInit(CanvasKitInitOptions( - locateFile: js.allowInterop( - (String file, String unusedBase) => canvasKitBaseUrl + file), + final CanvasKitInitPromise canvasKitInitPromise = CanvasKitInit(CanvasKitInitOptions( + locateFile: js.allowInterop((String file, String unusedBase) => canvasKitBaseUrl + file), )); canvasKitInitPromise.then(js.allowInterop((CanvasKit ck) { canvasKit = ck; diff --git a/lib/web_ui/lib/src/engine/canvaskit/path.dart b/lib/web_ui/lib/src/engine/canvaskit/path.dart index e8518dc6e6828..12626d5ec354e 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path.dart @@ -265,7 +265,7 @@ class CkPath extends ManagedSkiaObject implements ui.Path { ) { final CkPath path1 = uiPath1 as CkPath; final CkPath path2 = uiPath2 as CkPath; - final SkPath newPath = canvasKit.Path.MakeFromOp( + final SkPath newPath = canvasKit.MakePathFromOp( path1.skiaObject, path2.skiaObject, toSkPathOp(operation), @@ -320,7 +320,7 @@ class CkPath extends ManagedSkiaObject implements ui.Path { @override SkPath resurrect() { - final SkPath path = canvasKit.Path.MakeFromCmds(_cachedCommands!); + final SkPath path = canvasKit.MakePathFromCmds(_cachedCommands!); path.setFillType(toSkFillType(_fillType)); return path; } diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index 79339e4e527dd..cb4773abdaec2 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -62,7 +62,11 @@ class CkParagraphStyle implements ui.ParagraphStyle { skTextStyle.fontSize = fontSize; } - skTextStyle.fontFamilies = _getEffectiveFontFamilies(fontFamily); + if (fontFamily == null || + !skiaFontCollection.registeredFamilies.contains(fontFamily)) { + fontFamily = 'Roboto'; + } + skTextStyle.fontFamilies = [fontFamily]; return skTextStyle; } @@ -70,8 +74,20 @@ class CkParagraphStyle implements ui.ParagraphStyle { static SkStrutStyleProperties toSkStrutStyleProperties(ui.StrutStyle value) { EngineStrutStyle style = value as EngineStrutStyle; final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties(); - skStrutStyle.fontFamilies = - _getEffectiveFontFamilies(style._fontFamily, style._fontFamilyFallback); + if (style._fontFamily != null) { + String fontFamily = style._fontFamily!; + if (!skiaFontCollection.registeredFamilies.contains(fontFamily)) { + fontFamily = 'Roboto'; + } + final List fontFamilies = [fontFamily]; + if (style._fontFamilyFallback != null) { + fontFamilies.addAll(style._fontFamilyFallback!); + } + skStrutStyle.fontFamilies = fontFamilies; + } else { + // If no strut font family is given, default to Roboto. + skStrutStyle.fontFamilies = ['Roboto']; + } if (style._fontSize != null) { skStrutStyle.fontSize = style._fontSize; @@ -263,8 +279,18 @@ class CkTextStyle implements ui.TextStyle { properties.locale = locale.toLanguageTag(); } - properties.fontFamilies = - _getEffectiveFontFamilies(fontFamily, fontFamilyFallback); + if (fontFamily == null || + !skiaFontCollection.registeredFamilies.contains(fontFamily)) { + fontFamily = 'Roboto'; + } + + List fontFamilies = [fontFamily]; + if (fontFamilyFallback != null && + !fontFamilyFallback.every((font) => fontFamily == font)) { + fontFamilies.addAll(fontFamilyFallback); + } + + properties.fontFamilies = fontFamilies; if (fontWeight != null || fontStyle != null) { properties.fontStyle = toSkFontStyle(fontWeight, fontStyle); @@ -538,6 +564,7 @@ class CkParagraph extends ManagedSkiaObject @override void layout(ui.ParagraphConstraints constraints) { + assert(constraints.width != null); // ignore: unnecessary_null_comparison _lastLayoutConstraints = constraints; // TODO(het): CanvasKit throws an exception when laid out with @@ -633,65 +660,8 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { return properties; } - /// Determines if the given [text] contains any code points which are not - /// supported by the current set of fonts. - void _ensureFontsSupportText(String text) { - // TODO(hterkelsen): Make this faster for the common case where the text - // is supported by the given fonts. - - // If the text is ASCII, then skip this check. - bool isAscii = true; - for (int i = 0; i < text.length; i++) { - if (text.codeUnitAt(i) >= 160) { - isAscii = false; - break; - } - } - if (isAscii) { - return; - } - CkTextStyle style = _peekStyle(); - List fontFamilies = - _getEffectiveFontFamilies(style.fontFamily, style.fontFamilyFallback); - List typefaces = []; - for (var font in fontFamilies) { - List? typefacesForFamily = - skiaFontCollection.familyToTypefaceMap[font]; - if (typefacesForFamily != null) { - typefaces.addAll(typefacesForFamily); - } - } - // List codeUnits = text.codeUnits; - List codeUnitsSupported = List.filled(text.length, false); - for (SkTypeface typeface in typefaces) { - SkFont font = SkFont(typeface); - Uint8List glyphs = font.getGlyphIDs(text); - assert(glyphs.length == codeUnitsSupported.length); - for (int i = 0; i < glyphs.length; i++) { - codeUnitsSupported[i] |= - glyphs[i] != 0 || _isControlCode(text.codeUnitAt(i)); - } - } - - if (codeUnitsSupported.any((x) => !x)) { - List missingCodeUnits = []; - for (int i = 0; i < codeUnitsSupported.length; i++) { - if (!codeUnitsSupported[i]) { - missingCodeUnits.add(text.codeUnitAt(i)); - } - } - _findFontsForMissingCodeunits(missingCodeUnits); - } - } - - /// Returns [true] if [codepoint] is a Unicode control code. - bool _isControlCode(int codepoint) { - return codepoint < 32 || (codepoint > 127 && codepoint < 160); - } - @override void addText(String text) { - _ensureFontsSupportText(text); _commands.add(_ParagraphCommand.addText(text)); _paragraphBuilder.addText(text); } @@ -742,10 +712,8 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { _styleStack.add(skStyle); _commands.add(_ParagraphCommand.pushStyle(ckStyle)); if (skStyle.foreground != null || skStyle.background != null) { - final SkPaint foreground = - skStyle.foreground?.skiaObject ?? _defaultTextStylePaint; - final SkPaint background = - skStyle.background?.skiaObject ?? _defaultTextStylePaint; + final SkPaint foreground = skStyle.foreground?.skiaObject ?? _defaultTextStylePaint; + final SkPaint background = skStyle.background?.skiaObject ?? _defaultTextStylePaint; _paragraphBuilder.pushPaintStyle( skStyle.skTextStyle, foreground, background); } else { @@ -788,18 +756,3 @@ enum _ParagraphCommandType { pushStyle, addPlaceholder, } - -List _getEffectiveFontFamilies(String? fontFamily, - [List? fontFamilyFallback]) { - if (fontFamily == null || - !skiaFontCollection.registeredFamilies.contains(fontFamily)) { - fontFamily = 'Roboto'; - } - List fontFamilies = [fontFamily]; - if (fontFamilyFallback != null && - !fontFamilyFallback.every((font) => fontFamily == font)) { - fontFamilies.addAll(fontFamilyFallback); - } - fontFamilies.addAll(skiaFontCollection.globalFontFallbacks); - return fontFamilies; -} diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index 46743857615e7..b70182ac85b1d 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -836,7 +836,7 @@ void _pathTests() { expect(measure2, isNull); }); - test('SkPath.toCmds and CanvasKit.Path.MakeFromCmds', () { + test('SkPath.toCmds and CanvasKit.MakePathFromCmds', () { const ui.Rect rect = ui.Rect.fromLTRB(0, 0, 10, 10); final SkPath path = SkPath(); path.addRect(toSkRect(rect)); @@ -848,7 +848,7 @@ void _pathTests() { [5], // close ]); - final SkPath copy = canvasKit.Path.MakeFromCmds(path.toCmds()); + final SkPath copy = canvasKit.MakePathFromCmds(path.toCmds()); expect(fromSkRect(copy.getBounds()), rect); }); } @@ -1055,11 +1055,7 @@ void _canvasTests() { test('drawPoints', () { canvas.drawPoints( canvasKit.PointMode.Lines, - [ - Float32List.fromList([0, 0]), - Float32List.fromList([10, 10]), - Float32List.fromList([0, 10]), - ], + Float32List.fromList([0, 0, 10, 10, 0, 10]), SkPaint(), ); });