diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart index 342625347346966ccf39680e3e26ffda80cde633..06d325e3c1dfabf6ebafd72369b8bac7bb955a9f 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 { skCanvas.drawPicture(picture.skiaObject.skiaObject); } - void drawPoints(CkPaint paint, ui.PointMode pointMode, - Float32List points) { + void drawPoints(CkPaint paint, ui.PointMode pointMode, Float32List points) { skCanvas.drawPoints( toSkPointMode(pointMode), points, @@ -230,24 +229,24 @@ class CkCanvas { void saveLayer(ui.Rect bounds, CkPaint paint) { skCanvas.saveLayer( - toSkRect(bounds), paint.skiaObject, + toSkRect(bounds), + null, + null, ); } void saveLayerWithoutBounds(CkPaint paint) { - final SkCanvasSaveLayerWithoutBoundsOverload override = skCanvas as SkCanvasSaveLayerWithoutBoundsOverload; - override.saveLayer(paint.skiaObject); + skCanvas.saveLayer(paint.skiaObject, null, null, null); } void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) { - final SkCanvasSaveLayerWithFilterOverload override = skCanvas as SkCanvasSaveLayerWithFilterOverload; final CkImageFilter skImageFilter = filter as CkImageFilter; - return override.saveLayer( + return skCanvas.saveLayer( null, + toSkRect(bounds), skImageFilter.skiaObject, 0, - 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 1252cba16e436ebde9a037c82baca69f8b4ed7dc..09d67dbe25cbecb2717e2cd3515ad46fd45edf93 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart @@ -79,6 +79,10 @@ class CanvasKit { external int get LineThroughDecoration; // End of text decoration enum. + external SkTextDecorationStyleEnum get DecorationStyle; + external SkTextBaselineEnum get TextBaseline; + external SkPlaceholderAlignmentEnum get PlaceholderAlignment; + external SkFontMgrNamespace get SkFontMgr; external TypefaceFontProviderNamespace get TypefaceFontProvider; external int GetWebGLContext( @@ -1015,12 +1019,12 @@ class SkPath { external SkPath([SkPath? other]); external void setFillType(SkFillType fillType); external void addArc( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, ); external void addOval( - SkRect oval, + Float32List oval, bool counterClockWise, int startIndex, ); @@ -1041,16 +1045,15 @@ class SkPath { Float32List points, bool close, ); - external void addRoundRect( - SkRect outerRect, - Float32List radii, + external void addRRect( + Float32List rrect, bool counterClockWise, ); external void addRect( - SkRect rect, + Float32List rect, ); external void arcToOval( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, bool forceMoveTo, @@ -1084,7 +1087,7 @@ class SkPath { double x3, double y3, ); - external SkRect getBounds(); + external Float32List getBounds(); external void lineTo(double x, double y); external void moveTo(double x, double y); external void quadTo( @@ -1156,88 +1159,46 @@ class SkContourMeasure { external double length(); } -@JS() -@anonymous -class SkRect { - external factory SkRect({ - required double fLeft, - required double fTop, - required double fRight, - required double fBottom, - }); - external double get fLeft; - external double get fTop; - external double get fRight; - external double get fBottom; -} - -extension SkRectExtensions on SkRect { - ui.Rect toRect() { - return ui.Rect.fromLTRB( - this.fLeft, - this.fTop, - this.fRight, - this.fBottom, - ); - } -} - -SkRect toSkRect(ui.Rect rect) { - return SkRect( - fLeft: rect.left, - fTop: rect.top, - fRight: rect.right, - fBottom: rect.bottom, - ); -} - -@JS() -@anonymous -class SkRRect { - external factory SkRRect({ - required SkRect rect, - required double rx1, - required double ry1, - required double rx2, - required double ry2, - required double rx3, - required double ry3, - required double rx4, - required double ry4, - }); - - external SkRect get rect; - external double get rx1; - external double get ry1; - external double get rx2; - external double get ry2; - external double get rx3; - external double get ry3; - external double get rx4; - external double get ry4; -} - -SkRRect toSkRRect(ui.RRect rrect) { - return SkRRect( - rect: toOuterSkRect(rrect), - rx1: rrect.tlRadiusX, - ry1: rrect.tlRadiusY, - rx2: rrect.trRadiusX, - ry2: rrect.trRadiusY, - rx3: rrect.brRadiusX, - ry3: rrect.brRadiusY, - rx4: rrect.blRadiusX, - ry4: rrect.blRadiusY, - ); -} - -SkRect toOuterSkRect(ui.RRect rrect) { - return SkRect( - fLeft: rrect.left, - fTop: rrect.top, - fRight: rrect.right, - fBottom: rrect.bottom, - ); +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toSkRect(ui.Rect rect) { + final Float32List skRect = Float32List(4); + skRect[0] = rect.left; + skRect[1] = rect.top; + skRect[2] = rect.right; + skRect[3] = rect.bottom; + return skRect; +} + +ui.Rect fromSkRect(Float32List skRect) { + return ui.Rect.fromLTRB(skRect[0], skRect[1], skRect[2], skRect[3]); +} + +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toSkRRect(ui.RRect rrect) { + final Float32List skRRect = Float32List(12); + skRRect[0] = rrect.left; + skRRect[1] = rrect.top; + skRRect[2] = rrect.right; + skRRect[3] = rrect.bottom; + skRRect[4] = rrect.tlRadiusX; + skRRect[5] = rrect.tlRadiusY; + skRRect[6] = rrect.trRadiusX; + skRRect[7] = rrect.trRadiusY; + skRRect[8] = rrect.brRadiusX; + skRRect[9] = rrect.brRadiusY; + skRRect[10] = rrect.blRadiusX; + skRRect[11] = rrect.blRadiusY; + return skRRect; +} + +// TODO(hterkelsen): Use a shared malloc'ed array for performance. +Float32List toOuterSkRect(ui.RRect rrect) { + final Float32List skRect = Float32List(4); + skRect[0] = rrect.left; + skRect[1] = rrect.top; + skRect[2] = rrect.right; + skRect[3] = rrect.bottom; + return skRect; } /// Encodes a list of offsets to CanvasKit-compatible point array. @@ -1263,9 +1224,9 @@ List rawPointsToSkPoints2d(Float32List points) { assert(points.length % 2 == 0); final int pointLength = points.length ~/ 2; final List result = []; - for (var i = 0; i < pointLength; i++) { - var x = i * 2; - var y = x + 1; + for (int i = 0; i < pointLength; i++) { + int x = i * 2; + int y = x + 1; final Float32List skPoint = Float32List(2); skPoint[0] = points[x]; skPoint[1] = points[y]; @@ -1277,7 +1238,7 @@ List rawPointsToSkPoints2d(Float32List points) { List toSkPoints2d(List offsets) { final int len = offsets.length; final List result = []; - for (var i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { final ui.Offset offset = offsets[i]; final Float32List skPoint = Float32List(2); skPoint[0] = offset.dx; @@ -1299,7 +1260,7 @@ Uint16List toUint16List(List ints) { @JS('window.flutterCanvasKit.SkPictureRecorder') class SkPictureRecorder { external SkPictureRecorder(); - external SkCanvas beginRecording(SkRect bounds); + external SkCanvas beginRecording(Float32List bounds); external SkPicture finishRecordingAsPicture(); external void delete(); } @@ -1319,17 +1280,17 @@ class SkCanvas { bool doAntiAlias, ); external void clipRRect( - SkRRect rrect, + Float32List rrect, SkClipOp clipOp, bool doAntiAlias, ); external void clipRect( - SkRect rrect, + Float32List rrect, SkClipOp clipOp, bool doAntiAlias, ); external void drawArc( - SkRect oval, + Float32List oval, double startAngleDegrees, double sweepAngleDegrees, bool useCenter, @@ -1354,8 +1315,8 @@ class SkCanvas { SkBlendMode blendMode, ); external void drawDRRect( - SkRRect outer, - SkRRect inner, + Float32List outer, + Float32List inner, SkPaint paint, ); external void drawImage( @@ -1366,15 +1327,15 @@ class SkCanvas { ); external void drawImageRect( SkImage image, - SkRect src, - SkRect dst, + Float32List src, + Float32List dst, SkPaint paint, bool fastSample, ); external void drawImageNine( SkImage image, - SkRect center, - SkRect dst, + Float32List center, + Float32List dst, SkPaint paint, ); external void drawLine( @@ -1385,7 +1346,7 @@ class SkCanvas { SkPaint paint, ); external void drawOval( - SkRect rect, + Float32List rect, SkPaint paint, ); external void drawPaint( @@ -1401,11 +1362,11 @@ class SkCanvas { SkPaint paint, ); external void drawRRect( - SkRRect rrect, + Float32List rrect, SkPaint paint, ); external void drawRect( - SkRect rrect, + Float32List rrect, SkPaint paint, ); external void drawShadow( @@ -1425,8 +1386,10 @@ class SkCanvas { external int save(); external int getSaveCount(); external void saveLayer( - SkRect bounds, - SkPaint paint, + SkPaint? paint, + Float32List? bounds, + SkImageFilter? backdrop, + int? flags, ); external void restore(); external void restoreToCount(int count); @@ -1448,23 +1411,6 @@ class SkCanvas { ); } -@JS() -@anonymous -class SkCanvasSaveLayerWithoutBoundsOverload { - external void saveLayer(SkPaint paint); -} - -@JS() -@anonymous -class SkCanvasSaveLayerWithFilterOverload { - external void saveLayer( - SkPaint? paint, - SkImageFilter? imageFilter, - int flags, - SkRect rect, - ); -} - @JS() @anonymous class SkPicture { @@ -1493,6 +1439,7 @@ class SkParagraphBuilder { external void pushPaintStyle( SkTextStyle textStyle, SkPaint foreground, SkPaint background); external void pop(); + external void addPlaceholder(SkPlaceholderStyleProperties placeholderStyle); external SkParagraph build(); external void delete(); } @@ -1504,69 +1451,162 @@ class SkParagraphStyle {} @JS() @anonymous class SkParagraphStyleProperties { - external SkTextAlign? get textAlign; external set textAlign(SkTextAlign? value); - - external SkTextDirection? get textDirection; external set textDirection(SkTextDirection? value); - - external double? get heightMultiplier; external set heightMultiplier(double? value); - - external int? get textHeightBehavior; external set textHeightBehavior(int? value); - - external int? get maxLines; external set maxLines(int? value); - - external String? get ellipsis; external set ellipsis(String? value); - - external SkTextStyleProperties? get textStyle; external set textStyle(SkTextStyleProperties? value); + external set strutStyle(SkStrutStyleProperties? strutStyle); } @JS() class SkTextStyle {} +@JS() +class SkTextDecorationStyleEnum { + external SkTextDecorationStyle get Solid; + external SkTextDecorationStyle get Double; + external SkTextDecorationStyle get Dotted; + external SkTextDecorationStyle get Dashed; + external SkTextDecorationStyle get Wavy; +} + +@JS() +class SkTextDecorationStyle { + external int get value; +} + +final List _skTextDecorationStyles = + [ + canvasKit.DecorationStyle.Solid, + canvasKit.DecorationStyle.Double, + canvasKit.DecorationStyle.Dotted, + canvasKit.DecorationStyle.Dashed, + canvasKit.DecorationStyle.Wavy, +]; + +SkTextDecorationStyle toSkTextDecorationStyle(ui.TextDecorationStyle style) { + return _skTextDecorationStyles[style.index]; +} + +@JS() +class SkTextBaselineEnum { + external SkTextBaseline get Alphabetic; + external SkTextBaseline get Ideographic; +} + +@JS() +class SkTextBaseline { + external int get value; +} + +final List _skTextBaselines = [ + canvasKit.TextBaseline.Alphabetic, + canvasKit.TextBaseline.Ideographic, +]; + +SkTextBaseline toSkTextBaseline(ui.TextBaseline baseline) { + return _skTextBaselines[baseline.index]; +} + +@JS() +class SkPlaceholderAlignmentEnum { + external SkPlaceholderAlignment get Baseline; + external SkPlaceholderAlignment get AboveBaseline; + external SkPlaceholderAlignment get BelowBaseline; + external SkPlaceholderAlignment get Top; + external SkPlaceholderAlignment get Bottom; + external SkPlaceholderAlignment get Middle; +} + +@JS() +class SkPlaceholderAlignment { + external int get value; +} + +final List _skPlaceholderAlignments = + [ + canvasKit.PlaceholderAlignment.Baseline, + canvasKit.PlaceholderAlignment.AboveBaseline, + canvasKit.PlaceholderAlignment.BelowBaseline, + canvasKit.PlaceholderAlignment.Top, + canvasKit.PlaceholderAlignment.Bottom, + canvasKit.PlaceholderAlignment.Middle, +]; + +SkPlaceholderAlignment toSkPlaceholderAlignment( + ui.PlaceholderAlignment alignment) { + return _skPlaceholderAlignments[alignment.index]; +} + @JS() @anonymous class SkTextStyleProperties { - external Float32List? get backgroundColor; external set backgroundColor(Float32List? value); - - external Float32List? get color; external set color(Float32List? value); - - external Float32List? get foregroundColor; external set foregroundColor(Float32List? value); - - external int? get decoration; external set decoration(int? value); - - external double? get decorationThickness; external set decorationThickness(double? value); - - external double? get fontSize; + external set decorationColor(Float32List? value); + external set decorationStyle(SkTextDecorationStyle? value); + external set textBaseline(SkTextBaseline? value); external set fontSize(double? value); - - external List? get fontFamilies; + external set letterSpacing(double? value); + external set wordSpacing(double? value); + external set heightMultiplier(double? value); + external set locale(String? value); external set fontFamilies(List? value); + external set fontStyle(SkFontStyle? value); + external set shadows(List? value); + external set fontFeatures(List? value); +} - external SkFontStyle? get fontStyle; +@JS() +@anonymous +class SkStrutStyleProperties { + external set fontFamilies(List? value); external set fontStyle(SkFontStyle? value); + external set fontSize(double? value); + external set heightMultiplier(double? value); + external set leading(double? value); + external set strutEnabled(bool? value); + external set forceStrutHeight(bool? value); +} + +@JS() +@anonymous +class SkPlaceholderStyleProperties { + external set width(double? value); + external set height(double? value); + external set alignment(SkPlaceholderAlignment? value); + external set offset(double? value); + external set baseline(SkTextBaseline? value); } @JS() @anonymous class SkFontStyle { - external SkFontWeight? get weight; external set weight(SkFontWeight? value); - - external SkFontSlant? get slant; external set slant(SkFontSlant? value); } +@JS() +@anonymous +class SkTextShadow { + external set color(Float32List? value); + external set offset(Float32List? value); + external set blurRadius(double? value); +} + +@JS() +@anonymous +class SkFontFeature { + external set name(String? value); + external set value(int? value); +} + @JS() @anonymous class SkFontMgr { @@ -1591,12 +1631,13 @@ class SkParagraph { external double getMaxIntrinsicWidth(); external double getMinIntrinsicWidth(); external double getMaxWidth(); - external List getRectsForRange( + external List getRectsForRange( int start, int end, SkRectHeightStyle heightStyle, SkRectWidthStyle widthStyle, ); + external List getRectsForPlaceholders(); external SkTextPosition getGlyphPositionAtCoordinate( double x, double y, @@ -1649,7 +1690,8 @@ class TypefaceFontProviderNamespace { Timer? _skObjectCollector; List _skObjectDeleteQueue = []; -final SkObjectFinalizationRegistry skObjectFinalizationRegistry = SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { +final SkObjectFinalizationRegistry skObjectFinalizationRegistry = + SkObjectFinalizationRegistry(js.allowInterop((SkDeletable deletable) { _skObjectDeleteQueue.add(deletable); _skObjectCollector ??= _scheduleSkObjectCollection(); })); @@ -1668,19 +1710,20 @@ final SkObjectFinalizationRegistry skObjectFinalizationRegistry = SkObjectFinali /// yielding to the graphics system to render the frame on the screen if there /// is a large number of objects to delete, causing jank. Timer _scheduleSkObjectCollection() => Timer(Duration.zero, () { - html.window.performance.mark('SkObject collection-start'); - final int length = _skObjectDeleteQueue.length; - for (int i = 0; i < length; i++) { - _skObjectDeleteQueue[i].delete(); - } - _skObjectDeleteQueue = []; - - // Null out the timer so we can schedule a new one next time objects are - // scheduled for deletion. - _skObjectCollector = null; - html.window.performance.mark('SkObject collection-end'); - html.window.performance.measure('SkObject collection', 'SkObject collection-start', 'SkObject collection-end'); -}); + html.window.performance.mark('SkObject collection-start'); + final int length = _skObjectDeleteQueue.length; + for (int i = 0; i < length; i++) { + _skObjectDeleteQueue[i].delete(); + } + _skObjectDeleteQueue = []; + + // Null out the timer so we can schedule a new one next time objects are + // scheduled for deletion. + _skObjectCollector = null; + html.window.performance.mark('SkObject collection-end'); + html.window.performance.measure('SkObject collection', + 'SkObject collection-start', 'SkObject collection-end'); + }); /// Any Skia object that has a `delete` method. @JS() @@ -1716,7 +1759,8 @@ class SkObjectFinalizationRegistry { external Object? get _finalizationRegistryConstructor; /// Whether the current browser supports `FinalizationRegistry`. -bool browserSupportsFinalizationRegistry = _finalizationRegistryConstructor != null; +bool browserSupportsFinalizationRegistry = + _finalizationRegistryConstructor != null; @JS() class SkData { @@ -1741,7 +1785,7 @@ class SkImageInfo { external int get height; external bool get isEmpty; external bool get isOpaque; - external SkRect get bounds; + external Float32List get bounds; external int get width; external SkImageInfo makeAlphaType(SkAlphaType alphaType); external SkImageInfo makeColorSpace(SkColorSpace colorSpace); diff --git a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart index c1d91ad52592534230c4ec31f5bad65c13f79090..1bfe7cf97aa69b6494b5c62417ede6bbc81e1f6d 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/initialization.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/initialization.dart @@ -20,7 +20,7 @@ const bool canvasKitForceCpuOnly = /// 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.17.3/bin/', + defaultValue: 'https://unpkg.com/canvaskit-wasm@0.18.1/bin/', ); /// Initialize CanvasKit. diff --git a/lib/web_ui/lib/src/engine/canvaskit/path.dart b/lib/web_ui/lib/src/engine/canvaskit/path.dart index 86a758baf15fb053ab4137c752c7cdee47d3bd04..810c76e386a4943cfca73d925a0b5e8c01dcf87a 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/path.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/path.dart @@ -11,11 +11,15 @@ part of engine; class CkPath implements ui.Path { final SkPath _skPath; - CkPath() : _skPath = SkPath(), _fillType = ui.PathFillType.nonZero { + CkPath() + : _skPath = SkPath(), + _fillType = ui.PathFillType.nonZero { _skPath.setFillType(toSkFillType(_fillType)); } - CkPath.from(CkPath other) : _skPath = SkPath(other._skPath), _fillType = other.fillType { + CkPath.from(CkPath other) + : _skPath = SkPath(other._skPath), + _fillType = other.fillType { _skPath.setFillType(toSkFillType(_fillType)); } @@ -89,22 +93,10 @@ class CkPath implements ui.Path { @override void addRRect(ui.RRect rrect) { - final SkFloat32List skRadii = mallocFloat32List(8); - final Float32List radii = skRadii.toTypedArray(); - radii[0] = rrect.tlRadiusX; - radii[1] = rrect.tlRadiusY; - radii[2] = rrect.trRadiusX; - radii[3] = rrect.trRadiusY; - radii[4] = rrect.brRadiusX; - radii[5] = rrect.brRadiusY; - radii[6] = rrect.blRadiusX; - radii[7] = rrect.blRadiusY; - _skPath.addRoundRect( - toOuterSkRect(rrect), - radii, + _skPath.addRRect( + toSkRRect(rrect), false, ); - freeFloat32List(skRadii); } @override @@ -195,7 +187,7 @@ class CkPath implements ui.Path { } @override - ui.Rect getBounds() => _skPath.getBounds().toRect(); + ui.Rect getBounds() => fromSkRect(_skPath.getBounds()); @override void lineTo(double x, double y) { diff --git a/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart b/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart index fa6793d0f229701ef904805305c10d50c19af400..126ffd97648de659c454874d1cf638ca1a6b6776 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/picture_recorder.dart @@ -13,7 +13,7 @@ class CkPictureRecorder implements ui.PictureRecorder { CkCanvas beginRecording(ui.Rect bounds) { _cullRect = bounds; final SkPictureRecorder recorder = _skRecorder = SkPictureRecorder(); - final SkRect skRect = toSkRect(bounds); + final Float32List skRect = toSkRect(bounds); final SkCanvas skCanvas = recorder.beginRecording(skRect); return _recordingCanvas = CkCanvas(skCanvas); } diff --git a/lib/web_ui/lib/src/engine/canvaskit/text.dart b/lib/web_ui/lib/src/engine/canvaskit/text.dart index 146a9f0baf4e466bd77fcc5ee5d3e8a7f54a34ad..a033f78f2e521e85cb02d2fcaf0321fa41ebc71a 100644 --- a/lib/web_ui/lib/src/engine/canvaskit/text.dart +++ b/lib/web_ui/lib/src/engine/canvaskit/text.dart @@ -29,15 +29,23 @@ class CkParagraphStyle implements ui.ParagraphStyle { textHeightBehavior, fontWeight, fontStyle, + strutStyle, ellipsis, + locale, ) { _textDirection = textDirection ?? ui.TextDirection.ltr; _fontFamily = fontFamily; + _fontSize = fontSize; + _fontWeight = fontWeight; + _fontStyle = fontStyle; } SkParagraphStyle skParagraphStyle; ui.TextDirection? _textDirection; String? _fontFamily; + double? _fontSize; + ui.FontWeight? _fontWeight; + ui.FontStyle? _fontStyle; static SkTextStyleProperties toSkTextStyleProperties( String? fontFamily, @@ -63,6 +71,46 @@ class CkParagraphStyle implements ui.ParagraphStyle { return skTextStyle; } + static SkStrutStyleProperties toSkStrutStyleProperties(ui.StrutStyle value) { + EngineStrutStyle style = value as EngineStrutStyle; + final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties(); + if (style._fontFamily != null) { + final List fontFamilies = [style._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; + } + + if (style._height != null) { + skStrutStyle.heightMultiplier = style._height; + } + + if (style._leading != null) { + skStrutStyle.leading = style._leading; + } + + if (style._fontWeight != null || style._fontStyle != null) { + skStrutStyle.fontStyle = + toSkFontStyle(style._fontWeight, style._fontStyle); + } + + if (style._forceStrutHeight != null) { + skStrutStyle.forceStrutHeight = style._forceStrutHeight; + } + + skStrutStyle.strutEnabled = true; + + return skStrutStyle; + } + static SkParagraphStyle toSkParagraphStyle( ui.TextAlign? textAlign, ui.TextDirection? textDirection, @@ -73,7 +121,9 @@ class CkParagraphStyle implements ui.ParagraphStyle { ui.TextHeightBehavior? textHeightBehavior, ui.FontWeight? fontWeight, ui.FontStyle? fontStyle, + ui.StrutStyle? strutStyle, String? ellipsis, + ui.Locale? locale, ) { final SkParagraphStyleProperties properties = SkParagraphStyleProperties(); @@ -85,6 +135,10 @@ class CkParagraphStyle implements ui.ParagraphStyle { properties.textDirection = toSkTextDirection(textDirection); } + if (maxLines != null) { + properties.maxLines = maxLines; + } + if (height != null) { properties.heightMultiplier = height; } @@ -93,25 +147,52 @@ class CkParagraphStyle implements ui.ParagraphStyle { properties.textHeightBehavior = textHeightBehavior.encode(); } - if (maxLines != null) { - properties.maxLines = maxLines; - } - if (ellipsis != null) { properties.ellipsis = ellipsis; } + if (strutStyle != null) { + properties.strutStyle = toSkStrutStyleProperties(strutStyle); + } + properties.textStyle = toSkTextStyleProperties(fontFamily, fontSize, fontWeight, fontStyle); return canvasKit.ParagraphStyle(properties); } + + CkTextStyle getTextStyle() { + return CkTextStyle( + fontFamily: _fontFamily, + fontSize: _fontSize, + fontWeight: _fontWeight, + fontStyle: _fontStyle, + ); + } } class CkTextStyle implements ui.TextStyle { SkTextStyle skTextStyle; + + ui.Color? color; + ui.TextDecoration? decoration; + ui.Color? decorationColor; + ui.TextDecorationStyle? decorationStyle; + double? decorationThickness; + ui.FontWeight? fontWeight; + ui.FontStyle? fontStyle; + ui.TextBaseline? textBaseline; + String? fontFamily; + List? fontFamilyFallback; + double? fontSize; + double? letterSpacing; + double? wordSpacing; + double? height; + ui.Locale? locale; CkPaint? background; CkPaint? foreground; + List? shadows; + List? fontFeatures; factory CkTextStyle({ ui.Color? color, @@ -162,10 +243,38 @@ class CkTextStyle implements ui.TextStyle { properties.decorationThickness = decorationThickness; } + if (decorationColor != null) { + properties.decorationColor = makeFreshSkColor(decorationColor); + } + + if (decorationStyle != null) { + properties.decorationStyle = toSkTextDecorationStyle(decorationStyle); + } + + if (textBaseline != null) { + properties.textBaseline = toSkTextBaseline(textBaseline); + } + if (fontSize != null) { properties.fontSize = fontSize; } + if (letterSpacing != null) { + properties.letterSpacing = letterSpacing; + } + + if (wordSpacing != null) { + properties.wordSpacing = wordSpacing; + } + + if (height != null) { + properties.heightMultiplier = height; + } + + if (locale != null) { + properties.locale = locale.toLanguageTag(); + } + if (fontFamily == null || !skiaFontCollection.registeredFamilies.contains(fontFamily)) { fontFamily = 'Roboto'; @@ -187,21 +296,103 @@ class CkTextStyle implements ui.TextStyle { properties.foregroundColor = makeFreshSkColor(foreground.color); } - // TODO(hterkelsen): Add support for - // - decorationColor - // - decorationStyle - // - textBaseline - // - letterSpacing - // - wordSpacing - // - height - // - locale - // - shadows - // - fontFeatures + if (shadows != null) { + List ckShadows = []; + for (ui.Shadow shadow in shadows) { + final ckShadow = SkTextShadow(); + ckShadow.color = makeFreshSkColor(shadow.color); + ckShadow.offset = toSkPoint(shadow.offset); + ckShadow.blurRadius = shadow.blurRadius; + ckShadows.add(ckShadow); + } + properties.shadows = ckShadows; + } + + if (fontFeatures != null) { + List ckFontFeatures = []; + for (ui.FontFeature fontFeature in fontFeatures) { + SkFontFeature ckFontFeature = SkFontFeature(); + ckFontFeature.name = fontFeature.feature; + ckFontFeature.value = fontFeature.value; + ckFontFeatures.add(ckFontFeature); + } + properties.fontFeatures = ckFontFeatures; + } + return CkTextStyle._( - canvasKit.TextStyle(properties), foreground, background); + canvasKit.TextStyle(properties), + color, + decoration, + decorationColor, + decorationStyle, + decorationThickness, + fontWeight, + fontStyle, + textBaseline, + fontFamily, + fontFamilyFallback, + fontSize, + letterSpacing, + wordSpacing, + height, + locale, + background, + foreground, + shadows, + fontFeatures, + ); + } + + /// Merges this text style with [other] and returns the new text style. + /// + /// The values in this text style are used unless [other] specifically + /// overrides it. + CkTextStyle mergeWith(CkTextStyle other) { + return CkTextStyle( + color: other.color ?? color, + decoration: other.decoration ?? decoration, + decorationColor: other.decorationColor ?? decorationColor, + decorationStyle: other.decorationStyle ?? decorationStyle, + decorationThickness: other.decorationThickness ?? decorationThickness, + fontWeight: other.fontWeight ?? fontWeight, + fontStyle: other.fontStyle ?? fontStyle, + textBaseline: other.textBaseline ?? textBaseline, + fontFamily: other.fontFamily ?? fontFamily, + fontFamilyFallback: other.fontFamilyFallback ?? fontFamilyFallback, + fontSize: other.fontSize ?? fontSize, + letterSpacing: other.letterSpacing ?? letterSpacing, + wordSpacing: other.wordSpacing ?? wordSpacing, + height: other.height ?? height, + locale: other.locale ?? locale, + background: other.background ?? background, + foreground: other.foreground ?? foreground, + shadows: other.shadows ?? shadows, + fontFeatures: other.fontFeatures ?? fontFeatures, + ); } - CkTextStyle._(this.skTextStyle, this.foreground, this.background); + CkTextStyle._( + this.skTextStyle, + this.color, + this.decoration, + this.decorationColor, + this.decorationStyle, + this.decorationThickness, + this.fontWeight, + this.fontStyle, + this.textBaseline, + this.fontFamily, + this.fontFamilyFallback, + this.fontSize, + this.letterSpacing, + this.wordSpacing, + this.height, + this.locale, + this.background, + this.foreground, + this.shadows, + this.fontFeatures, + ); } SkFontStyle toSkFontStyle(ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) { @@ -260,6 +451,9 @@ class CkParagraph extends ManagedSkiaObject case _ParagraphCommandType.pushStyle: builder.pushStyle(command.style!); break; + case _ParagraphCommandType.addPlaceholder: + builder._addPlaceholder(command.placeholderStyle!); + break; } } @@ -304,10 +498,10 @@ class CkParagraph extends ManagedSkiaObject @override double get width => skiaObject.getMaxWidth(); - // TODO(hterkelsen): Implement placeholders once it's in CanvasKit @override List getBoxesForPlaceholders() { - return const []; + List> skRects = skiaObject.getRectsForPlaceholders(); + return skRectsToTextBoxes(skRects); } @override @@ -321,22 +515,26 @@ class CkParagraph extends ManagedSkiaObject return const []; } - List skRects = skiaObject.getRectsForRange( + List> skRects = skiaObject.getRectsForRange( start, end, toSkRectHeightStyle(boxHeightStyle), toSkRectWidthStyle(boxWidthStyle), ); + return skRectsToTextBoxes(skRects); + } + + List skRectsToTextBoxes(List> skRects) { List result = []; for (int i = 0; i < skRects.length; i++) { - final SkRect rect = skRects[i]; + final List rect = skRects[i]; result.add(ui.TextBox.fromLTRBD( - rect.fLeft, - rect.fTop, - rect.fRight, - rect.fBottom, + rect[0], + rect[1], + rect[2], + rect[3], _paragraphStyle._textDirection!, )); } @@ -404,16 +602,21 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { final SkParagraphBuilder _paragraphBuilder; final CkParagraphStyle _style; final List<_ParagraphCommand> _commands; + int _placeholderCount; + final List _placeholderScales; + final List _styleStack; CkParagraphBuilder(ui.ParagraphStyle style) : _commands = <_ParagraphCommand>[], _style = style as CkParagraphStyle, + _placeholderCount = 0, + _placeholderScales = [], + _styleStack = [], _paragraphBuilder = canvasKit.ParagraphBuilder.MakeFromFontProvider( style.skParagraphStyle, skiaFontCollection.fontProvider, ); - // TODO(hterkelsen): Implement placeholders. @override void addPlaceholder( double width, @@ -423,7 +626,44 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { double? baselineOffset, ui.TextBaseline? baseline, }) { - throw UnimplementedError('addPlaceholder'); + // Require a baseline to be specified if using a baseline-based alignment. + assert((alignment == ui.PlaceholderAlignment.aboveBaseline || + alignment == ui.PlaceholderAlignment.belowBaseline || + alignment == ui.PlaceholderAlignment.baseline) + ? baseline != null + : true); + + _placeholderCount++; + _placeholderScales.add(scale); + SkPlaceholderStyleProperties placeholderStyle = toSkPlaceholderStyle( + width * scale, + height * scale, + alignment, + (baselineOffset ?? height) * scale, + baseline ?? ui.TextBaseline.alphabetic, + ); + _addPlaceholder(placeholderStyle); + } + + void _addPlaceholder(SkPlaceholderStyleProperties placeholderStyle) { + _commands.add(_ParagraphCommand.addPlaceholder(placeholderStyle)); + _paragraphBuilder.addPlaceholder(placeholderStyle); + } + + static SkPlaceholderStyleProperties toSkPlaceholderStyle( + double width, + double height, + ui.PlaceholderAlignment alignment, + double baselineOffset, + ui.TextBaseline baseline, + ) { + final properties = SkPlaceholderStyleProperties(); + properties.width = width; + properties.height = height; + properties.alignment = toSkPlaceholderAlignment(alignment); + properties.offset = baselineOffset; + properties.baseline = toSkTextBaseline(baseline); + return properties; } @override @@ -446,22 +686,28 @@ class CkParagraphBuilder implements ui.ParagraphBuilder { } @override - int get placeholderCount => throw UnimplementedError('placeholderCount'); + int get placeholderCount => _placeholderCount; - // TODO(hterkelsen): Implement this once CanvasKit exposes placeholders. @override - List get placeholderScales => const []; + List get placeholderScales => _placeholderScales; @override void pop() { _commands.add(const _ParagraphCommand.pop()); + _styleStack.removeLast(); _paragraphBuilder.pop(); } + CkTextStyle _peekStyle() => + _styleStack.isEmpty ? _style.getTextStyle() : _styleStack.last; + @override void pushStyle(ui.TextStyle style) { - final CkTextStyle skStyle = style as CkTextStyle; - _commands.add(_ParagraphCommand.pushStyle(skStyle)); + final CkTextStyle baseStyle = _peekStyle(); + final CkTextStyle ckStyle = style as CkTextStyle; + final CkTextStyle skStyle = baseStyle.mergeWith(ckStyle); + _styleStack.add(skStyle); + _commands.add(_ParagraphCommand.pushStyle(ckStyle)); if (skStyle.foreground != null || skStyle.background != null) { final SkPaint foreground = skStyle.foreground?.skiaObject ?? SkPaint(); final SkPaint background = skStyle.background?.skiaObject ?? SkPaint(); @@ -477,20 +723,33 @@ class _ParagraphCommand { final _ParagraphCommandType type; final String? text; final CkTextStyle? style; + final SkPlaceholderStyleProperties? placeholderStyle; - const _ParagraphCommand._(this.type, this.text, this.style); + const _ParagraphCommand._( + this.type, + this.text, + this.style, + this.placeholderStyle, + ); const _ParagraphCommand.addText(String text) - : this._(_ParagraphCommandType.addText, text, null); + : this._(_ParagraphCommandType.addText, text, null, null); - const _ParagraphCommand.pop() : this._(_ParagraphCommandType.pop, null, null); + const _ParagraphCommand.pop() + : this._(_ParagraphCommandType.pop, null, null, null); const _ParagraphCommand.pushStyle(CkTextStyle style) - : this._(_ParagraphCommandType.pushStyle, null, style); + : this._(_ParagraphCommandType.pushStyle, null, style, null); + + const _ParagraphCommand.addPlaceholder( + SkPlaceholderStyleProperties placeholderStyle) + : this._( + _ParagraphCommandType.addPlaceholder, null, null, placeholderStyle); } enum _ParagraphCommandType { addText, pop, pushStyle, + addPlaceholder, } diff --git a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart index b03f3526db149b675122868243665b393add83f0..28e1c111984681c6c62ae043ff267fd989e2e4f6 100644 --- a/lib/web_ui/test/canvaskit/canvaskit_api_test.dart +++ b/lib/web_ui/test/canvaskit/canvaskit_api_test.dart @@ -51,7 +51,7 @@ void testMain() { _toSkPointTests(); _toSkColorStopsTests(); _toSkMatrixFromFloat32Tests(); - _skSkRectTests(); + _toSkRectTests(); _skVerticesTests(); group('SkPath', () { _pathTests(); @@ -59,7 +59,10 @@ void testMain() { group('SkCanvas', () { _canvasTests(); }); - // TODO: https://github.com/flutter/flutter/issues/60040 + group('SkParagraph', () { + _textStyleTests(); + }); + // TODO: https://github.com/flutter/flutter/issues/60040 }, skip: isIosSafari); } @@ -203,11 +206,13 @@ void _fillTypeTests() { void _pathOpTests() { test('path op mapping is correct', () { - expect(canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); + expect( + canvasKit.PathOp.Difference.value, ui.PathOperation.difference.index); expect(canvasKit.PathOp.Intersect.value, ui.PathOperation.intersect.index); expect(canvasKit.PathOp.Union.value, ui.PathOperation.union.index); expect(canvasKit.PathOp.XOR.value, ui.PathOperation.xor.index); - expect(canvasKit.PathOp.ReverseDifference.value, ui.PathOperation.reverseDifference.index); + expect(canvasKit.PathOp.ReverseDifference.value, + ui.PathOperation.reverseDifference.index); }); test('ui.PathOperation converts to SkPathOp', () { @@ -269,8 +274,10 @@ void _pointModeTests() { void _vertexModeTests() { test('vertex mode mapping is correct', () { expect(canvasKit.VertexMode.Triangles.value, ui.VertexMode.triangles.index); - expect(canvasKit.VertexMode.TrianglesStrip.value, ui.VertexMode.triangleStrip.index); - expect(canvasKit.VertexMode.TriangleFan.value, ui.VertexMode.triangleFan.index); + expect(canvasKit.VertexMode.TrianglesStrip.value, + ui.VertexMode.triangleStrip.index); + expect(canvasKit.VertexMode.TriangleFan.value, + ui.VertexMode.triangleFan.index); }); test('ui.VertexMode converts to SkVertexMode', () { @@ -282,7 +289,8 @@ void _vertexModeTests() { void _imageTests() { test('MakeAnimatedImageFromEncoded makes a non-animated image', () { - final SkAnimatedImage nonAnimated = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage nonAnimated = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); expect(nonAnimated.getFrameCount(), 1); expect(nonAnimated.getRepetitionCount(), 0); expect(nonAnimated.width(), 1); @@ -304,9 +312,10 @@ void _imageTests() { }); test('MakeAnimatedImageFromEncoded makes an animated image', () { - final SkAnimatedImage animated = canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif); + final SkAnimatedImage animated = + canvasKit.MakeAnimatedImageFromEncoded(kAnimatedGif); expect(animated.getFrameCount(), 3); - expect(animated.getRepetitionCount(), -1); // animates forever + expect(animated.getRepetitionCount(), -1); // animates forever expect(animated.width(), 1); expect(animated.height(), 1); for (int i = 0; i < 100; i++) { @@ -324,35 +333,39 @@ void _shaderTests() { }); test('MakeRadialGradient', () { - expect(canvasKit.SkShader.MakeRadialGradient( - Float32List.fromList([1, 1]), - 10.0, - [ - Float32List.fromList([0, 0, 0, 1]), - Float32List.fromList([1, 1, 1, 1]), - ], - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), isNotNull); + expect( + canvasKit.SkShader.MakeRadialGradient( + Float32List.fromList([1, 1]), + 10.0, + [ + Float32List.fromList([0, 0, 0, 1]), + Float32List.fromList([1, 1, 1, 1]), + ], + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull); }); test('MakeTwoPointConicalGradient', () { - expect(canvasKit.SkShader.MakeTwoPointConicalGradient( - Float32List.fromList([1, 1]), - 10.0, - Float32List.fromList([1, 1]), - 10.0, - [ - Float32List.fromList([0, 0, 0, 1]), - Float32List.fromList([1, 1, 1, 1]), - ], - Float32List.fromList([0, 1]), - canvasKit.TileMode.Repeat, - toSkMatrixFromFloat32(Matrix4.identity().storage), - 0, - ), isNotNull); + expect( + canvasKit.SkShader.MakeTwoPointConicalGradient( + Float32List.fromList([1, 1]), + 10.0, + Float32List.fromList([1, 1]), + 10.0, + [ + Float32List.fromList([0, 0, 0, 1]), + Float32List.fromList([1, 1, 1, 1]), + ], + Float32List.fromList([0, 1]), + canvasKit.TileMode.Repeat, + toSkMatrixFromFloat32(Matrix4.identity().storage), + 0, + ), + isNotNull); }); } @@ -398,11 +411,13 @@ void _paintTests() { void _maskFilterTests() { test('MakeBlurMaskFilter', () { - expect(canvasKit.MakeBlurMaskFilter( - canvasKit.BlurStyle.Outer, - 5.0, - false, - ), isNotNull); + expect( + canvasKit.MakeBlurMaskFilter( + canvasKit.BlurStyle.Outer, + 5.0, + false, + ), + isNotNull); }); } @@ -543,18 +558,44 @@ void _toSkMatrixFromFloat32Tests() { ..translate(1, 2, 3) ..rotateZ(4); expect( - toSkMatrixFromFloat32(matrix.storage), - Float32List.fromList([ - -0.6536436080932617, - 0.756802499294281, + toSkMatrixFromFloat32(matrix.storage), + Float32List.fromList([ + -0.6536436080932617, + 0.756802499294281, + 1, + -0.756802499294281, + -0.6536436080932617, + 2, + -0.0, + 0, + 1, + ])); + }); +} + +void _toSkRectTests() { + test('toSkRect', () { + expect(toSkRect(ui.Rect.fromLTRB(1, 2, 3, 4)), [1, 2, 3, 4]); + }); + + test('fromSkRect', () { + expect(fromSkRect(Float32List.fromList([1, 2, 3, 4])), + ui.Rect.fromLTRB(1, 2, 3, 4)); + }); + + test('toSkRRect', () { + expect( + toSkRRect(ui.RRect.fromLTRBAndCorners( 1, - -0.756802499294281, - -0.6536436080932617, 2, - -0.0, - 0, - 1, - ]) + 3, + 4, + topLeft: ui.Radius.elliptical(5, 6), + topRight: ui.Radius.elliptical(7, 8), + bottomRight: ui.Radius.elliptical(9, 10), + bottomLeft: ui.Radius.elliptical(11, 12), + )), + [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ); }); } @@ -581,7 +622,7 @@ void _pathTests() { test('addArc', () { path.addArc( - SkRect(fLeft: 10, fTop: 20, fRight: 30, fBottom: 40), + toSkRect(ui.Rect.fromLTRB(10, 20, 30, 40)), 1, 5, ); @@ -589,7 +630,7 @@ void _pathTests() { test('addOval', () { path.addOval( - SkRect(fLeft: 10, fTop: 20, fRight: 30, fBottom: 40), + toSkRect(ui.Rect.fromLTRB(10, 20, 30, 40)), false, 1, ); @@ -608,36 +649,24 @@ void _pathTests() { freeFloat32List(encodedPoints); }); - test('addRoundRect', () { + test('addRRect', () { final ui.RRect rrect = ui.RRect.fromRectAndRadius( ui.Rect.fromLTRB(10, 10, 20, 20), ui.Radius.circular(3), ); - final SkFloat32List skRadii = mallocFloat32List(8); - final Float32List radii = skRadii.toTypedArray(); - radii[0] = rrect.tlRadiusX; - radii[1] = rrect.tlRadiusY; - radii[2] = rrect.trRadiusX; - radii[3] = rrect.trRadiusY; - radii[4] = rrect.brRadiusX; - radii[5] = rrect.brRadiusY; - radii[6] = rrect.blRadiusX; - radii[7] = rrect.blRadiusY; - path.addRoundRect( - toOuterSkRect(rrect), - radii, + path.addRRect( + toSkRRect(rrect), false, ); - freeFloat32List(skRadii); }); test('addRect', () { - path.addRect(SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4)); + path.addRect(toSkRect(ui.Rect.fromLTRB(1, 2, 3, 4))); }); test('arcTo', () { path.arcToOval( - SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4), + toSkRect(ui.Rect.fromLTRB(1, 2, 3, 4)), 5, 40, false, @@ -676,7 +705,7 @@ void _pathTests() { test('getBounds', () { final SkPath testPath = _testClosedSkPath(); - final ui.Rect bounds = testPath.getBounds().toRect(); + final ui.Rect bounds = fromSkRect(testPath.getBounds()); expect(bounds, const ui.Rect.fromLTRB(10, 10, 20, 20)); }); @@ -726,13 +755,15 @@ void _pathTests() { test('reset', () { final SkPath testPath = _testClosedSkPath(); - expect(testPath.getBounds().toRect(), const ui.Rect.fromLTRB(10, 10, 20, 20)); + expect(fromSkRect(testPath.getBounds()), + const ui.Rect.fromLTRB(10, 10, 20, 20)); testPath.reset(); - expect(testPath.getBounds().toRect(), ui.Rect.zero); + expect(fromSkRect(testPath.getBounds()), ui.Rect.zero); }); test('toSVGString', () { - expect(_testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); + expect( + _testClosedSkPath().toSVGString(), 'M10 10L20 10L20 20L10 20L10 10Z'); }); test('isEmpty', () { @@ -743,22 +774,24 @@ void _pathTests() { test('copy', () { final SkPath original = _testClosedSkPath(); final SkPath copy = original.copy(); - expect(original.getBounds().toRect(), copy.getBounds().toRect()); + expect(fromSkRect(original.getBounds()), fromSkRect(copy.getBounds())); }); test('transform', () { path = _testClosedSkPath(); path.transform(2, 0, 10, 0, 2, 10, 0, 0, 0); - final ui.Rect transformedBounds = path.getBounds().toRect(); + final ui.Rect transformedBounds = fromSkRect(path.getBounds()); expect(transformedBounds, ui.Rect.fromLTRB(30, 30, 50, 50)); }); test('SkContourMeasureIter/SkContourMeasure', () { - final SkContourMeasureIter iter = SkContourMeasureIter(_testClosedSkPath(), false, 0); + final SkContourMeasureIter iter = + SkContourMeasureIter(_testClosedSkPath(), false, 0); final SkContourMeasure measure1 = iter.next(); expect(measure1.length(), 40); expect(measure1.getPosTan(5), Float32List.fromList([15, 10, 1, 0])); - expect(measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); + expect( + measure1.getPosTan(15), Float32List.fromList([20, 15, 0, 1])); expect(measure1.isClosed(), true); // Starting with a box path: @@ -783,29 +816,13 @@ void _pathTests() { // | | // 20 +-----------+ final SkPath segment = measure1.getSegment(5, 15, true); - expect(segment.getBounds().toRect(), ui.Rect.fromLTRB(15, 10, 20, 15)); + expect(fromSkRect(segment.getBounds()), ui.Rect.fromLTRB(15, 10, 20, 15)); final SkContourMeasure measure2 = iter.next(); expect(measure2, isNull); }); } -void _skSkRectTests() { - test('SkRect', () { - final SkRect rect = SkRect(fLeft: 1, fTop: 2, fRight: 3, fBottom: 4); - expect(rect.fLeft, 1); - expect(rect.fTop, 2); - expect(rect.fRight, 3); - expect(rect.fBottom, 4); - - final ui.Rect uiRect = rect.toRect(); - expect(uiRect.left, 1); - expect(uiRect.top, 2); - expect(uiRect.right, 3); - expect(uiRect.bottom, 4); - }); -} - SkVertices _testVertices() { return canvasKit.MakeSkVertices( canvasKit.VertexMode.Triangles, @@ -840,12 +857,8 @@ void _canvasTests() { setUp(() { recorder = SkPictureRecorder(); - canvas = recorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - )); + canvas = + recorder.beginRecording(toSkRect(ui.Rect.fromLTRB(0, 0, 100, 100))); }); tearDown(() { @@ -866,33 +879,23 @@ void _canvasTests() { test('saveLayer', () { canvas.saveLayer( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), SkPaint(), + toSkRect(ui.Rect.fromLTRB(0, 0, 100, 100)), + null, + null, ); }); - test('SkCanvasSaveLayerWithoutBoundsOverload.saveLayer', () { - final SkCanvasSaveLayerWithoutBoundsOverload override = canvas as SkCanvasSaveLayerWithoutBoundsOverload; - override.saveLayer(SkPaint()); + test('saveLayer without bounds', () { + canvas.saveLayer(SkPaint(), null, null, null); }); - test('SkCanvasSaveLayerWithFilterOverload.saveLayer', () { - final SkCanvasSaveLayerWithFilterOverload override = canvas as SkCanvasSaveLayerWithFilterOverload; - override.saveLayer( + test('saveLayer with filter', () { + canvas.saveLayer( SkPaint(), + toSkRect(ui.Rect.fromLTRB(0, 0, 100, 100)), canvasKit.SkImageFilter.MakeBlur(1, 2, canvasKit.TileMode.Repeat, null), 0, - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), ); }); @@ -910,22 +913,7 @@ void _canvasTests() { test('clipRRect', () { canvas.clipRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), canvasKit.ClipOp.Intersect, true, ); @@ -933,12 +921,7 @@ void _canvasTests() { test('clipRect', () { canvas.clipRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), + Float32List.fromList([0, 0, 100, 100]), canvasKit.ClipOp.Intersect, true, ); @@ -946,12 +929,7 @@ void _canvasTests() { test('drawArc', () { canvas.drawArc( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 50, - ), + Float32List.fromList([0, 0, 100, 50]), 0, 100, true, @@ -960,7 +938,8 @@ void _canvasTests() { }); test('drawAtlas', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); canvas.drawAtlas( image.getCurrentFrame(), Float32List.fromList([0, 0, 1, 1]), @@ -984,44 +963,15 @@ void _canvasTests() { test('drawDRRect', () { canvas.drawDRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), - SkRRect( - rect: SkRect( - fLeft: 20, - fTop: 20, - fRight: 80, - fBottom: 80, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), + Float32List.fromList([20, 20, 80, 80, 1, 2, 3, 4, 5, 6, 7, 8]), SkPaint(), ); }); test('drawImage', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); canvas.drawImage( image.getCurrentFrame(), 10, @@ -1031,22 +981,24 @@ void _canvasTests() { }); test('drawImageRect', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); canvas.drawImageRect( image.getCurrentFrame(), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([0, 0, 1, 1]), SkPaint(), false, ); }); test('drawImageNine', () { - final SkAnimatedImage image = canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); + final SkAnimatedImage image = + canvasKit.MakeAnimatedImageFromEncoded(kTransparentImage); canvas.drawImageNine( image.getCurrentFrame(), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), - SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), + Float32List.fromList([0, 0, 1, 1]), + Float32List.fromList([0, 0, 1, 1]), SkPaint(), ); }); @@ -1056,7 +1008,7 @@ void _canvasTests() { }); test('drawOval', () { - canvas.drawOval(SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), SkPaint()); + canvas.drawOval(Float32List.fromList([0, 0, 1, 1]), SkPaint()); }); test('drawPaint', () { @@ -1080,34 +1032,14 @@ void _canvasTests() { test('drawRRect', () { canvas.drawRRect( - SkRRect( - rect: SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), - rx1: 1, - ry1: 2, - rx2: 3, - ry2: 4, - rx3: 5, - ry3: 6, - rx4: 7, - ry4: 8, - ), + Float32List.fromList([0, 0, 100, 100, 1, 2, 3, 4, 5, 6, 7, 8]), SkPaint(), ); }); test('drawRect', () { canvas.drawRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - ), + Float32List.fromList([0, 0, 100, 100]), SkPaint(), ); }); @@ -1120,12 +1052,13 @@ void _canvasTests() { const double spotAlpha = 0.25; final SkPath path = _testClosedSkPath(); - final ui.Rect bounds = path.getBounds().toRect(); + final ui.Rect bounds = fromSkRect(path.getBounds()); final double shadowX = (bounds.left + bounds.right) / 2.0; final double shadowY = bounds.top - 600.0; const ui.Color color = ui.Color(0xAABBCCDD); - ui.Color inAmbient = color.withAlpha((color.alpha * ambientAlpha).round()); + ui.Color inAmbient = + color.withAlpha((color.alpha * ambientAlpha).round()); ui.Color inSpot = color.withAlpha((color.alpha * spotAlpha).round()); final SkTonalColors inTonalColors = SkTonalColors( @@ -1138,8 +1071,7 @@ void _canvasTests() { canvas.drawShadow( path, - Float32List(3) - ..[2] = devicePixelRatio * elevation, + Float32List(3)..[2] = devicePixelRatio * elevation, Float32List(3) ..[0] = shadowX ..[1] = shadowY @@ -1186,12 +1118,8 @@ void _canvasTests() { test('drawPicture', () { final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 100, - fBottom: 100, - )); + final SkCanvas otherCanvas = + otherRecorder.beginRecording(Float32List.fromList([0, 0, 100, 100])); otherCanvas.drawLine(0, 0, 10, 10, SkPaint()); canvas.drawPicture(otherRecorder.finishRecordingAsPicture()); }); @@ -1212,26 +1140,79 @@ void _canvasTests() { test('toImage.toByteData', () async { final SkPictureRecorder otherRecorder = SkPictureRecorder(); - final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect( - fLeft: 0, - fTop: 0, - fRight: 1, - fBottom: 1, - )); + final SkCanvas otherCanvas = + otherRecorder.beginRecording(Float32List.fromList([0, 0, 1, 1])); otherCanvas.drawRect( - SkRect( - fLeft: 0, - fTop: 0, - fRight: 1, - fBottom: 1, - ), + Float32List.fromList([0, 0, 1, 1]), SkPaint(), ); - final CkPicture picture = CkPicture(otherRecorder.finishRecordingAsPicture(), null); + final CkPicture picture = + CkPicture(otherRecorder.finishRecordingAsPicture(), null); final CkImage image = await picture.toImage(1, 1); - final ByteData rawData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); + final ByteData rawData = + await image.toByteData(format: ui.ImageByteFormat.rawRgba); expect(rawData, isNotNull); - final ByteData pngData = await image.toByteData(format: ui.ImageByteFormat.png); + final ByteData pngData = + await image.toByteData(format: ui.ImageByteFormat.png); expect(pngData, isNotNull); }); } + +void _textStyleTests() { + test('SkTextDecorationStyle mapping is correct', () { + expect(canvasKit.DecorationStyle.Solid.value, + ui.TextDecorationStyle.solid.index); + expect(canvasKit.DecorationStyle.Double.value, + ui.TextDecorationStyle.double.index); + expect(canvasKit.DecorationStyle.Dotted.value, + ui.TextDecorationStyle.dotted.index); + expect(canvasKit.DecorationStyle.Dashed.value, + ui.TextDecorationStyle.dashed.index); + expect(canvasKit.DecorationStyle.Wavy.value, + ui.TextDecorationStyle.wavy.index); + }); + + test('ui.TextDecorationStyle converts to SkTextDecorationStyle', () { + for (ui.TextDecorationStyle decorationStyle + in ui.TextDecorationStyle.values) { + expect(toSkTextDecorationStyle(decorationStyle).value, + decorationStyle.index); + } + }); + + test('SkTextBaseline mapping is correct', () { + expect(canvasKit.TextBaseline.Alphabetic.value, + ui.TextBaseline.alphabetic.index); + expect(canvasKit.TextBaseline.Ideographic.value, + ui.TextBaseline.ideographic.index); + }); + + test('ui.TextBaseline converts to SkTextBaseline', () { + for (ui.TextBaseline textBaseline in ui.TextBaseline.values) { + expect(toSkTextBaseline(textBaseline).value, textBaseline.index); + } + }); + + test('SkPlaceholderAlignment mapping is correct', () { + expect(canvasKit.PlaceholderAlignment.Baseline.value, + ui.PlaceholderAlignment.baseline.index); + expect(canvasKit.PlaceholderAlignment.AboveBaseline.value, + ui.PlaceholderAlignment.aboveBaseline.index); + expect(canvasKit.PlaceholderAlignment.BelowBaseline.value, + ui.PlaceholderAlignment.belowBaseline.index); + expect(canvasKit.PlaceholderAlignment.Top.value, + ui.PlaceholderAlignment.top.index); + expect(canvasKit.PlaceholderAlignment.Bottom.value, + ui.PlaceholderAlignment.bottom.index); + expect(canvasKit.PlaceholderAlignment.Middle.value, + ui.PlaceholderAlignment.middle.index); + }); + + test('ui.PlaceholderAlignment converts to SkPlaceholderAlignment', () { + for (ui.PlaceholderAlignment placeholderAlignment + in ui.PlaceholderAlignment.values) { + expect(toSkPlaceholderAlignment(placeholderAlignment).value, + placeholderAlignment.index); + } + }); +}