Add more TextStyle support to Paragraph in CanvasKit mode (#21629)
* WIP on Paragraph
* WIP skparagraph
* Add more Paragraph features in CanvasKit mode
* Fix addRoundRect test
* Respond to review comments
* Remove unused (and potentially harmful) getters from Sk classes
diff --git a/lib/web_ui/lib/src/engine/canvaskit/canvas.dart b/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
index 3426253..06d325e 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 @@
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 @@
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 1252cba..09d67db 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 @@
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 @@
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 @@
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 @@
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 @@
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;
+// 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;
}
-extension SkRectExtensions on SkRect {
- ui.Rect toRect() {
- return ui.Rect.fromLTRB(
- this.fLeft,
- this.fTop,
- this.fRight,
- this.fBottom,
- );
- }
+ui.Rect fromSkRect(Float32List skRect) {
+ return ui.Rect.fromLTRB(skRect[0], skRect[1], skRect[2], skRect[3]);
}
-SkRect toSkRect(ui.Rect rect) {
- return SkRect(
- fLeft: rect.left,
- fTop: rect.top,
- fRight: rect.right,
- fBottom: rect.bottom,
- );
+// 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;
}
-@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 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 @@
assert(points.length % 2 == 0);
final int pointLength = points.length ~/ 2;
final List<Float32List> result = <Float32List>[];
- 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<Float32List> toSkPoints2d(List<ui.Offset> offsets) {
final int len = offsets.length;
final List<Float32List> result = <Float32List>[];
- 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 @@
@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 @@
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 @@
SkBlendMode blendMode,
);
external void drawDRRect(
- SkRRect outer,
- SkRRect inner,
+ Float32List outer,
+ Float32List inner,
SkPaint paint,
);
external void drawImage(
@@ -1366,15 +1327,15 @@
);
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 @@
SkPaint paint,
);
external void drawOval(
- SkRect rect,
+ Float32List rect,
SkPaint paint,
);
external void drawPaint(
@@ -1401,11 +1362,11 @@
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 @@
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);
@@ -1450,23 +1413,6 @@
@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 {
external void delete();
}
@@ -1493,6 +1439,7 @@
external void pushPaintStyle(
SkTextStyle textStyle, SkPaint foreground, SkPaint background);
external void pop();
+ external void addPlaceholder(SkPlaceholderStyleProperties placeholderStyle);
external SkParagraph build();
external void delete();
}
@@ -1504,71 +1451,164 @@
@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<SkTextDecorationStyle> _skTextDecorationStyles =
+ <SkTextDecorationStyle>[
+ 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<SkTextBaseline> _skTextBaselines = <SkTextBaseline>[
+ 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<SkPlaceholderAlignment> _skPlaceholderAlignments =
+ <SkPlaceholderAlignment>[
+ 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<String>? 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<String>? value);
-
- external SkFontStyle? get fontStyle;
external set fontStyle(SkFontStyle? value);
+ external set shadows(List<SkTextShadow>? value);
+ external set fontFeatures(List<SkFontFeature>? value);
+}
+
+@JS()
+@anonymous
+class SkStrutStyleProperties {
+ external set fontFamilies(List<String>? 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 {
external String? getFamilyName(int fontId);
external void delete();
@@ -1591,12 +1631,13 @@
external double getMaxIntrinsicWidth();
external double getMinIntrinsicWidth();
external double getMaxWidth();
- external List<SkRect> getRectsForRange(
+ external List<Float32List> getRectsForRange(
int start,
int end,
SkRectHeightStyle heightStyle,
SkRectWidthStyle widthStyle,
);
+ external List<Float32List> getRectsForPlaceholders();
external SkTextPosition getGlyphPositionAtCoordinate(
double x,
double y,
@@ -1649,7 +1690,8 @@
Timer? _skObjectCollector;
List<SkDeletable> _skObjectDeleteQueue = <SkDeletable>[];
-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 @@
/// 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 = <SkDeletable>[];
+ html.window.performance.mark('SkObject collection-start');
+ final int length = _skObjectDeleteQueue.length;
+ for (int i = 0; i < length; i++) {
+ _skObjectDeleteQueue[i].delete();
+ }
+ _skObjectDeleteQueue = <SkDeletable>[];
- // 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');
-});
+ // 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 @@
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 @@
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 c1d91ad..1bfe7cf 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 @@
/// 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 86a758b..810c76e 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 @@
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 @@
@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 @@
}
@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 fa6793d..126ffd9 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 @@
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 146a9f0..a033f78 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 @@
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 @@
return skTextStyle;
}
+ static SkStrutStyleProperties toSkStrutStyleProperties(ui.StrutStyle value) {
+ EngineStrutStyle style = value as EngineStrutStyle;
+ final SkStrutStyleProperties skStrutStyle = SkStrutStyleProperties();
+ if (style._fontFamily != null) {
+ final List<String> fontFamilies = <String>[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 @@
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 @@
properties.textDirection = toSkTextDirection(textDirection);
}
+ if (maxLines != null) {
+ properties.maxLines = maxLines;
+ }
+
if (height != null) {
properties.heightMultiplier = height;
}
@@ -93,25 +147,52 @@
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<String>? fontFamilyFallback;
+ double? fontSize;
+ double? letterSpacing;
+ double? wordSpacing;
+ double? height;
+ ui.Locale? locale;
CkPaint? background;
CkPaint? foreground;
+ List<ui.Shadow>? shadows;
+ List<ui.FontFeature>? fontFeatures;
factory CkTextStyle({
ui.Color? color,
@@ -162,10 +243,38 @@
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 @@
properties.foregroundColor = makeFreshSkColor(foreground.color);
}
- // TODO(hterkelsen): Add support for
- // - decorationColor
- // - decorationStyle
- // - textBaseline
- // - letterSpacing
- // - wordSpacing
- // - height
- // - locale
- // - shadows
- // - fontFeatures
+ if (shadows != null) {
+ List<SkTextShadow> ckShadows = <SkTextShadow>[];
+ 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<SkFontFeature> ckFontFeatures = <SkFontFeature>[];
+ 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,
+ );
}
- CkTextStyle._(this.skTextStyle, this.foreground, this.background);
+ /// 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.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 @@
case _ParagraphCommandType.pushStyle:
builder.pushStyle(command.style!);
break;
+ case _ParagraphCommandType.addPlaceholder:
+ builder._addPlaceholder(command.placeholderStyle!);
+ break;
}
}
@@ -304,10 +498,10 @@
@override
double get width => skiaObject.getMaxWidth();
- // TODO(hterkelsen): Implement placeholders once it's in CanvasKit
@override
List<ui.TextBox> getBoxesForPlaceholders() {
- return const <ui.TextBox>[];
+ List<List<double>> skRects = skiaObject.getRectsForPlaceholders();
+ return skRectsToTextBoxes(skRects);
}
@override
@@ -321,22 +515,26 @@
return const <ui.TextBox>[];
}
- List<SkRect> skRects = skiaObject.getRectsForRange(
+ List<List<double>> skRects = skiaObject.getRectsForRange(
start,
end,
toSkRectHeightStyle(boxHeightStyle),
toSkRectWidthStyle(boxWidthStyle),
);
+ return skRectsToTextBoxes(skRects);
+ }
+
+ List<ui.TextBox> skRectsToTextBoxes(List<List<double>> skRects) {
List<ui.TextBox> result = <ui.TextBox>[];
for (int i = 0; i < skRects.length; i++) {
- final SkRect rect = skRects[i];
+ final List<double> 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 @@
final SkParagraphBuilder _paragraphBuilder;
final CkParagraphStyle _style;
final List<_ParagraphCommand> _commands;
+ int _placeholderCount;
+ final List<double> _placeholderScales;
+ final List<CkTextStyle> _styleStack;
CkParagraphBuilder(ui.ParagraphStyle style)
: _commands = <_ParagraphCommand>[],
_style = style as CkParagraphStyle,
+ _placeholderCount = 0,
+ _placeholderScales = <double>[],
+ _styleStack = <CkTextStyle>[],
_paragraphBuilder = canvasKit.ParagraphBuilder.MakeFromFontProvider(
style.skParagraphStyle,
skiaFontCollection.fontProvider,
);
- // TODO(hterkelsen): Implement placeholders.
@override
void addPlaceholder(
double width,
@@ -423,7 +626,44 @@
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 @@
}
@override
- int get placeholderCount => throw UnimplementedError('placeholderCount');
+ int get placeholderCount => _placeholderCount;
- // TODO(hterkelsen): Implement this once CanvasKit exposes placeholders.
@override
- List<double> get placeholderScales => const <double>[];
+ List<double> 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 @@
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 b03f352..28e1c11 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 @@
_toSkPointTests();
_toSkColorStopsTests();
_toSkMatrixFromFloat32Tests();
- _skSkRectTests();
+ _toSkRectTests();
_skVerticesTests();
group('SkPath', () {
_pathTests();
@@ -59,7 +59,10 @@
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 _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 _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 _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 @@
});
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 @@
});
test('MakeRadialGradient', () {
- expect(canvasKit.SkShader.MakeRadialGradient(
- Float32List.fromList([1, 1]),
- 10.0,
- <Float32List>[
- 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>[
+ 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>[
- 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>[
+ 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 _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 @@
..translate(1, 2, 3)
..rotateZ(4);
expect(
- toSkMatrixFromFloat32(matrix.storage),
- Float32List.fromList(<double>[
- -0.6536436080932617,
- 0.756802499294281,
+ toSkMatrixFromFloat32(matrix.storage),
+ Float32List.fromList(<double>[
+ -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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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(<double>[15, 10, 1, 0]));
- expect(measure1.getPosTan(15), Float32List.fromList(<double>[20, 15, 0, 1]));
+ expect(
+ measure1.getPosTan(15), Float32List.fromList(<double>[20, 15, 0, 1]));
expect(measure1.isClosed(), true);
// Starting with a box path:
@@ -783,29 +816,13 @@
// | |
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
});
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 @@
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 @@
});
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 @@
});
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 @@
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 @@
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 @@
canvas.drawShadow(
path,
- Float32List(3)
- ..[2] = devicePixelRatio * elevation,
+ Float32List(3)..[2] = devicePixelRatio * elevation,
Float32List(3)
..[0] = shadowX
..[1] = shadowY
@@ -1186,12 +1118,8 @@
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 @@
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);
+ }
+ });
+}