未验证 提交 ddf978b8 编写于 作者: H Harry Terkelsen 提交者: GitHub

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
上级 8ec9b3a1
......@@ -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),
);
}
......
......@@ -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<Float32List> rawPointsToSkPoints2d(Float32List points) {
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> rawPointsToSkPoints2d(Float32List points) {
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 @@ Uint16List toUint16List(List<int> 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<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 set fontStyle(SkFontStyle? value);
external set shadows(List<SkTextShadow>? value);
external set fontFeatures(List<SkFontFeature>? value);
}
external SkFontStyle? get fontStyle;
@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 {
......@@ -1591,12 +1631,13 @@ class SkParagraph {
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 @@ class TypefaceFontProviderNamespace {
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 @@ 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 = <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');
});
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');
});
/// 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);
......
......@@ -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.
......
......@@ -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) {
......
......@@ -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);
}
......
......@@ -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<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 @@ 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<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 @@ 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<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,
);
}
/// 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<SkParagraph>
case _ParagraphCommandType.pushStyle:
builder.pushStyle(command.style!);
break;
case _ParagraphCommandType.addPlaceholder:
builder._addPlaceholder(command.placeholderStyle!);
break;
}
}
......@@ -304,10 +498,10 @@ class CkParagraph extends ManagedSkiaObject<SkParagraph>
@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 @@ class CkParagraph extends ManagedSkiaObject<SkParagraph>
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 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {
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 @@ 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<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 @@ 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,
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册