mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Move CkCanvas to new JS-interop (#19748)
* Move CkCanvas to new JS-interop * Make SkiaObject compatible with JS-interop; text bindings * fix drawVertices
This commit is contained in:
parent
5a25a28174
commit
94b23abaae
@ -6,49 +6,41 @@ part of engine;
|
||||
|
||||
/// A Dart wrapper around Skia's SKCanvas.
|
||||
class CkCanvas {
|
||||
final js.JsObject skCanvas;
|
||||
final SkCanvas skCanvas;
|
||||
|
||||
CkCanvas(this.skCanvas);
|
||||
|
||||
int? get saveCount => skCanvas.callMethod('getSaveCount');
|
||||
int? get saveCount => skCanvas.getSaveCount();
|
||||
|
||||
void clear(ui.Color color) {
|
||||
setSharedSkColor1(color);
|
||||
skCanvas.callMethod('clear', <js.JsObject?>[sharedSkColor1]);
|
||||
skCanvas.clear(toSharedSkColor1(color));
|
||||
}
|
||||
|
||||
static final SkClipOp _clipOpIntersect = canvasKitJs.ClipOp.Intersect;
|
||||
|
||||
void clipPath(ui.Path path, bool doAntiAlias) {
|
||||
final CkPath skPath = path as CkPath;
|
||||
final js.JsObject? intersectClipOp = canvasKit['ClipOp']['Intersect'];
|
||||
skCanvas.callMethod('clipPath', <dynamic>[
|
||||
skPath._legacyJsObject,
|
||||
intersectClipOp,
|
||||
final CkPath ckPath = path as CkPath;
|
||||
skCanvas.clipPath(
|
||||
ckPath._skPath,
|
||||
_clipOpIntersect,
|
||||
doAntiAlias,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void clipRRect(ui.RRect rrect, bool doAntiAlias) {
|
||||
final js.JsObject? intersectClipOp = canvasKit['ClipOp']['Intersect'];
|
||||
skCanvas.callMethod('clipRRect', <dynamic>[
|
||||
makeSkRRect(rrect),
|
||||
intersectClipOp,
|
||||
skCanvas.clipRRect(
|
||||
toSkRRect(rrect),
|
||||
_clipOpIntersect,
|
||||
doAntiAlias,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void clipRect(ui.Rect rect, ui.ClipOp clipOp, bool doAntiAlias) {
|
||||
js.JsObject? skClipOp;
|
||||
switch (clipOp) {
|
||||
case ui.ClipOp.difference:
|
||||
skClipOp = canvasKit['ClipOp']['Difference'];
|
||||
break;
|
||||
case ui.ClipOp.intersect:
|
||||
skClipOp = canvasKit['ClipOp']['Intersect'];
|
||||
break;
|
||||
}
|
||||
|
||||
skCanvas.callMethod(
|
||||
'clipRect', <dynamic>[makeSkRect(rect), skClipOp, doAntiAlias]);
|
||||
skCanvas.clipRect(
|
||||
toSkRect(rect),
|
||||
toSkClipOp(clipOp),
|
||||
doAntiAlias,
|
||||
);
|
||||
}
|
||||
|
||||
void drawArc(
|
||||
@ -59,13 +51,13 @@ class CkCanvas {
|
||||
CkPaint paint,
|
||||
) {
|
||||
const double toDegrees = 180 / math.pi;
|
||||
skCanvas.callMethod('drawArc', <dynamic>[
|
||||
makeSkRect(oval),
|
||||
skCanvas.drawArc(
|
||||
toSkRect(oval),
|
||||
startAngle * toDegrees,
|
||||
sweepAngle * toDegrees,
|
||||
useCenter,
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawAtlasRaw(
|
||||
@ -73,140 +65,131 @@ class CkCanvas {
|
||||
ui.Image atlas,
|
||||
Float32List rstTransforms,
|
||||
Float32List rects,
|
||||
js.JsArray<Float32List>? colors,
|
||||
List<Float32List>? colors,
|
||||
ui.BlendMode blendMode,
|
||||
) {
|
||||
final CkImage skAtlas = atlas as CkImage;
|
||||
skCanvas.callMethod('drawAtlas', <dynamic>[
|
||||
skAtlas.legacyJsObject,
|
||||
skCanvas.drawAtlas(
|
||||
skAtlas.skImage,
|
||||
rects,
|
||||
rstTransforms,
|
||||
paint.skiaObject,
|
||||
makeSkBlendMode(blendMode),
|
||||
toSkBlendMode(blendMode),
|
||||
colors,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawCircle(ui.Offset c, double radius, CkPaint paint) {
|
||||
skCanvas.callMethod('drawCircle', <dynamic>[
|
||||
skCanvas.drawCircle(
|
||||
c.dx,
|
||||
c.dy,
|
||||
radius,
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawColor(ui.Color color, ui.BlendMode blendMode) {
|
||||
skCanvas.callMethod('drawColorInt', <dynamic>[
|
||||
skCanvas.drawColorInt(
|
||||
color.value,
|
||||
makeSkBlendMode(blendMode),
|
||||
]);
|
||||
toSkBlendMode(blendMode),
|
||||
);
|
||||
}
|
||||
|
||||
void drawDRRect(ui.RRect outer, ui.RRect inner, CkPaint paint) {
|
||||
skCanvas.callMethod('drawDRRect', <js.JsObject?>[
|
||||
makeSkRRect(outer),
|
||||
makeSkRRect(inner),
|
||||
skCanvas.drawDRRect(
|
||||
toSkRRect(outer),
|
||||
toSkRRect(inner),
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawImage(ui.Image image, ui.Offset offset, CkPaint paint) {
|
||||
final CkImage skImage = image as CkImage;
|
||||
skCanvas.callMethod('drawImage', <dynamic>[
|
||||
skImage.legacyJsObject,
|
||||
skCanvas.drawImage(
|
||||
skImage.skImage,
|
||||
offset.dx,
|
||||
offset.dy,
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawImageRect(ui.Image image, ui.Rect src, ui.Rect dst, CkPaint paint) {
|
||||
final CkImage skImage = image as CkImage;
|
||||
skCanvas.callMethod('drawImageRect', <dynamic>[
|
||||
skImage.legacyJsObject,
|
||||
makeSkRect(src),
|
||||
makeSkRect(dst),
|
||||
skCanvas.drawImageRect(
|
||||
skImage.skImage,
|
||||
toSkRect(src),
|
||||
toSkRect(dst),
|
||||
paint.skiaObject,
|
||||
false,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawImageNine(
|
||||
ui.Image image, ui.Rect center, ui.Rect dst, CkPaint paint) {
|
||||
final CkImage skImage = image as CkImage;
|
||||
skCanvas.callMethod('drawImageNine', <dynamic>[
|
||||
skImage.legacyJsObject,
|
||||
makeSkRect(center),
|
||||
makeSkRect(dst),
|
||||
skCanvas.drawImageNine(
|
||||
skImage.skImage,
|
||||
toSkRect(center),
|
||||
toSkRect(dst),
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawLine(ui.Offset p1, ui.Offset p2, CkPaint paint) {
|
||||
skCanvas.callMethod('drawLine', <dynamic>[
|
||||
skCanvas.drawLine(
|
||||
p1.dx,
|
||||
p1.dy,
|
||||
p2.dx,
|
||||
p2.dy,
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawOval(ui.Rect rect, CkPaint paint) {
|
||||
skCanvas.callMethod('drawOval', <js.JsObject?>[
|
||||
makeSkRect(rect),
|
||||
skCanvas.drawOval(
|
||||
toSkRect(rect),
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawPaint(CkPaint paint) {
|
||||
skCanvas.callMethod('drawPaint', <js.JsObject?>[paint.skiaObject]);
|
||||
skCanvas.drawPaint(paint.skiaObject);
|
||||
}
|
||||
|
||||
void drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
|
||||
final CkParagraph skParagraph = paragraph as CkParagraph;
|
||||
skCanvas.callMethod('drawParagraph', <dynamic>[
|
||||
skParagraph.skiaObject,
|
||||
void drawParagraph(CkParagraph paragraph, ui.Offset offset) {
|
||||
skCanvas.drawParagraph(
|
||||
_jsObjectWrapper.unwrapSkParagraph(paragraph.legacySkiaObject),
|
||||
offset.dx,
|
||||
offset.dy,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawPath(ui.Path path, CkPaint paint) {
|
||||
final js.JsObject? skPaint = paint.skiaObject;
|
||||
final CkPath enginePath = path as CkPath;
|
||||
final js.JsObject? skPath = enginePath._legacyJsObject;
|
||||
skCanvas.callMethod('drawPath', <js.JsObject?>[skPath, skPaint]);
|
||||
void drawPath(CkPath path, CkPaint paint) {
|
||||
skCanvas.drawPath(path._skPath, paint.skiaObject);
|
||||
}
|
||||
|
||||
void drawPicture(ui.Picture picture) {
|
||||
final CkPicture skPicture = picture as CkPicture;
|
||||
skCanvas.callMethod(
|
||||
'drawPicture', <js.JsObject?>[skPicture.skPicture.skiaObject]);
|
||||
void drawPicture(CkPicture picture) {
|
||||
skCanvas.drawPicture(picture._skPicture);
|
||||
}
|
||||
|
||||
// TODO(hterkelsen): https://github.com/flutter/flutter/issues/58824
|
||||
void drawPoints(CkPaint paint, ui.PointMode pointMode,
|
||||
js.JsArray<js.JsArray<double>>? points) {
|
||||
skCanvas.callMethod('drawPoints', <dynamic>[
|
||||
makeSkPointMode(pointMode),
|
||||
Float32List points) {
|
||||
skCanvas.drawPoints(
|
||||
toSkPointMode(pointMode),
|
||||
points,
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawRRect(ui.RRect rrect, CkPaint paint) {
|
||||
skCanvas.callMethod('drawRRect', <js.JsObject?>[
|
||||
makeSkRRect(rrect),
|
||||
skCanvas.drawRRect(
|
||||
toSkRRect(rrect),
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void drawRect(ui.Rect rect, CkPaint paint) {
|
||||
final js.JsObject skRect = makeSkRect(rect);
|
||||
final js.JsObject? skPaint = paint.skiaObject;
|
||||
skCanvas.callMethod('drawRect', <js.JsObject?>[skRect, skPaint]);
|
||||
skCanvas.drawRect(toSkRect(rect), paint.skiaObject);
|
||||
}
|
||||
|
||||
void drawShadow(ui.Path path, ui.Color color, double elevation,
|
||||
@ -218,72 +201,69 @@ class CkCanvas {
|
||||
void drawVertices(
|
||||
ui.Vertices vertices, ui.BlendMode blendMode, CkPaint paint) {
|
||||
CkVertices skVertices = vertices as CkVertices;
|
||||
skCanvas.callMethod('drawVertices', <js.JsObject?>[
|
||||
skCanvas.drawVertices(
|
||||
skVertices.skVertices,
|
||||
makeSkBlendMode(blendMode),
|
||||
paint.skiaObject
|
||||
]);
|
||||
toSkBlendMode(blendMode),
|
||||
paint.skiaObject,
|
||||
);
|
||||
}
|
||||
|
||||
void restore() {
|
||||
skCanvas.callMethod('restore');
|
||||
skCanvas.restore();
|
||||
}
|
||||
|
||||
void restoreToCount(int? count) {
|
||||
skCanvas.callMethod('restoreToCount', <int?>[count]);
|
||||
void restoreToCount(int count) {
|
||||
skCanvas.restoreToCount(count);
|
||||
}
|
||||
|
||||
void rotate(double radians) {
|
||||
skCanvas
|
||||
.callMethod('rotate', <double>[radians * 180.0 / math.pi, 0.0, 0.0]);
|
||||
skCanvas.rotate(radians * 180.0 / math.pi, 0.0, 0.0);
|
||||
}
|
||||
|
||||
int? save() {
|
||||
return skCanvas.callMethod('save');
|
||||
int save() {
|
||||
return skCanvas.save();
|
||||
}
|
||||
|
||||
void saveLayer(ui.Rect bounds, CkPaint paint) {
|
||||
skCanvas.callMethod('saveLayer', <js.JsObject?>[
|
||||
makeSkRect(bounds),
|
||||
skCanvas.saveLayer(
|
||||
toSkRect(bounds),
|
||||
paint.skiaObject,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
void saveLayerWithoutBounds(CkPaint paint) {
|
||||
skCanvas.callMethod('saveLayer', <js.JsObject?>[paint.skiaObject]);
|
||||
final SkCanvasSaveLayerWithoutBoundsOverride override = _jsObjectWrapper.castToSkCanvasSaveLayerWithoutBoundsOverride(skCanvas);
|
||||
override.saveLayer(paint.skiaObject);
|
||||
}
|
||||
|
||||
void saveLayerWithFilter(ui.Rect bounds, ui.ImageFilter filter) {
|
||||
final SkCanvasSaveLayerWithFilterOverride override = _jsObjectWrapper.castToSkCanvasSaveLayerWithFilterOverride(skCanvas);
|
||||
final CkImageFilter skImageFilter = filter as CkImageFilter;
|
||||
return skCanvas.callMethod(
|
||||
'saveLayer',
|
||||
<dynamic>[
|
||||
null,
|
||||
skImageFilter.skiaObject,
|
||||
0,
|
||||
makeSkRect(bounds),
|
||||
],
|
||||
return override.saveLayer(
|
||||
null,
|
||||
skImageFilter.skiaObject,
|
||||
0,
|
||||
toSkRect(bounds),
|
||||
);
|
||||
}
|
||||
|
||||
void scale(double sx, double sy) {
|
||||
skCanvas.callMethod('scale', <double>[sx, sy]);
|
||||
skCanvas.scale(sx, sy);
|
||||
}
|
||||
|
||||
void skew(double sx, double sy) {
|
||||
skCanvas.callMethod('skew', <double>[sx, sy]);
|
||||
skCanvas.skew(sx, sy);
|
||||
}
|
||||
|
||||
void transform(Float32List? matrix4) {
|
||||
skCanvas.callMethod(
|
||||
'concat', <js.JsArray<double>>[makeSkMatrixFromFloat32(matrix4)]);
|
||||
void transform(Float32List matrix4) {
|
||||
skCanvas.concat(toSkMatrixFromFloat32(matrix4));
|
||||
}
|
||||
|
||||
void translate(double dx, double dy) {
|
||||
skCanvas.callMethod('translate', <double>[dx, dy]);
|
||||
skCanvas.translate(dx, dy);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
skCanvas.callMethod('flush');
|
||||
skCanvas.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,11 +234,7 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
// ignore: unnecessary_null_comparison
|
||||
assert(path != null); // path is checked on the engine side
|
||||
assert(paint != null); // ignore: unnecessary_null_comparison
|
||||
_drawPath(path, paint);
|
||||
}
|
||||
|
||||
void _drawPath(ui.Path path, ui.Paint paint) {
|
||||
_canvas!.drawPath(path, paint as CkPaint);
|
||||
_canvas!.drawPath(path as CkPath, paint as CkPaint);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -289,11 +285,7 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
void drawPicture(ui.Picture picture) {
|
||||
// ignore: unnecessary_null_comparison
|
||||
assert(picture != null); // picture is checked on the engine side
|
||||
_drawPicture(picture);
|
||||
}
|
||||
|
||||
void _drawPicture(ui.Picture picture) {
|
||||
_canvas!.drawPicture(picture);
|
||||
_canvas!.drawPicture(picture as CkPicture);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -304,7 +296,7 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
}
|
||||
|
||||
void _drawParagraph(ui.Paragraph paragraph, ui.Offset offset) {
|
||||
_canvas!.drawParagraph(paragraph, offset);
|
||||
_canvas!.drawParagraph(paragraph as CkParagraph, offset);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -313,7 +305,13 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
assert(pointMode != null); // ignore: unnecessary_null_comparison
|
||||
assert(points != null); // ignore: unnecessary_null_comparison
|
||||
assert(paint != null); // ignore: unnecessary_null_comparison
|
||||
_drawPoints(paint, pointMode, encodePointList(points));
|
||||
final SkFloat32List skPoints = toMallocedSkPoints(points);
|
||||
_canvas!.drawPoints(
|
||||
paint as CkPaint,
|
||||
pointMode,
|
||||
skPoints.toTypedArray(),
|
||||
);
|
||||
freeFloat32List(skPoints);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -325,12 +323,11 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
if (points.length % 2 != 0) {
|
||||
throw ArgumentError('"points" must have an even number of values.');
|
||||
}
|
||||
_drawPoints(paint, pointMode, encodeRawPointList(points));
|
||||
}
|
||||
|
||||
void _drawPoints(
|
||||
ui.Paint paint, ui.PointMode pointMode, List<List<double>>? points) {
|
||||
_canvas!.drawPoints(paint as CkPaint, pointMode, points as js.JsArray<js.JsArray<double>>?);
|
||||
_canvas!.drawPoints(
|
||||
paint as CkPaint,
|
||||
pointMode,
|
||||
points,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -395,8 +392,8 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
rectBuffer[index3] = rect.bottom;
|
||||
}
|
||||
|
||||
final js.JsArray<Float32List>? colorBuffer =
|
||||
colors.isEmpty ? null : makeColorList(colors);
|
||||
final List<Float32List>? colorBuffer =
|
||||
colors.isEmpty ? null : toSkFloatColorList(colors);
|
||||
|
||||
_drawAtlas(
|
||||
paint, atlas, rstTransformBuffer, rectBuffer, colorBuffer, blendMode);
|
||||
@ -429,7 +426,7 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
throw ArgumentError(
|
||||
'If non-null, "colors" length must be one fourth the length of "rstTransforms" and "rects".');
|
||||
|
||||
_drawAtlas(paint, atlas, rstTransforms, rects, _encodeRawColorList(colors),
|
||||
_drawAtlas(paint, atlas, rstTransforms, rects, encodeRawColorList(colors),
|
||||
blendMode);
|
||||
}
|
||||
|
||||
@ -439,7 +436,7 @@ class CanvasKitCanvas implements ui.Canvas {
|
||||
ui.Image atlas,
|
||||
Float32List rstTransforms,
|
||||
Float32List rects,
|
||||
js.JsArray<Float32List>? colors,
|
||||
List<Float32List>? colors,
|
||||
ui.BlendMode blendMode,
|
||||
) {
|
||||
_canvas!.drawAtlasRaw(paint as CkPaint, atlas, rstTransforms, rects, colors, blendMode);
|
||||
|
||||
@ -3,6 +3,9 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
/// Bindings for CanvasKit JavaScript API.
|
||||
///
|
||||
/// Prefer keeping the originl CanvasKit names so it is easier to locate
|
||||
/// the API behind these bindings in the Skia source code.
|
||||
part of engine;
|
||||
|
||||
final js.JsObject _jsWindow = js.JsObject.fromBrowserObject(html.window);
|
||||
@ -37,12 +40,26 @@ class JsObjectWrapper {
|
||||
external set skImageFilter(SkImageFilter? filter);
|
||||
external set skPath(SkPath? path);
|
||||
external set skImage(SkImage? image);
|
||||
external SkCanvas? get skCanvas;
|
||||
external set skCanvas(SkCanvas? canvas);
|
||||
external SkPicture? get skPicture;
|
||||
external set skPicture(SkPicture? picture);
|
||||
external SkParagraph? get skParagraph;
|
||||
external set skParagraph(SkParagraph? paragraph);
|
||||
}
|
||||
|
||||
/// Reads [JsObjectWrapper.skPath] as [SkPathArcToPointOverload].
|
||||
@JS('window.flutter_js_object_wrapper.skPath')
|
||||
external SkPathArcToPointOverload get _skPathArcToPointOverload;
|
||||
|
||||
/// Reads [JsObjectWrapper.skCanvas] as [SkCanvasSaveLayerWithoutBoundsOverride].
|
||||
@JS('window.flutter_js_object_wrapper.skCanvas')
|
||||
external SkCanvasSaveLayerWithoutBoundsOverride get _skCanvasSaveLayerWithoutBoundsOverride;
|
||||
|
||||
/// Reads [JsObjectWrapper.skCanvas] as [SkCanvasSaveLayerWithFilterOverride].
|
||||
@JS('window.flutter_js_object_wrapper.skCanvas')
|
||||
external SkCanvasSaveLayerWithFilterOverride get _skCanvasSaveLayerWithFilterOverride;
|
||||
|
||||
/// Specific methods that wrap `@JS`-backed objects into a [js.JsObject]
|
||||
/// for use with legacy `dart:js` API.
|
||||
extension JsObjectWrappers on JsObjectWrapper {
|
||||
@ -88,12 +105,61 @@ extension JsObjectWrappers on JsObjectWrapper {
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
js.JsObject wrapSkPicture(SkPicture picture) {
|
||||
_jsObjectWrapper.skPicture = picture;
|
||||
js.JsObject wrapped = _jsObjectWrapperLegacy['skPicture'];
|
||||
_jsObjectWrapper.skPicture = null;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
SkPicture unwrapSkPicture(js.JsObject wrapped) {
|
||||
_jsObjectWrapperLegacy['skPicture'] = wrapped;
|
||||
final SkPicture unwrapped = _jsObjectWrapper.skPicture!;
|
||||
_jsObjectWrapper.skPicture = null;
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
js.JsObject wrapSkParagraph(SkParagraph paragraph) {
|
||||
_jsObjectWrapper.skParagraph = paragraph;
|
||||
js.JsObject wrapped = _jsObjectWrapperLegacy['skParagraph'];
|
||||
_jsObjectWrapper.skParagraph = null;
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
SkParagraph unwrapSkParagraph(js.JsObject wrapped) {
|
||||
_jsObjectWrapperLegacy['skParagraph'] = wrapped;
|
||||
final SkParagraph unwrapped = _jsObjectWrapper.skParagraph!;
|
||||
_jsObjectWrapper.skParagraph = null;
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
SkCanvas unwrapSkCanvas(js.JsObject wrapped) {
|
||||
_jsObjectWrapperLegacy['skCanvas'] = wrapped;
|
||||
final SkCanvas unwrapped = _jsObjectWrapper.skCanvas!;
|
||||
_jsObjectWrapper.skCanvas = null;
|
||||
return unwrapped;
|
||||
}
|
||||
|
||||
SkPathArcToPointOverload castToSkPathArcToPointOverload(SkPath path) {
|
||||
_jsObjectWrapper.skPath = path;
|
||||
final SkPathArcToPointOverload overload = _skPathArcToPointOverload;
|
||||
_jsObjectWrapper.skPath = null;
|
||||
return overload;
|
||||
}
|
||||
|
||||
SkCanvasSaveLayerWithoutBoundsOverride castToSkCanvasSaveLayerWithoutBoundsOverride(SkCanvas canvas) {
|
||||
_jsObjectWrapper.skCanvas = canvas;
|
||||
final SkCanvasSaveLayerWithoutBoundsOverride overload = _skCanvasSaveLayerWithoutBoundsOverride;
|
||||
_jsObjectWrapper.skCanvas = null;
|
||||
return overload;
|
||||
}
|
||||
|
||||
SkCanvasSaveLayerWithFilterOverride castToSkCanvasSaveLayerWithFilterOverride(SkCanvas canvas) {
|
||||
_jsObjectWrapper.skCanvas = canvas;
|
||||
final SkCanvasSaveLayerWithFilterOverride overload = _skCanvasSaveLayerWithFilterOverride;
|
||||
_jsObjectWrapper.skCanvas = null;
|
||||
return overload;
|
||||
}
|
||||
}
|
||||
|
||||
@JS('window.flutter_canvas_kit')
|
||||
@ -110,12 +176,274 @@ class CanvasKit {
|
||||
external SkTileModeEnum get TileMode;
|
||||
external SkFillTypeEnum get FillType;
|
||||
external SkPathOpEnum get PathOp;
|
||||
external SkClipOpEnum get ClipOp;
|
||||
external SkPointModeEnum get PointMode;
|
||||
external SkVertexModeEnum get VertexMode;
|
||||
external SkRectHeightStyleEnum get RectHeightStyle;
|
||||
external SkRectWidthStyleEnum get RectWidthStyle;
|
||||
external SkAffinityEnum get Affinity;
|
||||
external SkTextAlignEnum get TextAlign;
|
||||
external SkTextDirectionEnum get TextDirection;
|
||||
external SkFontWeightEnum get FontWeight;
|
||||
external SkFontSlantEnum get FontSlant;
|
||||
external SkAnimatedImage MakeAnimatedImageFromEncoded(Uint8List imageData);
|
||||
external SkShaderNamespace get SkShader;
|
||||
external SkMaskFilter MakeBlurMaskFilter(SkBlurStyle blurStyle, double sigma, bool respectCTM);
|
||||
external SkColorFilterNamespace get SkColorFilter;
|
||||
external SkImageFilterNamespace get SkImageFilter;
|
||||
external SkPath MakePathFromOp(SkPath path1, SkPath path2, SkPathOp pathOp);
|
||||
external SkTonalColors computeTonalColors(SkTonalColors inTonalColors);
|
||||
external SkVertices MakeSkVertices(
|
||||
SkVertexMode mode,
|
||||
List<Float32List> positions,
|
||||
List<Float32List>? textureCoordinates,
|
||||
// TODO(yjbanov): make this Uint32Array when CanvasKit supports it.
|
||||
List<Float32List>? colors,
|
||||
Uint16List? indices,
|
||||
);
|
||||
external SkParagraphBuilderNamespace get ParagraphBuilder;
|
||||
external SkParagraphStyle ParagraphStyle(SkParagraphStyleProperties properties);
|
||||
external SkTextStyle TextStyle(SkTextStyleProperties properties);
|
||||
|
||||
// Text decoration enum is embedded in the CanvasKit object itself.
|
||||
external int get NoDecoration;
|
||||
external int get UnderlineDecoration;
|
||||
external int get OverlineDecoration;
|
||||
external int get LineThroughDecoration;
|
||||
// End of text decoration enum.
|
||||
|
||||
external SkFontMgrNamespace get SkFontMgr;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontSlantEnum {
|
||||
external SkFontSlant get Upright;
|
||||
external SkFontSlant get Italic;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontSlant {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkFontSlant> _skFontSlants = <SkFontSlant>[
|
||||
canvasKitJs.FontSlant.Upright,
|
||||
canvasKitJs.FontSlant.Italic,
|
||||
];
|
||||
|
||||
SkFontSlant toSkFontSlant(ui.FontStyle style) {
|
||||
return _skFontSlants[style.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontWeightEnum {
|
||||
external SkFontWeight get Thin;
|
||||
external SkFontWeight get ExtraLight;
|
||||
external SkFontWeight get Light;
|
||||
external SkFontWeight get Normal;
|
||||
external SkFontWeight get Medium;
|
||||
external SkFontWeight get SemiBold;
|
||||
external SkFontWeight get Bold;
|
||||
external SkFontWeight get ExtraBold;
|
||||
external SkFontWeight get ExtraBlack;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontWeight {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkFontWeight> _skFontWeights = <SkFontWeight>[
|
||||
canvasKitJs.FontWeight.Thin,
|
||||
canvasKitJs.FontWeight.ExtraLight,
|
||||
canvasKitJs.FontWeight.Light,
|
||||
canvasKitJs.FontWeight.Normal,
|
||||
canvasKitJs.FontWeight.Medium,
|
||||
canvasKitJs.FontWeight.SemiBold,
|
||||
canvasKitJs.FontWeight.Bold,
|
||||
canvasKitJs.FontWeight.ExtraBold,
|
||||
canvasKitJs.FontWeight.ExtraBlack,
|
||||
];
|
||||
|
||||
SkFontWeight toSkFontWeight(ui.FontWeight weight) {
|
||||
return _skFontWeights[weight.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkAffinityEnum {
|
||||
external SkAffinity get Upstream;
|
||||
external SkAffinity get Downstream;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkAffinity {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkAffinity> _skAffinitys = <SkAffinity>[
|
||||
canvasKitJs.Affinity.Upstream,
|
||||
canvasKitJs.Affinity.Downstream,
|
||||
];
|
||||
|
||||
SkAffinity toSkAffinity(ui.TextAffinity affinity) {
|
||||
return _skAffinitys[affinity.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextDirectionEnum {
|
||||
external SkTextDirection get RTL;
|
||||
external SkTextDirection get LTR;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextDirection {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
// Flutter enumerates text directions as RTL, LTR, while CanvasKit
|
||||
// enumerates them LTR, RTL.
|
||||
final List<SkTextDirection> _skTextDirections = <SkTextDirection>[
|
||||
canvasKitJs.TextDirection.RTL,
|
||||
canvasKitJs.TextDirection.LTR,
|
||||
];
|
||||
|
||||
SkTextDirection toSkTextDirection(ui.TextDirection direction) {
|
||||
return _skTextDirections[direction.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextAlignEnum {
|
||||
external SkTextAlign get Left;
|
||||
external SkTextAlign get Right;
|
||||
external SkTextAlign get Center;
|
||||
external SkTextAlign get Justify;
|
||||
external SkTextAlign get Start;
|
||||
external SkTextAlign get End;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextAlign {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkTextAlign> _skTextAligns = <SkTextAlign>[
|
||||
canvasKitJs.TextAlign.Left,
|
||||
canvasKitJs.TextAlign.Right,
|
||||
canvasKitJs.TextAlign.Center,
|
||||
canvasKitJs.TextAlign.Justify,
|
||||
canvasKitJs.TextAlign.Start,
|
||||
canvasKitJs.TextAlign.End,
|
||||
];
|
||||
|
||||
SkTextAlign toSkTextAlign(ui.TextAlign align) {
|
||||
return _skTextAligns[align.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkRectHeightStyleEnum {
|
||||
// TODO(yjbanov): support all styles
|
||||
external SkRectHeightStyle get Tight;
|
||||
external SkRectHeightStyle get Max;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkRectHeightStyle {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkRectHeightStyle> _skRectHeightStyles = <SkRectHeightStyle>[
|
||||
canvasKitJs.RectHeightStyle.Tight,
|
||||
canvasKitJs.RectHeightStyle.Max,
|
||||
];
|
||||
|
||||
SkRectHeightStyle toSkRectHeightStyle(ui.BoxHeightStyle style) {
|
||||
final int index = style.index;
|
||||
return _skRectHeightStyles[index < 2 ? index : 0];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkRectWidthStyleEnum {
|
||||
external SkRectWidthStyle get Tight;
|
||||
external SkRectWidthStyle get Max;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkRectWidthStyle {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkRectWidthStyle> _skRectWidthStyles = <SkRectWidthStyle>[
|
||||
canvasKitJs.RectWidthStyle.Tight,
|
||||
canvasKitJs.RectWidthStyle.Max,
|
||||
];
|
||||
|
||||
SkRectWidthStyle toSkRectWidthStyle(ui.BoxWidthStyle style) {
|
||||
final int index = style.index;
|
||||
return _skRectWidthStyles[index < 2 ? index : 0];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkVertexModeEnum {
|
||||
external SkVertexMode get Triangles;
|
||||
external SkVertexMode get TrianglesStrip;
|
||||
external SkVertexMode get TriangleFan;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkVertexMode {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkVertexMode> _skVertexModes = <SkVertexMode>[
|
||||
canvasKitJs.VertexMode.Triangles,
|
||||
canvasKitJs.VertexMode.TrianglesStrip,
|
||||
canvasKitJs.VertexMode.TriangleFan,
|
||||
];
|
||||
|
||||
SkVertexMode toSkVertexMode(ui.VertexMode mode) {
|
||||
return _skVertexModes[mode.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkPointModeEnum {
|
||||
external SkPointMode get Points;
|
||||
external SkPointMode get Lines;
|
||||
external SkPointMode get Polygon;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkPointMode {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkPointMode> _skPointModes = <SkPointMode>[
|
||||
canvasKitJs.PointMode.Points,
|
||||
canvasKitJs.PointMode.Lines,
|
||||
canvasKitJs.PointMode.Polygon,
|
||||
];
|
||||
|
||||
SkPointMode toSkPointMode(ui.PointMode mode) {
|
||||
return _skPointModes[mode.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkClipOpEnum {
|
||||
external SkClipOp get Difference;
|
||||
external SkClipOp get Intersect;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkClipOp {
|
||||
external int get value;
|
||||
}
|
||||
|
||||
final List<SkClipOp> _skClipOps = <SkClipOp>[
|
||||
canvasKitJs.ClipOp.Difference,
|
||||
canvasKitJs.ClipOp.Intersect,
|
||||
];
|
||||
|
||||
SkClipOp toSkClipOp(ui.ClipOp clipOp) {
|
||||
return _skClipOps[clipOp.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
@ -184,8 +512,8 @@ final List<SkBlurStyle> _skBlurStyles = <SkBlurStyle>[
|
||||
canvasKitJs.BlurStyle.Inner,
|
||||
];
|
||||
|
||||
SkBlurStyle toSkBlurStyle(ui.BlurStyle strokeCap) {
|
||||
return _skBlurStyles[strokeCap.index];
|
||||
SkBlurStyle toSkBlurStyle(ui.BlurStyle style) {
|
||||
return _skBlurStyles[style.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
@ -370,8 +698,8 @@ final List<SkTileMode> _skTileModes = <SkTileMode>[
|
||||
canvasKitJs.TileMode.Mirror,
|
||||
];
|
||||
|
||||
SkTileMode toSkTileMode(ui.TileMode filterQuality) {
|
||||
return _skTileModes[filterQuality.index];
|
||||
SkTileMode toSkTileMode(ui.TileMode mode) {
|
||||
return _skTileModes[mode.index];
|
||||
}
|
||||
|
||||
@JS()
|
||||
@ -403,7 +731,7 @@ class SkShaderNamespace {
|
||||
external SkShader MakeLinearGradient(
|
||||
Float32List from, // 2-element array
|
||||
Float32List to, // 2-element array
|
||||
Uint32List colors,
|
||||
List<Float32List> colors,
|
||||
Float32List colorStops,
|
||||
SkTileMode tileMode,
|
||||
);
|
||||
@ -457,10 +785,13 @@ class SkPaint {
|
||||
external void setColorFilter(SkColorFilter? colorFilter);
|
||||
external void setStrokeMiter(double miterLimit);
|
||||
external void setImageFilter(SkImageFilter? imageFilter);
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkMaskFilter {}
|
||||
class SkMaskFilter {
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkColorFilterNamespace {
|
||||
@ -473,7 +804,9 @@ class SkColorFilterNamespace {
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkColorFilter {}
|
||||
class SkColorFilter {
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkImageFilterNamespace {
|
||||
@ -492,7 +825,9 @@ class SkImageFilterNamespace {
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkImageFilter {}
|
||||
class SkImageFilter {
|
||||
external void delete();
|
||||
}
|
||||
|
||||
/// Converts a 4x4 Flutter matrix (represented as a [Float32List]) to an
|
||||
/// SkMatrix, which is a 3x3 transform matrix.
|
||||
@ -647,6 +982,30 @@ Uint32List toSkIntColorList(List<ui.Color> colors) {
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Float32List> toSkFloatColorList(List<ui.Color> colors) {
|
||||
final int len = colors.length;
|
||||
final List<Float32List> result = <Float32List>[];
|
||||
for (int i = 0; i < len; i++) {
|
||||
final Float32List array = Float32List(4);
|
||||
final ui.Color color = colors[i];
|
||||
array[0] = color.red / 255.0;
|
||||
array[1] = color.green / 255.0;
|
||||
array[2] = color.blue / 255.0;
|
||||
array[3] = color.alpha / 255.0;
|
||||
result.add(array);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Float32List> encodeRawColorList(Int32List rawColors) {
|
||||
final int colorCount = rawColors.length;
|
||||
final List<ui.Color> colors = <ui.Color>[];
|
||||
for (int i = 0; i < colorCount; ++i) {
|
||||
colors.add(ui.Color(rawColors[i]));
|
||||
}
|
||||
return toSkFloatColorList(colors);
|
||||
}
|
||||
|
||||
@JS('window.flutter_canvas_kit.SkPath')
|
||||
class SkPath {
|
||||
external SkPath([SkPath? other]);
|
||||
@ -834,6 +1193,46 @@ SkRect toSkRect(ui.Rect rect) {
|
||||
);
|
||||
}
|
||||
|
||||
@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,
|
||||
@ -859,3 +1258,359 @@ SkFloat32List toMallocedSkPoints(List<ui.Offset> points) {
|
||||
}
|
||||
return skPoints;
|
||||
}
|
||||
|
||||
// TODO(yjbanov): this is inefficient. We should be able to pass points
|
||||
// as Float32List without a conversion.
|
||||
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;
|
||||
final Float32List skPoint = Float32List(2);
|
||||
skPoint[0] = points[x];
|
||||
skPoint[1] = points[y];
|
||||
result.add(skPoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<Float32List> toSkPoints2d(List<ui.Offset> offsets) {
|
||||
final int len = offsets.length;
|
||||
final List<Float32List> result = <Float32List>[];
|
||||
for (var i = 0; i < len; i++) {
|
||||
final ui.Offset offset = offsets[i];
|
||||
final Float32List skPoint = Float32List(2);
|
||||
skPoint[0] = offset.dx;
|
||||
skPoint[1] = offset.dy;
|
||||
result.add(skPoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Uint16List toUint16List(List<int> ints) {
|
||||
final int len = ints.length;
|
||||
final Uint16List result = Uint16List(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
result[i] = ints[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@JS('window.flutter_canvas_kit.SkPictureRecorder')
|
||||
class SkPictureRecorder {
|
||||
external SkPictureRecorder();
|
||||
external SkCanvas beginRecording(SkRect bounds);
|
||||
external SkPicture finishRecordingAsPicture();
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkCanvas {
|
||||
external void clear(Float32List color);
|
||||
external void clipPath(
|
||||
SkPath path,
|
||||
SkClipOp clipOp,
|
||||
bool doAntiAlias,
|
||||
);
|
||||
external void clipRRect(
|
||||
SkRRect rrect,
|
||||
SkClipOp clipOp,
|
||||
bool doAntiAlias,
|
||||
);
|
||||
external void clipRect(
|
||||
SkRect rrect,
|
||||
SkClipOp clipOp,
|
||||
bool doAntiAlias,
|
||||
);
|
||||
external void drawArc(
|
||||
SkRect oval,
|
||||
double startAngleDegrees,
|
||||
double sweepAngleDegrees,
|
||||
bool useCenter,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawAtlas(
|
||||
SkImage image,
|
||||
Float32List rects,
|
||||
Float32List rstTransforms,
|
||||
SkPaint paint,
|
||||
SkBlendMode blendMode,
|
||||
List<Float32List>? colors,
|
||||
);
|
||||
external void drawCircle(
|
||||
double x,
|
||||
double y,
|
||||
double radius,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawColorInt(
|
||||
int color,
|
||||
SkBlendMode blendMode,
|
||||
);
|
||||
external void drawDRRect(
|
||||
SkRRect outer,
|
||||
SkRRect inner,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawImage(
|
||||
SkImage image,
|
||||
double x,
|
||||
double y,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawImageRect(
|
||||
SkImage image,
|
||||
SkRect src,
|
||||
SkRect dst,
|
||||
SkPaint paint,
|
||||
bool fastSample,
|
||||
);
|
||||
external void drawImageNine(
|
||||
SkImage image,
|
||||
SkRect center,
|
||||
SkRect dst,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawLine(
|
||||
double x1,
|
||||
double y1,
|
||||
double x2,
|
||||
double y2,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawOval(
|
||||
SkRect rect,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawPaint(
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawPath(
|
||||
SkPath path,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawPoints(
|
||||
SkPointMode pointMode,
|
||||
Float32List points,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawRRect(
|
||||
SkRRect rrect,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawRect(
|
||||
SkRect rrect,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void drawShadow(
|
||||
SkPath path,
|
||||
Float32List zPlaneParams,
|
||||
Float32List lightPos,
|
||||
double lightRadius,
|
||||
Float32List ambientColor,
|
||||
Float32List spotColor,
|
||||
int flags,
|
||||
);
|
||||
external void drawVertices(
|
||||
SkVertices vertices,
|
||||
SkBlendMode blendMode,
|
||||
SkPaint paint,
|
||||
);
|
||||
external int save();
|
||||
external int getSaveCount();
|
||||
external void saveLayer(
|
||||
SkRect bounds,
|
||||
SkPaint paint,
|
||||
);
|
||||
external void restore();
|
||||
external void restoreToCount(int count);
|
||||
external void rotate(
|
||||
double angleDegrees,
|
||||
double px,
|
||||
double py,
|
||||
);
|
||||
external void scale(double x, double y);
|
||||
external void skew(double x, double y);
|
||||
external void concat(Float32List matrix);
|
||||
external void translate(double x, double y);
|
||||
external void flush();
|
||||
external void drawPicture(SkPicture picture);
|
||||
external void drawParagraph(
|
||||
SkParagraph paragraph,
|
||||
double x,
|
||||
double y,
|
||||
);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkCanvasSaveLayerWithoutBoundsOverride {
|
||||
external void saveLayer(SkPaint paint);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkCanvasSaveLayerWithFilterOverride {
|
||||
external void saveLayer(
|
||||
SkPaint? paint,
|
||||
SkImageFilter? imageFilter,
|
||||
int flags,
|
||||
SkRect rect,
|
||||
);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkPicture {
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkParagraphBuilderNamespace {
|
||||
external SkParagraphBuilder Make(
|
||||
SkParagraphStyle paragraphStyle,
|
||||
SkFontMgr? fontManager,
|
||||
);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkParagraphBuilder {
|
||||
external void addText(String text);
|
||||
external void pushStyle(SkTextStyle textStyle);
|
||||
external void pop();
|
||||
external SkParagraph build();
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
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);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextStyle {
|
||||
|
||||
}
|
||||
|
||||
@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 fontSize(double? value);
|
||||
|
||||
external List<String>? get fontFamilies;
|
||||
external set fontFamilies(List<String>? value);
|
||||
|
||||
external SkFontStyle? get fontStyle;
|
||||
external set fontStyle(SkFontStyle? value);
|
||||
}
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class SkFontStyle {
|
||||
external SkFontWeight? get weight;
|
||||
external set weight(SkFontWeight? value);
|
||||
|
||||
external SkFontSlant? get slant;
|
||||
external set slant(SkFontSlant? value);
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontMgr {
|
||||
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkParagraph {
|
||||
external double getAlphabeticBaseline();
|
||||
external bool didExceedMaxLines();
|
||||
external double getHeight();
|
||||
external double getIdeographicBaseline();
|
||||
external double getLongestLine();
|
||||
external double getMaxIntrinsicWidth();
|
||||
external double getMinIntrinsicWidth();
|
||||
external double getMaxWidth();
|
||||
external List<SkRect> getRectsForRange(
|
||||
int start,
|
||||
int end,
|
||||
SkRectHeightStyle heightStyle,
|
||||
SkRectWidthStyle widthStyle,
|
||||
);
|
||||
external SkTextPosition getGlyphPositionAtCoordinate(
|
||||
double x,
|
||||
double y,
|
||||
);
|
||||
external SkTextRange getWordBoundary(int position);
|
||||
external void layout(double width);
|
||||
external void delete();
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextPosition {
|
||||
external SkAffinity get affinity;
|
||||
external int get pos;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkTextRange {
|
||||
external int get start;
|
||||
external int get end;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkVertices { }
|
||||
|
||||
@JS()
|
||||
@anonymous
|
||||
class SkTonalColors {
|
||||
external factory SkTonalColors({
|
||||
required Float32List ambient,
|
||||
required Float32List spot,
|
||||
});
|
||||
external Float32List get ambient;
|
||||
external Float32List get spot;
|
||||
}
|
||||
|
||||
@JS()
|
||||
class SkFontMgrNamespace {
|
||||
// TODO(yjbanov): can this be made non-null? It returns null in our unit-tests right now.
|
||||
external SkFontMgr? FromData(List<Uint8List> fonts);
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
part of engine;
|
||||
|
||||
/// A [ui.ColorFilter] backed by Skia's [CkColorFilter].
|
||||
class CkColorFilter extends ResurrectableSkiaObject {
|
||||
class CkColorFilter extends ResurrectableSkiaObject<SkColorFilter> {
|
||||
final EngineColorFilter _engineFilter;
|
||||
|
||||
CkColorFilter.mode(EngineColorFilter filter) : _engineFilter = filter;
|
||||
@ -19,9 +19,7 @@ class CkColorFilter extends ResurrectableSkiaObject {
|
||||
CkColorFilter.srgbToLinearGamma(EngineColorFilter filter)
|
||||
: _engineFilter = filter;
|
||||
|
||||
SkColorFilter? _skColorFilter;
|
||||
|
||||
js.JsObject _createSkiaObjectFromFilter() {
|
||||
SkColorFilter _createSkiaObjectFromFilter() {
|
||||
SkColorFilter skColorFilter;
|
||||
switch (_engineFilter._type) {
|
||||
case EngineColorFilter._TypeMode:
|
||||
@ -48,17 +46,24 @@ class CkColorFilter extends ResurrectableSkiaObject {
|
||||
throw StateError(
|
||||
'Unknown mode ${_engineFilter._type} for ColorFilter.');
|
||||
}
|
||||
_skColorFilter = skColorFilter;
|
||||
return _jsObjectWrapper.wrapSkColorFilter(skColorFilter);
|
||||
return skColorFilter;
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject createDefault() {
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkColorFilter(skiaObject);
|
||||
|
||||
@override
|
||||
SkColorFilter createDefault() {
|
||||
return _createSkiaObjectFromFilter();
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject resurrect() {
|
||||
SkColorFilter resurrect() {
|
||||
return _createSkiaObjectFromFilter();
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,7 +334,9 @@ class HtmlViewEmbedder {
|
||||
final SurfaceFrame frame =
|
||||
_overlays[viewId]!.surface.acquireFrame(_frameSize);
|
||||
final CkCanvas canvas = frame.skiaCanvas;
|
||||
canvas.drawPicture(_pictureRecorders[viewId]!.endRecording());
|
||||
canvas.drawPicture(
|
||||
_pictureRecorders[viewId]!.endRecording() as CkPicture,
|
||||
);
|
||||
frame.submit();
|
||||
}
|
||||
_pictureRecorders.clear();
|
||||
|
||||
@ -41,7 +41,7 @@ class SkiaFontCollection {
|
||||
final List<Uint8List> fontBuffers =
|
||||
_registeredFonts.map<Uint8List>((f) => f!.bytes).toList();
|
||||
|
||||
skFontMgr = canvasKit['SkFontMgr'].callMethod('FromData', fontBuffers);
|
||||
skFontMgr = canvasKitJs.SkFontMgr.FromData(fontBuffers);
|
||||
}
|
||||
|
||||
/// Loads all of the unloaded fonts in [_unloadedFonts] and adds them
|
||||
@ -178,7 +178,7 @@ class SkiaFontCollection {
|
||||
.then<ByteBuffer>((dynamic x) => x as ByteBuffer);
|
||||
}
|
||||
|
||||
js.JsObject? skFontMgr;
|
||||
SkFontMgr? skFontMgr;
|
||||
}
|
||||
|
||||
/// Represents a font that has been registered.
|
||||
|
||||
@ -7,7 +7,7 @@ part of engine;
|
||||
/// The CanvasKit implementation of [ui.ImageFilter].
|
||||
///
|
||||
/// Currently only supports `blur`.
|
||||
class CkImageFilter extends ResurrectableSkiaObject implements ui.ImageFilter {
|
||||
class CkImageFilter extends ResurrectableSkiaObject<SkImageFilter> implements ui.ImageFilter {
|
||||
CkImageFilter.blur({double sigmaX = 0.0, double sigmaY = 0.0})
|
||||
: _sigmaX = sigmaX,
|
||||
_sigmaY = sigmaY;
|
||||
@ -15,23 +15,27 @@ class CkImageFilter extends ResurrectableSkiaObject implements ui.ImageFilter {
|
||||
final double _sigmaX;
|
||||
final double _sigmaY;
|
||||
|
||||
SkImageFilter? _skImageFilter;
|
||||
@override
|
||||
SkImageFilter createDefault() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
js.JsObject createDefault() => _initSkiaObject();
|
||||
SkImageFilter resurrect() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
js.JsObject resurrect() => _initSkiaObject();
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
|
||||
js.JsObject _initSkiaObject() {
|
||||
final SkImageFilter skImageFilter = canvasKitJs.SkImageFilter.MakeBlur(
|
||||
@override
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkImageFilter(skiaObject);
|
||||
|
||||
SkImageFilter _initSkiaObject() {
|
||||
return canvasKitJs.SkImageFilter.MakeBlur(
|
||||
_sigmaX,
|
||||
_sigmaY,
|
||||
canvasKitJs.TileMode.Clamp,
|
||||
null,
|
||||
);
|
||||
_skImageFilter = skImageFilter;
|
||||
return _jsObjectWrapper.wrapSkImageFilter(skImageFilter);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@ -402,10 +402,10 @@ class PictureLayer extends Layer {
|
||||
/// on the given elevation.
|
||||
class PhysicalShapeLayer extends ContainerLayer
|
||||
implements ui.PhysicalShapeEngineLayer {
|
||||
final double? _elevation;
|
||||
final ui.Color? _color;
|
||||
final double _elevation;
|
||||
final ui.Color _color;
|
||||
final ui.Color? _shadowColor;
|
||||
final ui.Path? _path;
|
||||
final CkPath _path;
|
||||
final ui.Clip _clipBehavior;
|
||||
|
||||
PhysicalShapeLayer(
|
||||
@ -420,7 +420,7 @@ class PhysicalShapeLayer extends ContainerLayer
|
||||
void preroll(PrerollContext prerollContext, Matrix4 matrix) {
|
||||
prerollChildren(prerollContext, matrix);
|
||||
|
||||
paintBounds = _path!.getBounds();
|
||||
paintBounds = _path.getBounds();
|
||||
if (_elevation == 0.0) {
|
||||
// No need to extend the paint bounds if there is no shadow.
|
||||
return;
|
||||
@ -480,16 +480,16 @@ class PhysicalShapeLayer extends ContainerLayer
|
||||
assert(needsPainting);
|
||||
|
||||
if (_elevation != 0) {
|
||||
drawShadow(paintContext.leafNodesCanvas!, _path!, _shadowColor!, _elevation!,
|
||||
_color!.alpha != 0xff);
|
||||
drawShadow(paintContext.leafNodesCanvas!, _path, _shadowColor!, _elevation,
|
||||
_color.alpha != 0xff);
|
||||
}
|
||||
|
||||
final ui.Paint paint = ui.Paint()..color = _color!;
|
||||
final ui.Paint paint = ui.Paint()..color = _color;
|
||||
if (_clipBehavior != ui.Clip.antiAliasWithSaveLayer) {
|
||||
paintContext.leafNodesCanvas!.drawPath(_path!, paint as CkPaint);
|
||||
paintContext.leafNodesCanvas!.drawPath(_path, paint as CkPaint);
|
||||
}
|
||||
|
||||
final int? saveCount = paintContext.internalNodesCanvas.save();
|
||||
final int saveCount = paintContext.internalNodesCanvas.save();
|
||||
switch (_clipBehavior) {
|
||||
case ui.Clip.hardEdge:
|
||||
paintContext.internalNodesCanvas.clipPath(_path, false);
|
||||
|
||||
@ -185,8 +185,13 @@ class LayerSceneBuilder implements ui.SceneBuilder {
|
||||
ui.Clip clipBehavior = ui.Clip.none,
|
||||
ui.EngineLayer? oldLayer,
|
||||
}) {
|
||||
final PhysicalShapeLayer layer =
|
||||
PhysicalShapeLayer(elevation, color, shadowColor, path, clipBehavior);
|
||||
final PhysicalShapeLayer layer = PhysicalShapeLayer(
|
||||
elevation,
|
||||
color,
|
||||
shadowColor,
|
||||
path as CkPath,
|
||||
clipBehavior,
|
||||
);
|
||||
pushLayer(layer);
|
||||
return layer;
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
part of engine;
|
||||
|
||||
/// The CanvasKit implementation of [ui.MaskFilter].
|
||||
class CkMaskFilter extends ResurrectableSkiaObject {
|
||||
class CkMaskFilter extends ResurrectableSkiaObject<SkMaskFilter> {
|
||||
CkMaskFilter.blur(ui.BlurStyle blurStyle, double sigma)
|
||||
: _blurStyle = blurStyle,
|
||||
_sigma = sigma;
|
||||
@ -13,21 +13,25 @@ class CkMaskFilter extends ResurrectableSkiaObject {
|
||||
final ui.BlurStyle _blurStyle;
|
||||
final double _sigma;
|
||||
|
||||
SkMaskFilter? _skMaskFilter;
|
||||
@override
|
||||
SkMaskFilter createDefault() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
js.JsObject createDefault() => _initSkiaObject();
|
||||
SkMaskFilter resurrect() => _initSkiaObject();
|
||||
|
||||
@override
|
||||
js.JsObject resurrect() => _initSkiaObject();
|
||||
|
||||
js.JsObject _initSkiaObject() {
|
||||
final SkMaskFilter skMaskFilter = canvasKitJs.MakeBlurMaskFilter(
|
||||
SkMaskFilter _initSkiaObject() {
|
||||
return canvasKitJs.MakeBlurMaskFilter(
|
||||
toSkBlurStyle(_blurStyle),
|
||||
_sigma,
|
||||
true,
|
||||
);
|
||||
_skMaskFilter = skMaskFilter;
|
||||
return _jsObjectWrapper.wrapSkMaskFilter(skMaskFilter);
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkMaskFilter(skiaObject);
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,8 +14,8 @@ class CkNWayCanvas {
|
||||
}
|
||||
|
||||
/// Calls [save] on all canvases.
|
||||
int? save() {
|
||||
int? saveCount;
|
||||
int save() {
|
||||
int saveCount = 0;
|
||||
for (int i = 0; i < _canvases.length; i++) {
|
||||
saveCount = _canvases[i]!.save();
|
||||
}
|
||||
@ -44,7 +44,7 @@ class CkNWayCanvas {
|
||||
}
|
||||
|
||||
/// Calls [restoreToCount] on all canvases.
|
||||
void restoreToCount(int? count) {
|
||||
void restoreToCount(int count) {
|
||||
for (int i = 0; i < _canvases.length; i++) {
|
||||
_canvases[i]!.restoreToCount(count);
|
||||
}
|
||||
@ -58,7 +58,7 @@ class CkNWayCanvas {
|
||||
}
|
||||
|
||||
/// Calls [transform] on all canvases.
|
||||
void transform(Float32List? matrix) {
|
||||
void transform(Float32List matrix) {
|
||||
for (int i = 0; i < _canvases.length; i++) {
|
||||
_canvases[i]!.transform(matrix);
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ part of engine;
|
||||
///
|
||||
/// This class is backed by a Skia object that must be explicitly
|
||||
/// deleted to avoid a memory leak. This is done by extending [SkiaObject].
|
||||
class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
class CkPaint extends ResurrectableSkiaObject<SkPaint> implements ui.Paint {
|
||||
CkPaint();
|
||||
|
||||
static const ui.Color _defaultPaintColor = ui.Color(0xFF000000);
|
||||
@ -21,7 +21,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_blendMode = value;
|
||||
_skPaint.setBlendMode(toSkBlendMode(value));
|
||||
skiaObject.setBlendMode(toSkBlendMode(value));
|
||||
}
|
||||
|
||||
ui.BlendMode _blendMode = ui.BlendMode.srcOver;
|
||||
@ -35,7 +35,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_style = value;
|
||||
_skPaint.setStyle(toSkPaintStyle(value));
|
||||
skiaObject.setStyle(toSkPaintStyle(value));
|
||||
}
|
||||
|
||||
ui.PaintingStyle _style = ui.PaintingStyle.fill;
|
||||
@ -48,7 +48,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_strokeWidth = value;
|
||||
_skPaint.setStrokeWidth(value);
|
||||
skiaObject.setStrokeWidth(value);
|
||||
}
|
||||
|
||||
double _strokeWidth = 0.0;
|
||||
@ -61,7 +61,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_strokeCap = value;
|
||||
_skPaint.setStrokeCap(toSkStrokeCap(value));
|
||||
skiaObject.setStrokeCap(toSkStrokeCap(value));
|
||||
}
|
||||
|
||||
ui.StrokeCap _strokeCap = ui.StrokeCap.butt;
|
||||
@ -74,7 +74,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_strokeJoin = value;
|
||||
_skPaint.setStrokeJoin(toSkStrokeJoin(value));
|
||||
skiaObject.setStrokeJoin(toSkStrokeJoin(value));
|
||||
}
|
||||
|
||||
ui.StrokeJoin _strokeJoin = ui.StrokeJoin.miter;
|
||||
@ -87,7 +87,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_isAntiAlias = value;
|
||||
_skPaint.setAntiAlias(value);
|
||||
skiaObject.setAntiAlias(value);
|
||||
}
|
||||
|
||||
bool _isAntiAlias = true;
|
||||
@ -100,7 +100,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_color = value;
|
||||
_skPaint.setColorInt(value.value);
|
||||
skiaObject.setColorInt(value.value);
|
||||
}
|
||||
|
||||
ui.Color _color = _defaultPaintColor;
|
||||
@ -123,7 +123,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_shader = value as EngineShader?;
|
||||
_skPaint.setShader(_shader?.createSkiaShader());
|
||||
skiaObject.setShader(_shader?.createSkiaShader());
|
||||
}
|
||||
|
||||
EngineShader? _shader;
|
||||
@ -144,7 +144,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
} else {
|
||||
_ckMaskFilter = null;
|
||||
}
|
||||
_skPaint.setMaskFilter(_ckMaskFilter?._skMaskFilter);
|
||||
skiaObject.setMaskFilter(_ckMaskFilter?.skiaObject);
|
||||
}
|
||||
|
||||
ui.MaskFilter? _maskFilter;
|
||||
@ -158,7 +158,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_filterQuality = value;
|
||||
_skPaint.setFilterQuality(toSkFilterQuality(value));
|
||||
skiaObject.setFilterQuality(toSkFilterQuality(value));
|
||||
}
|
||||
|
||||
ui.FilterQuality _filterQuality = ui.FilterQuality.none;
|
||||
@ -173,7 +173,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
final EngineColorFilter? engineValue = value as EngineColorFilter?;
|
||||
_colorFilter = engineValue;
|
||||
_ckColorFilter = engineValue?._toCkColorFilter();
|
||||
_skPaint.setColorFilter(_ckColorFilter?._skColorFilter);
|
||||
skiaObject.setColorFilter(_ckColorFilter?.skiaObject);
|
||||
}
|
||||
|
||||
EngineColorFilter? _colorFilter;
|
||||
@ -187,7 +187,7 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_strokeMiterLimit = value;
|
||||
_skPaint.setStrokeMiter(value);
|
||||
skiaObject.setStrokeMiter(value);
|
||||
}
|
||||
|
||||
double _strokeMiterLimit = 0.0;
|
||||
@ -200,34 +200,40 @@ class CkPaint extends ResurrectableSkiaObject implements ui.Paint {
|
||||
return;
|
||||
}
|
||||
_imageFilter = value as CkImageFilter?;
|
||||
_skPaint.setImageFilter(_imageFilter?._skImageFilter);
|
||||
skiaObject.setImageFilter(_imageFilter?.skiaObject);
|
||||
}
|
||||
|
||||
CkImageFilter? _imageFilter;
|
||||
|
||||
late SkPaint _skPaint;
|
||||
|
||||
@override
|
||||
js.JsObject createDefault() {
|
||||
_skPaint = SkPaint();
|
||||
_skPaint.setAntiAlias(_isAntiAlias);
|
||||
_skPaint.setColorInt(_color.value);
|
||||
return _jsObjectWrapper.wrapSkPaint(_skPaint);
|
||||
SkPaint createDefault() {
|
||||
final SkPaint paint = SkPaint();
|
||||
paint.setAntiAlias(_isAntiAlias);
|
||||
paint.setColorInt(_color.value);
|
||||
return paint;
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject resurrect() {
|
||||
_skPaint = SkPaint();
|
||||
_skPaint.setBlendMode(toSkBlendMode(_blendMode));
|
||||
_skPaint.setStyle(toSkPaintStyle(_style));
|
||||
_skPaint.setStrokeWidth(_strokeWidth);
|
||||
_skPaint.setAntiAlias(_isAntiAlias);
|
||||
_skPaint.setColorInt(_color.value);
|
||||
_skPaint.setShader(_shader?.createSkiaShader());
|
||||
_skPaint.setMaskFilter(_ckMaskFilter?._skMaskFilter);
|
||||
_skPaint.setColorFilter(_ckColorFilter?._skColorFilter);
|
||||
_skPaint.setImageFilter(_imageFilter?._skImageFilter);
|
||||
_skPaint.setFilterQuality(toSkFilterQuality(_filterQuality));
|
||||
return _jsObjectWrapper.wrapSkPaint(_skPaint);
|
||||
SkPaint resurrect() {
|
||||
final SkPaint paint = SkPaint();
|
||||
paint.setBlendMode(toSkBlendMode(_blendMode));
|
||||
paint.setStyle(toSkPaintStyle(_style));
|
||||
paint.setStrokeWidth(_strokeWidth);
|
||||
paint.setAntiAlias(_isAntiAlias);
|
||||
paint.setColorInt(_color.value);
|
||||
paint.setShader(_shader?.createSkiaShader());
|
||||
paint.setMaskFilter(_ckMaskFilter?.skiaObject);
|
||||
paint.setColorFilter(_ckColorFilter?.skiaObject);
|
||||
paint.setImageFilter(_imageFilter?.skiaObject);
|
||||
paint.setFilterQuality(toSkFilterQuality(_filterQuality));
|
||||
return paint;
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkPaint(skiaObject);
|
||||
}
|
||||
|
||||
@ -10,9 +10,6 @@ part of engine;
|
||||
class CkPath implements ui.Path {
|
||||
final SkPath _skPath;
|
||||
|
||||
// TODO(yjbanov): remove this once we're fully @JS-ified.
|
||||
late final js.JsObject _legacyJsObject = _jsObjectWrapper.wrapSkPath(_skPath);
|
||||
|
||||
CkPath() : _skPath = SkPath(), _fillType = ui.PathFillType.nonZero {
|
||||
_skPath.setFillType(toSkFillType(_fillType));
|
||||
}
|
||||
|
||||
@ -5,17 +5,20 @@
|
||||
part of engine;
|
||||
|
||||
class CkPicture implements ui.Picture {
|
||||
final SkiaObject skPicture;
|
||||
final SkPicture _skPicture;
|
||||
final SkiaObject skiaObject;
|
||||
final ui.Rect? cullRect;
|
||||
|
||||
CkPicture(this.skPicture, this.cullRect);
|
||||
CkPicture(SkPicture picture, this.cullRect)
|
||||
: _skPicture = picture,
|
||||
skiaObject = SkPictureSkiaObject(picture);
|
||||
|
||||
@override
|
||||
int get approximateBytesUsed => 0;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
skPicture.delete();
|
||||
skiaObject.delete();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -23,3 +26,15 @@ class CkPicture implements ui.Picture {
|
||||
throw UnsupportedError('Picture.toImage not yet implemented for CanvasKit and HTML');
|
||||
}
|
||||
}
|
||||
|
||||
class SkPictureSkiaObject extends OneShotSkiaObject<SkPicture> {
|
||||
SkPictureSkiaObject(SkPicture picture) : super(picture);
|
||||
|
||||
@override
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkPicture(skiaObject);
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,32 +6,33 @@ part of engine;
|
||||
|
||||
class CkPictureRecorder implements ui.PictureRecorder {
|
||||
ui.Rect? _cullRect;
|
||||
js.JsObject? _recorder;
|
||||
SkPictureRecorder? _skRecorder;
|
||||
CkCanvas? _recordingCanvas;
|
||||
|
||||
CkCanvas? beginRecording(ui.Rect bounds) {
|
||||
CkCanvas beginRecording(ui.Rect bounds) {
|
||||
_cullRect = bounds;
|
||||
_recorder = js.JsObject(canvasKit['SkPictureRecorder']);
|
||||
final js.JsObject skRect = js.JsObject(canvasKit['LTRBRect'],
|
||||
<double>[bounds.left, bounds.top, bounds.right, bounds.bottom]);
|
||||
final js.JsObject skCanvas =
|
||||
_recorder!.callMethod('beginRecording', <js.JsObject>[skRect]);
|
||||
_recordingCanvas = CkCanvas(skCanvas);
|
||||
return _recordingCanvas;
|
||||
final SkPictureRecorder recorder = _skRecorder = SkPictureRecorder();
|
||||
final SkRect skRect = toSkRect(bounds);
|
||||
final SkCanvas skCanvas = recorder.beginRecording(skRect);
|
||||
return _recordingCanvas = CkCanvas(skCanvas);
|
||||
}
|
||||
|
||||
CkCanvas? get recordingCanvas => _recordingCanvas;
|
||||
|
||||
@override
|
||||
ui.Picture endRecording() {
|
||||
final js.JsObject? skPicture =
|
||||
_recorder!.callMethod('finishRecordingAsPicture');
|
||||
_recorder!.callMethod('delete');
|
||||
_recorder = null;
|
||||
final SkPictureRecorder? recorder = _skRecorder;
|
||||
|
||||
return CkPicture(OneShotSkiaObject(skPicture), _cullRect);
|
||||
if (recorder == null) {
|
||||
throw StateError('PictureRecorder is not recording');
|
||||
}
|
||||
|
||||
final SkPicture skPicture = recorder.finishRecordingAsPicture();
|
||||
recorder.delete();
|
||||
_skRecorder = null;
|
||||
return CkPicture(skPicture, _cullRect);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRecording => _recorder != null;
|
||||
bool get isRecording => _skRecorder != null;
|
||||
}
|
||||
|
||||
@ -76,20 +76,30 @@ class SkiaObjectCache {
|
||||
final SkiaObject oldObject = _itemQueue.removeLast();
|
||||
_itemMap.remove(oldObject);
|
||||
oldObject.delete();
|
||||
oldObject.didDelete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An object backed by a [js.JsObject] mapped onto a Skia C++ object in the
|
||||
/// An object backed by a JavaScript object mapped onto a Skia C++ object in the
|
||||
/// WebAssembly heap.
|
||||
///
|
||||
/// These objects are automatically deleted when no longer used.
|
||||
abstract class SkiaObject {
|
||||
abstract class SkiaObject<T> {
|
||||
/// The JavaScript object that's mapped onto a Skia C++ object in the WebAssembly heap.
|
||||
js.JsObject? get skiaObject;
|
||||
T get skiaObject;
|
||||
|
||||
/// The legacy view on the [skiaObject].
|
||||
// TODO(yjbanov): remove this after completing JS-interop migration.
|
||||
js.JsObject? get legacySkiaObject;
|
||||
|
||||
/// Deletes the associated C++ object from the WebAssembly heap.
|
||||
void delete();
|
||||
|
||||
/// Lifecycle method called immediately after calling [delete].
|
||||
///
|
||||
/// This method is used to
|
||||
void didDelete();
|
||||
}
|
||||
|
||||
/// A [SkiaObject] that can resurrect its C++ counterpart.
|
||||
@ -113,9 +123,9 @@ abstract class SkiaObject {
|
||||
/// [resurrect] method.
|
||||
/// - Final delete: if a Dart object is never reused, it is GC'd after its
|
||||
/// underlying C++ object is deleted. This is implemented by [SkiaObjects].
|
||||
abstract class ResurrectableSkiaObject extends SkiaObject {
|
||||
abstract class ResurrectableSkiaObject<T> extends SkiaObject<T> {
|
||||
ResurrectableSkiaObject() {
|
||||
_skiaObject = createDefault();
|
||||
rawSkiaObject = createDefault();
|
||||
if (isResurrectionExpensive) {
|
||||
SkiaObjects.manageExpensive(this);
|
||||
} else {
|
||||
@ -124,20 +134,33 @@ abstract class ResurrectableSkiaObject extends SkiaObject {
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject get skiaObject {
|
||||
if (_skiaObject == null) {
|
||||
_skiaObject = resurrect();
|
||||
if (isResurrectionExpensive) {
|
||||
SkiaObjects.manageExpensive(this);
|
||||
} else {
|
||||
SkiaObjects.manageResurrectable(this);
|
||||
}
|
||||
T get skiaObject => rawSkiaObject ?? _doResurrect();
|
||||
|
||||
T _doResurrect() {
|
||||
final T skiaObject = resurrect();
|
||||
rawSkiaObject = skiaObject;
|
||||
if (isResurrectionExpensive) {
|
||||
SkiaObjects.manageExpensive(this);
|
||||
} else {
|
||||
SkiaObjects.manageResurrectable(this);
|
||||
}
|
||||
return _skiaObject!;
|
||||
return skiaObject;
|
||||
}
|
||||
|
||||
/// Do not use this field outside this class. Use [skiaObject] instead.
|
||||
js.JsObject? _skiaObject;
|
||||
@override
|
||||
void didDelete() {
|
||||
rawSkiaObject = null;
|
||||
}
|
||||
|
||||
/// Returns the current skia object as is without attempting to
|
||||
/// resurrect it.
|
||||
///
|
||||
/// If the returned value is `null`, the corresponding C++ object has
|
||||
/// been deleted.
|
||||
///
|
||||
/// Use this field instead of the [skiaObject] getter when implementing
|
||||
/// the [delete] method.
|
||||
T? rawSkiaObject;
|
||||
|
||||
/// Instantiates a new Skia-backed JavaScript object containing default
|
||||
/// values.
|
||||
@ -145,22 +168,16 @@ abstract class ResurrectableSkiaObject extends SkiaObject {
|
||||
/// The object is expected to represent Flutter's defaults. If Skia uses
|
||||
/// different defaults from those used by Flutter, this method is expected
|
||||
/// initialize the object to Flutter's defaults.
|
||||
js.JsObject createDefault();
|
||||
T createDefault();
|
||||
|
||||
/// Creates a new Skia-backed JavaScript object containing data representing
|
||||
/// the current state of the Dart object.
|
||||
js.JsObject resurrect();
|
||||
T resurrect();
|
||||
|
||||
/// Whether or not it is expensive to resurrect this object.
|
||||
///
|
||||
/// Defaults to false.
|
||||
bool get isResurrectionExpensive => false;
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
_skiaObject!.callMethod('delete');
|
||||
_skiaObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(hterkelsen): [OneShotSkiaObject] is dangerous because it might delete
|
||||
@ -168,26 +185,33 @@ abstract class ResurrectableSkiaObject extends SkiaObject {
|
||||
// use. This issue discusses ways to address this:
|
||||
// https://github.com/flutter/flutter/issues/60401
|
||||
/// A [SkiaObject] which is deleted once and cannot be used again.
|
||||
class OneShotSkiaObject extends SkiaObject {
|
||||
js.JsObject? _skiaObject;
|
||||
abstract class OneShotSkiaObject<T> extends SkiaObject<T> {
|
||||
/// Returns the current skia object as is without attempting to
|
||||
/// resurrect it.
|
||||
///
|
||||
/// If the returned value is `null`, the corresponding C++ object has
|
||||
/// been deleted.
|
||||
///
|
||||
/// Use this field instead of the [skiaObject] getter when implementing
|
||||
/// the [delete] method.
|
||||
T? rawSkiaObject;
|
||||
|
||||
OneShotSkiaObject(this._skiaObject) {
|
||||
OneShotSkiaObject(this.rawSkiaObject) {
|
||||
SkiaObjects.manageOneShot(this);
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject? get skiaObject {
|
||||
if (_skiaObject == null) {
|
||||
T get skiaObject {
|
||||
if (rawSkiaObject == null) {
|
||||
throw StateError('Attempting to use a Skia object that has been freed.');
|
||||
}
|
||||
SkiaObjects.oneShotCache.markUsed(this);
|
||||
return _skiaObject;
|
||||
return rawSkiaObject!;
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
_skiaObject!.callMethod('delete');
|
||||
_skiaObject = null;
|
||||
void didDelete() {
|
||||
rawSkiaObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -267,6 +291,7 @@ class SkiaObjects {
|
||||
for (int i = 0; i < resurrectableObjects.length; i++) {
|
||||
final SkiaObject object = resurrectableObjects[i];
|
||||
object.delete();
|
||||
object.didDelete();
|
||||
}
|
||||
resurrectableObjects.clear();
|
||||
|
||||
|
||||
@ -169,7 +169,9 @@ class CkSurface {
|
||||
|
||||
CkCanvas getCanvas() {
|
||||
final js.JsObject skCanvas = _surface.callMethod('getCanvas');
|
||||
return CkCanvas(skCanvas);
|
||||
return CkCanvas(
|
||||
_jsObjectWrapper.unwrapSkCanvas(skCanvas),
|
||||
);
|
||||
}
|
||||
|
||||
int get context => _glContext;
|
||||
|
||||
@ -18,41 +18,40 @@ class CkParagraphStyle implements ui.ParagraphStyle {
|
||||
ui.StrutStyle? strutStyle,
|
||||
String? ellipsis,
|
||||
ui.Locale? locale,
|
||||
}) {
|
||||
skParagraphStyle = toCkParagraphStyle(
|
||||
textAlign,
|
||||
textDirection,
|
||||
maxLines,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
height,
|
||||
textHeightBehavior,
|
||||
fontWeight,
|
||||
fontStyle,
|
||||
ellipsis,
|
||||
);
|
||||
}) : skParagraphStyle = toSkParagraphStyle(
|
||||
textAlign,
|
||||
textDirection,
|
||||
maxLines,
|
||||
fontFamily,
|
||||
fontSize,
|
||||
height,
|
||||
textHeightBehavior,
|
||||
fontWeight,
|
||||
fontStyle,
|
||||
ellipsis,
|
||||
) {
|
||||
assert(skParagraphStyle != null);
|
||||
_textDirection = textDirection ?? ui.TextDirection.ltr;
|
||||
_fontFamily = fontFamily;
|
||||
}
|
||||
|
||||
js.JsObject? skParagraphStyle;
|
||||
SkParagraphStyle skParagraphStyle;
|
||||
ui.TextDirection? _textDirection;
|
||||
String? _fontFamily;
|
||||
|
||||
static Map<String, dynamic> toCkTextStyle(
|
||||
static SkTextStyleProperties toSkTextStyleProperties(
|
||||
String? fontFamily,
|
||||
double? fontSize,
|
||||
ui.FontWeight? fontWeight,
|
||||
ui.FontStyle? fontStyle,
|
||||
) {
|
||||
final Map<String, dynamic> skTextStyle = <String, dynamic>{};
|
||||
final SkTextStyleProperties skTextStyle = SkTextStyleProperties();
|
||||
if (fontWeight != null || fontStyle != null) {
|
||||
skTextStyle['fontStyle'] = toSkFontStyle(fontWeight, fontStyle);
|
||||
skTextStyle.fontStyle = toSkFontStyle(fontWeight, fontStyle);
|
||||
}
|
||||
|
||||
if (fontSize != null) {
|
||||
skTextStyle['fontSize'] = fontSize;
|
||||
skTextStyle.fontSize = fontSize;
|
||||
}
|
||||
|
||||
if (fontFamily == null ||
|
||||
@ -60,14 +59,14 @@ class CkParagraphStyle implements ui.ParagraphStyle {
|
||||
fontFamily = 'Roboto';
|
||||
}
|
||||
if (skiaFontCollection.fontFamilyOverrides.containsKey(fontFamily)) {
|
||||
fontFamily = skiaFontCollection.fontFamilyOverrides[fontFamily];
|
||||
fontFamily = skiaFontCollection.fontFamilyOverrides[fontFamily]!;
|
||||
}
|
||||
skTextStyle['fontFamilies'] = [fontFamily];
|
||||
skTextStyle.fontFamilies = [fontFamily];
|
||||
|
||||
return skTextStyle;
|
||||
}
|
||||
|
||||
static js.JsObject? toCkParagraphStyle(
|
||||
static SkParagraphStyle toSkParagraphStyle(
|
||||
ui.TextAlign? textAlign,
|
||||
ui.TextDirection? textDirection,
|
||||
int? maxLines,
|
||||
@ -79,70 +78,43 @@ class CkParagraphStyle implements ui.ParagraphStyle {
|
||||
ui.FontStyle? fontStyle,
|
||||
String? ellipsis,
|
||||
) {
|
||||
final Map<String, dynamic> skParagraphStyle = <String, dynamic>{};
|
||||
final SkParagraphStyleProperties properties = SkParagraphStyleProperties();
|
||||
|
||||
if (textAlign != null) {
|
||||
switch (textAlign) {
|
||||
case ui.TextAlign.left:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Left'];
|
||||
break;
|
||||
case ui.TextAlign.right:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Right'];
|
||||
break;
|
||||
case ui.TextAlign.center:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Center'];
|
||||
break;
|
||||
case ui.TextAlign.justify:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Justify'];
|
||||
break;
|
||||
case ui.TextAlign.start:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['Start'];
|
||||
break;
|
||||
case ui.TextAlign.end:
|
||||
skParagraphStyle['textAlign'] = canvasKit['TextAlign']['End'];
|
||||
break;
|
||||
}
|
||||
properties.textAlign = toSkTextAlign(textAlign);
|
||||
}
|
||||
|
||||
if (textDirection != null) {
|
||||
switch (textDirection) {
|
||||
case ui.TextDirection.ltr:
|
||||
skParagraphStyle['textDirection'] = canvasKit['TextDirection']['LTR'];
|
||||
break;
|
||||
case ui.TextDirection.rtl:
|
||||
skParagraphStyle['textDirection'] = canvasKit['TextDirection']['RTL'];
|
||||
break;
|
||||
}
|
||||
properties.textDirection = toSkTextDirection(textDirection);
|
||||
}
|
||||
|
||||
if (height != null) {
|
||||
skParagraphStyle['heightMultiplier'] = height;
|
||||
properties.heightMultiplier = height;
|
||||
}
|
||||
|
||||
if (textHeightBehavior != null) {
|
||||
skParagraphStyle['textHeightBehavior'] = textHeightBehavior.encode();
|
||||
properties.textHeightBehavior = textHeightBehavior.encode();
|
||||
}
|
||||
|
||||
if (maxLines != null) {
|
||||
skParagraphStyle['maxLines'] = maxLines;
|
||||
properties.maxLines = maxLines;
|
||||
}
|
||||
|
||||
if (ellipsis != null) {
|
||||
skParagraphStyle['ellipsis'] = ellipsis;
|
||||
properties.ellipsis = ellipsis;
|
||||
}
|
||||
|
||||
skParagraphStyle['textStyle'] =
|
||||
toCkTextStyle(fontFamily, fontSize, fontWeight, fontStyle);
|
||||
properties.textStyle =
|
||||
toSkTextStyleProperties(fontFamily, fontSize, fontWeight, fontStyle);
|
||||
|
||||
return canvasKit.callMethod(
|
||||
'ParagraphStyle', <js.JsObject>[js.JsObject.jsify(skParagraphStyle)]);
|
||||
return canvasKitJs.ParagraphStyle(properties);
|
||||
}
|
||||
}
|
||||
|
||||
class CkTextStyle implements ui.TextStyle {
|
||||
js.JsObject? skTextStyle;
|
||||
SkTextStyle skTextStyle;
|
||||
|
||||
CkTextStyle({
|
||||
factory CkTextStyle({
|
||||
ui.Color? color,
|
||||
ui.TextDecoration? decoration,
|
||||
ui.Color? decorationColor,
|
||||
@ -163,36 +135,36 @@ class CkTextStyle implements ui.TextStyle {
|
||||
List<ui.Shadow>? shadows,
|
||||
List<ui.FontFeature>? fontFeatures,
|
||||
}) {
|
||||
final js.JsObject style = js.JsObject(js.context['Object']);
|
||||
final SkTextStyleProperties properties = SkTextStyleProperties();
|
||||
|
||||
if (background != null) {
|
||||
style['backgroundColor'] = makeFreshSkColor(background.color);
|
||||
properties.backgroundColor = makeFreshSkColor(background.color);
|
||||
}
|
||||
|
||||
if (color != null) {
|
||||
style['color'] = makeFreshSkColor(color);
|
||||
properties.color = makeFreshSkColor(color);
|
||||
}
|
||||
|
||||
if (decoration != null) {
|
||||
int decorationValue = canvasKit['NoDecoration'];
|
||||
int decorationValue = canvasKitJs.NoDecoration;
|
||||
if (decoration.contains(ui.TextDecoration.underline)) {
|
||||
decorationValue |= canvasKit['UnderlineDecoration'];
|
||||
decorationValue |= canvasKitJs.UnderlineDecoration;
|
||||
}
|
||||
if (decoration.contains(ui.TextDecoration.overline)) {
|
||||
decorationValue |= canvasKit['OverlineDecoration'];
|
||||
decorationValue |= canvasKitJs.OverlineDecoration;
|
||||
}
|
||||
if (decoration.contains(ui.TextDecoration.lineThrough)) {
|
||||
decorationValue |= canvasKit['LineThroughDecoration'];
|
||||
decorationValue |= canvasKitJs.LineThroughDecoration;
|
||||
}
|
||||
style['decoration'] = decorationValue;
|
||||
properties.decoration = decorationValue;
|
||||
}
|
||||
|
||||
if (decorationThickness != null) {
|
||||
style['decorationThickness'] = decorationThickness;
|
||||
properties.decorationThickness = decorationThickness;
|
||||
}
|
||||
|
||||
if (fontSize != null) {
|
||||
style['fontSize'] = fontSize;
|
||||
properties.fontSize = fontSize;
|
||||
}
|
||||
|
||||
if (fontFamily == null ||
|
||||
@ -201,22 +173,22 @@ class CkTextStyle implements ui.TextStyle {
|
||||
}
|
||||
|
||||
if (skiaFontCollection.fontFamilyOverrides.containsKey(fontFamily)) {
|
||||
fontFamily = skiaFontCollection.fontFamilyOverrides[fontFamily];
|
||||
fontFamily = skiaFontCollection.fontFamilyOverrides[fontFamily]!;
|
||||
}
|
||||
List<String?> fontFamilies = <String?>[fontFamily];
|
||||
List<String> fontFamilies = <String>[fontFamily];
|
||||
if (fontFamilyFallback != null &&
|
||||
!fontFamilyFallback.every((font) => fontFamily == font)) {
|
||||
fontFamilies.addAll(fontFamilyFallback);
|
||||
}
|
||||
|
||||
style['fontFamilies'] = js.JsArray.from(fontFamilies);
|
||||
properties.fontFamilies = fontFamilies;
|
||||
|
||||
if (fontWeight != null || fontStyle != null) {
|
||||
style['fontStyle'] = toSkFontStyle(fontWeight, fontStyle);
|
||||
properties.fontStyle = toSkFontStyle(fontWeight, fontStyle);
|
||||
}
|
||||
|
||||
if (foreground != null) {
|
||||
style['foregroundColor'] = makeFreshSkColor(foreground.color);
|
||||
properties.foregroundColor = makeFreshSkColor(foreground.color);
|
||||
}
|
||||
|
||||
// TODO(hterkelsen): Add support for
|
||||
@ -229,67 +201,32 @@ class CkTextStyle implements ui.TextStyle {
|
||||
// - locale
|
||||
// - shadows
|
||||
// - fontFeatures
|
||||
skTextStyle = canvasKit.callMethod('TextStyle', <js.JsObject>[style]);
|
||||
assert(skTextStyle != null);
|
||||
return CkTextStyle._(canvasKitJs.TextStyle(properties));
|
||||
}
|
||||
|
||||
CkTextStyle._(this.skTextStyle);
|
||||
}
|
||||
|
||||
Map<String, js.JsObject?> toSkFontStyle(
|
||||
SkFontStyle toSkFontStyle(
|
||||
ui.FontWeight? fontWeight, ui.FontStyle? fontStyle) {
|
||||
Map<String, js.JsObject?> style = <String, js.JsObject?>{};
|
||||
final style = SkFontStyle();
|
||||
if (fontWeight != null) {
|
||||
switch (fontWeight) {
|
||||
case ui.FontWeight.w100:
|
||||
style['weight'] = canvasKit['FontWeight']['Thin'];
|
||||
break;
|
||||
case ui.FontWeight.w200:
|
||||
style['weight'] = canvasKit['FontWeight']['ExtraLight'];
|
||||
break;
|
||||
case ui.FontWeight.w300:
|
||||
style['weight'] = canvasKit['FontWeight']['Light'];
|
||||
break;
|
||||
case ui.FontWeight.w400:
|
||||
style['weight'] = canvasKit['FontWeight']['Normal'];
|
||||
break;
|
||||
case ui.FontWeight.w500:
|
||||
style['weight'] = canvasKit['FontWeight']['Medium'];
|
||||
break;
|
||||
case ui.FontWeight.w600:
|
||||
style['weight'] = canvasKit['FontWeight']['SemiBold'];
|
||||
break;
|
||||
case ui.FontWeight.w700:
|
||||
style['weight'] = canvasKit['FontWeight']['Bold'];
|
||||
break;
|
||||
case ui.FontWeight.w800:
|
||||
style['weight'] = canvasKit['FontWeight']['ExtraBold'];
|
||||
break;
|
||||
case ui.FontWeight.w900:
|
||||
style['weight'] = canvasKit['FontWeight']['ExtraBlack'];
|
||||
break;
|
||||
}
|
||||
style.weight = toSkFontWeight(fontWeight);
|
||||
}
|
||||
|
||||
if (fontStyle != null) {
|
||||
switch (fontStyle) {
|
||||
case ui.FontStyle.normal:
|
||||
style['slant'] = canvasKit['FontSlant']['Upright'];
|
||||
break;
|
||||
case ui.FontStyle.italic:
|
||||
style['slant'] = canvasKit['FontSlant']['Italic'];
|
||||
break;
|
||||
}
|
||||
style.slant = toSkFontSlant(fontStyle);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
class CkParagraph extends ResurrectableSkiaObject<SkParagraph> implements ui.Paragraph {
|
||||
CkParagraph(
|
||||
this._initialParagraph, this._paragraphStyle, this._paragraphCommands);
|
||||
|
||||
/// The result of calling `build()` on the JS CkParagraphBuilder.
|
||||
///
|
||||
/// This may be invalidated later.
|
||||
final js.JsObject _initialParagraph;
|
||||
final SkParagraph _initialParagraph;
|
||||
|
||||
/// The paragraph style used to build this paragraph.
|
||||
///
|
||||
@ -310,10 +247,10 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
ui.ParagraphConstraints? _lastLayoutConstraints;
|
||||
|
||||
@override
|
||||
js.JsObject createDefault() => _initialParagraph;
|
||||
SkParagraph createDefault() => _initialParagraph;
|
||||
|
||||
@override
|
||||
js.JsObject resurrect() {
|
||||
SkParagraph resurrect() {
|
||||
final builder = CkParagraphBuilder(_paragraphStyle);
|
||||
for (_ParagraphCommand command in _paragraphCommands) {
|
||||
switch (command.type) {
|
||||
@ -329,43 +266,51 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
}
|
||||
}
|
||||
|
||||
final js.JsObject result = builder._buildCkParagraph();
|
||||
final SkParagraph result = builder._buildCkParagraph();
|
||||
if (_lastLayoutConstraints != null) {
|
||||
// We need to set the Skia object early so layout works.
|
||||
_skiaObject = result;
|
||||
rawSkiaObject = result;
|
||||
this.layout(_lastLayoutConstraints!);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
}
|
||||
|
||||
@override
|
||||
js.JsObject get legacySkiaObject => _jsObjectWrapper.wrapSkParagraph(skiaObject);
|
||||
|
||||
@override
|
||||
bool get isResurrectionExpensive => true;
|
||||
|
||||
@override
|
||||
double get alphabeticBaseline =>
|
||||
skiaObject.callMethod('getAlphabeticBaseline');
|
||||
skiaObject.getAlphabeticBaseline();
|
||||
|
||||
@override
|
||||
bool get didExceedMaxLines => skiaObject.callMethod('didExceedMaxLines');
|
||||
bool get didExceedMaxLines => skiaObject.didExceedMaxLines();
|
||||
|
||||
@override
|
||||
double get height => skiaObject.callMethod('getHeight');
|
||||
double get height => skiaObject.getHeight();
|
||||
|
||||
@override
|
||||
double get ideographicBaseline =>
|
||||
skiaObject.callMethod('getIdeographicBaseline');
|
||||
skiaObject.getIdeographicBaseline();
|
||||
|
||||
@override
|
||||
double get longestLine => skiaObject.callMethod('getLongestLine');
|
||||
double get longestLine => skiaObject.getLongestLine();
|
||||
|
||||
@override
|
||||
double get maxIntrinsicWidth => skiaObject.callMethod('getMaxIntrinsicWidth');
|
||||
double get maxIntrinsicWidth => skiaObject.getMaxIntrinsicWidth();
|
||||
|
||||
@override
|
||||
double get minIntrinsicWidth => skiaObject.callMethod('getMinIntrinsicWidth');
|
||||
double get minIntrinsicWidth => skiaObject.getMinIntrinsicWidth();
|
||||
|
||||
@override
|
||||
double get width => skiaObject.callMethod('getMaxWidth');
|
||||
double get width => skiaObject.getMaxWidth();
|
||||
|
||||
// TODO(hterkelsen): Implement placeholders once it's in CanvasKit
|
||||
@override
|
||||
@ -384,49 +329,22 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
return const <ui.TextBox>[];
|
||||
}
|
||||
|
||||
js.JsObject? heightStyle;
|
||||
switch (boxHeightStyle) {
|
||||
case ui.BoxHeightStyle.tight:
|
||||
heightStyle = canvasKit['RectHeightStyle']['Tight'];
|
||||
break;
|
||||
case ui.BoxHeightStyle.max:
|
||||
heightStyle = canvasKit['RectHeightStyle']['Max'];
|
||||
break;
|
||||
default:
|
||||
// TODO(hterkelsen): Support all height styles
|
||||
html.window.console.warn(
|
||||
'We do not support $boxHeightStyle. Defaulting to BoxHeightStyle.tight');
|
||||
heightStyle = canvasKit['RectHeightStyle']['Tight'];
|
||||
break;
|
||||
}
|
||||
|
||||
js.JsObject? widthStyle;
|
||||
switch (boxWidthStyle) {
|
||||
case ui.BoxWidthStyle.tight:
|
||||
widthStyle = canvasKit['RectWidthStyle']['Tight'];
|
||||
break;
|
||||
case ui.BoxWidthStyle.max:
|
||||
widthStyle = canvasKit['RectWidthStyle']['Max'];
|
||||
break;
|
||||
}
|
||||
|
||||
List<js.JsObject> skRects =
|
||||
skiaObject.callMethod('getRectsForRange', <dynamic>[
|
||||
List<SkRect> skRects = skiaObject.getRectsForRange(
|
||||
start,
|
||||
end,
|
||||
heightStyle,
|
||||
widthStyle,
|
||||
]);
|
||||
toSkRectHeightStyle(boxHeightStyle),
|
||||
toSkRectWidthStyle(boxWidthStyle),
|
||||
);
|
||||
|
||||
List<ui.TextBox> result = <ui.TextBox>[];
|
||||
|
||||
for (int i = 0; i < skRects.length; i++) {
|
||||
final js.JsObject rect = skRects[i];
|
||||
final SkRect rect = skRects[i];
|
||||
result.add(ui.TextBox.fromLTRBD(
|
||||
rect['fLeft'],
|
||||
rect['fTop'],
|
||||
rect['fRight'],
|
||||
rect['fBottom'],
|
||||
rect.fLeft,
|
||||
rect.fTop,
|
||||
rect.fRight,
|
||||
rect.fBottom,
|
||||
_paragraphStyle._textDirection!,
|
||||
));
|
||||
}
|
||||
@ -436,19 +354,18 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
|
||||
@override
|
||||
ui.TextPosition getPositionForOffset(ui.Offset offset) {
|
||||
js.JsObject positionWithAffinity =
|
||||
skiaObject.callMethod('getGlyphPositionAtCoordinate', <double>[
|
||||
final SkTextPosition positionWithAffinity =
|
||||
skiaObject.getGlyphPositionAtCoordinate(
|
||||
offset.dx,
|
||||
offset.dy,
|
||||
]);
|
||||
);
|
||||
return fromPositionWithAffinity(positionWithAffinity);
|
||||
}
|
||||
|
||||
@override
|
||||
ui.TextRange getWordBoundary(ui.TextPosition position) {
|
||||
js.JsObject skRange =
|
||||
skiaObject.callMethod('getWordBoundary', <int>[position.offset]);
|
||||
return ui.TextRange(start: skRange['start'], end: skRange['end']);
|
||||
final SkTextRange skRange = skiaObject.getWordBoundary(position.offset);
|
||||
return ui.TextRange(start: skRange.start, end: skRange.end);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -469,7 +386,7 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
// TODO(het): CanvasKit throws an exception when laid out with
|
||||
// a font that wasn't registered.
|
||||
try {
|
||||
skiaObject.callMethod('layout', <double>[width]);
|
||||
skiaObject.layout(width);
|
||||
} catch (e) {
|
||||
html.window.console.warn('CanvasKit threw an exception while laying '
|
||||
'out the paragraph. The font was "${_paragraphStyle._fontFamily}". '
|
||||
@ -492,21 +409,17 @@ class CkParagraph extends ResurrectableSkiaObject implements ui.Paragraph {
|
||||
}
|
||||
|
||||
class CkParagraphBuilder implements ui.ParagraphBuilder {
|
||||
js.JsObject? _paragraphBuilder;
|
||||
final SkParagraphBuilder _paragraphBuilder;
|
||||
final CkParagraphStyle _style;
|
||||
final List<_ParagraphCommand> _commands;
|
||||
|
||||
CkParagraphBuilder(ui.ParagraphStyle style)
|
||||
: _commands = <_ParagraphCommand>[],
|
||||
_style = style as CkParagraphStyle {
|
||||
_paragraphBuilder = canvasKit['ParagraphBuilder'].callMethod(
|
||||
'Make',
|
||||
<js.JsObject?>[
|
||||
_style.skParagraphStyle,
|
||||
skiaFontCollection.skFontMgr,
|
||||
],
|
||||
);
|
||||
}
|
||||
_style = style as CkParagraphStyle,
|
||||
_paragraphBuilder = canvasKitJs.ParagraphBuilder.Make(
|
||||
style.skParagraphStyle,
|
||||
skiaFontCollection.skFontMgr,
|
||||
);
|
||||
|
||||
// TODO(hterkelsen): Implement placeholders.
|
||||
@override
|
||||
@ -524,7 +437,7 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {
|
||||
@override
|
||||
void addText(String text) {
|
||||
_commands.add(_ParagraphCommand.addText(text));
|
||||
_paragraphBuilder!.callMethod('addText', <String>[text]);
|
||||
_paragraphBuilder.addText(text);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -534,10 +447,9 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {
|
||||
}
|
||||
|
||||
/// Builds the CkParagraph with the builder and deletes the builder.
|
||||
js.JsObject _buildCkParagraph() {
|
||||
final js.JsObject result = _paragraphBuilder!.callMethod('build');
|
||||
_paragraphBuilder!.callMethod('delete');
|
||||
_paragraphBuilder = null;
|
||||
SkParagraph _buildCkParagraph() {
|
||||
final SkParagraph result = _paragraphBuilder.build();
|
||||
_paragraphBuilder.delete();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -551,15 +463,14 @@ class CkParagraphBuilder implements ui.ParagraphBuilder {
|
||||
@override
|
||||
void pop() {
|
||||
_commands.add(const _ParagraphCommand.pop());
|
||||
_paragraphBuilder!.callMethod('pop');
|
||||
_paragraphBuilder.pop();
|
||||
}
|
||||
|
||||
@override
|
||||
void pushStyle(ui.TextStyle style) {
|
||||
final CkTextStyle skStyle = style as CkTextStyle;
|
||||
_commands.add(_ParagraphCommand.pushStyle(skStyle));
|
||||
_paragraphBuilder!
|
||||
.callMethod('pushStyle', <js.JsObject?>[skStyle.skTextStyle]);
|
||||
_paragraphBuilder.pushStyle(skStyle.skTextStyle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,22 +15,6 @@ class CanvasKitError extends Error {
|
||||
String toString() => 'CanvasKitError: $message';
|
||||
}
|
||||
|
||||
/// Converts a list of [ui.Color] into the 2d array expected by CanvasKit.
|
||||
js.JsArray<Float32List> makeColorList(List<ui.Color> colors) {
|
||||
var result = js.JsArray<Float32List>();
|
||||
result.length = colors.length;
|
||||
for (var i = 0; i < colors.length; i++) {
|
||||
var color = colors[i];
|
||||
var jsColor = Float32List(4);
|
||||
jsColor[0] = color.red / 255.0;
|
||||
jsColor[1] = color.green / 255.0;
|
||||
jsColor[2] = color.blue / 255.0;
|
||||
jsColor[3] = color.alpha / 255.0;
|
||||
result[i] = jsColor;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
js.JsObject _mallocColorArray() {
|
||||
return canvasKit
|
||||
.callMethod('Malloc', <dynamic>[js.context['Float32Array'], 4]);
|
||||
@ -107,20 +91,12 @@ ui.Rect fromSkRect(js.JsObject skRect) {
|
||||
);
|
||||
}
|
||||
|
||||
ui.TextPosition fromPositionWithAffinity(js.JsObject positionWithAffinity) {
|
||||
if (positionWithAffinity['affinity'] == canvasKit['Affinity']['Upstream']) {
|
||||
return ui.TextPosition(
|
||||
offset: positionWithAffinity['pos'],
|
||||
affinity: ui.TextAffinity.upstream,
|
||||
);
|
||||
} else {
|
||||
assert(positionWithAffinity['affinity'] ==
|
||||
canvasKit['Affinity']['Downstream']);
|
||||
return ui.TextPosition(
|
||||
offset: positionWithAffinity['pos'],
|
||||
affinity: ui.TextAffinity.downstream,
|
||||
);
|
||||
}
|
||||
ui.TextPosition fromPositionWithAffinity(SkTextPosition positionWithAffinity) {
|
||||
final ui.TextAffinity affinity = ui.TextAffinity.values[positionWithAffinity.affinity.value];
|
||||
return ui.TextPosition(
|
||||
offset: positionWithAffinity.pos,
|
||||
affinity: affinity,
|
||||
);
|
||||
}
|
||||
|
||||
js.JsArray<double> makeSkPoint(ui.Offset point) {
|
||||
@ -312,7 +288,7 @@ js.JsArray<double> makeSkiaColorStops(List<double>? colorStops) {
|
||||
}
|
||||
|
||||
void drawSkShadow(
|
||||
js.JsObject skCanvas,
|
||||
SkCanvas skCanvas,
|
||||
CkPath path,
|
||||
ui.Color color,
|
||||
double elevation,
|
||||
@ -331,22 +307,25 @@ void drawSkShadow(
|
||||
ui.Color inAmbient = color.withAlpha((color.alpha * ambientAlpha).round());
|
||||
ui.Color inSpot = color.withAlpha((color.alpha * spotAlpha).round());
|
||||
|
||||
final js.JsObject inTonalColors = js.JsObject.jsify(<String, Float32List>{
|
||||
'ambient': makeFreshSkColor(inAmbient),
|
||||
'spot': makeFreshSkColor(inSpot),
|
||||
});
|
||||
final SkTonalColors inTonalColors = SkTonalColors(
|
||||
ambient: makeFreshSkColor(inAmbient),
|
||||
spot: makeFreshSkColor(inSpot),
|
||||
);
|
||||
|
||||
final js.JsObject tonalColors =
|
||||
canvasKit.callMethod('computeTonalColors', <js.JsObject>[inTonalColors]);
|
||||
final SkTonalColors tonalColors =
|
||||
canvasKitJs.computeTonalColors(inTonalColors);
|
||||
|
||||
skCanvas.callMethod('drawShadow', <dynamic>[
|
||||
path._legacyJsObject,
|
||||
js.JsArray<double>.from(<double>[0, 0, devicePixelRatio * elevation]),
|
||||
js.JsArray<double>.from(
|
||||
<double>[shadowX, shadowY, devicePixelRatio * kLightHeight]),
|
||||
skCanvas.drawShadow(
|
||||
path._skPath,
|
||||
Float32List(3)
|
||||
..[2] = devicePixelRatio * elevation,
|
||||
Float32List(3)
|
||||
..[0] = shadowX
|
||||
..[1] = shadowY
|
||||
..[2] = devicePixelRatio * kLightHeight,
|
||||
devicePixelRatio * kLightRadius,
|
||||
tonalColors['ambient'],
|
||||
tonalColors['spot'],
|
||||
tonalColors.ambient,
|
||||
tonalColors.spot,
|
||||
flags,
|
||||
]);
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,17 +5,8 @@
|
||||
|
||||
part of engine;
|
||||
|
||||
js.JsArray<Float32List> _encodeRawColorList(Int32List rawColors) {
|
||||
final int colorCount = rawColors.length;
|
||||
final List<ui.Color> colors = <ui.Color>[];
|
||||
for (int i = 0; i < colorCount; ++i) {
|
||||
colors.add(ui.Color(rawColors[i]));
|
||||
}
|
||||
return makeColorList(colors);
|
||||
}
|
||||
|
||||
class CkVertices implements ui.Vertices {
|
||||
js.JsObject? skVertices;
|
||||
late SkVertices skVertices;
|
||||
|
||||
CkVertices(
|
||||
ui.VertexMode mode,
|
||||
@ -36,13 +27,13 @@ class CkVertices implements ui.Vertices {
|
||||
throw ArgumentError(
|
||||
'"indices" values must be valid indices in the positions list.');
|
||||
|
||||
final js.JsArray<js.JsArray<double>>? encodedPositions = encodePointList(positions);
|
||||
final js.JsArray<js.JsArray<double>>? encodedTextures =
|
||||
encodePointList(textureCoordinates);
|
||||
final js.JsArray<Float32List>? encodedColors =
|
||||
colors != null ? makeColorList(colors) : null;
|
||||
if (!_init(mode, encodedPositions, encodedTextures, encodedColors, indices))
|
||||
throw ArgumentError('Invalid configuration for vertices.');
|
||||
skVertices = canvasKitJs.MakeSkVertices(
|
||||
toSkVertexMode(mode),
|
||||
toSkPoints2d(positions),
|
||||
textureCoordinates != null ? toSkPoints2d(textureCoordinates) : null,
|
||||
colors != null ? toSkFloatColorList(colors) : null,
|
||||
indices != null ? toUint16List(indices) : null,
|
||||
);
|
||||
}
|
||||
|
||||
CkVertices.raw(
|
||||
@ -64,50 +55,12 @@ class CkVertices implements ui.Vertices {
|
||||
throw ArgumentError(
|
||||
'"indices" values must be valid indices in the positions list.');
|
||||
|
||||
if (!_init(
|
||||
mode,
|
||||
encodeRawPointList(positions) as js.JsArray<js.JsArray<double>>?,
|
||||
encodeRawPointList(textureCoordinates) as js.JsArray<js.JsArray<double>>?,
|
||||
colors != null ? _encodeRawColorList(colors) : null,
|
||||
skVertices = canvasKitJs.MakeSkVertices(
|
||||
toSkVertexMode(mode),
|
||||
rawPointsToSkPoints2d(positions),
|
||||
textureCoordinates != null ? rawPointsToSkPoints2d(textureCoordinates) : null,
|
||||
colors != null ? encodeRawColorList(colors) : null,
|
||||
indices,
|
||||
)) {
|
||||
throw ArgumentError('Invalid configuration for vertices.');
|
||||
}
|
||||
}
|
||||
|
||||
bool _init(
|
||||
ui.VertexMode mode,
|
||||
js.JsArray<js.JsArray<double>>? positions,
|
||||
js.JsArray<js.JsArray<double>>? textureCoordinates,
|
||||
js.JsArray<Float32List>? colors,
|
||||
List<int>? indices) {
|
||||
js.JsObject? skVertexMode;
|
||||
switch (mode) {
|
||||
case ui.VertexMode.triangles:
|
||||
skVertexMode = canvasKit['VertexMode']['Triangles'];
|
||||
break;
|
||||
case ui.VertexMode.triangleStrip:
|
||||
skVertexMode = canvasKit['VertexMode']['TrianglesStrip'];
|
||||
break;
|
||||
case ui.VertexMode.triangleFan:
|
||||
skVertexMode = canvasKit['VertexMode']['TriangleFan'];
|
||||
break;
|
||||
}
|
||||
|
||||
final js.JsObject? vertices =
|
||||
canvasKit.callMethod('MakeSkVertices', <dynamic>[
|
||||
skVertexMode,
|
||||
positions,
|
||||
textureCoordinates,
|
||||
colors,
|
||||
indices,
|
||||
]);
|
||||
|
||||
if (vertices != null) {
|
||||
skVertices = vertices;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,7 +161,7 @@ class GradientLinear extends EngineGradient {
|
||||
return canvasKitJs.SkShader.MakeLinearGradient(
|
||||
toSkPoint(from),
|
||||
toSkPoint(to),
|
||||
toSkIntColorList(colors),
|
||||
toSkFloatColorList(colors),
|
||||
toSkColorStops(colorStops),
|
||||
toSkTileMode(tileMode),
|
||||
);
|
||||
@ -212,12 +212,10 @@ class GradientRadial extends EngineGradient {
|
||||
SkShader createSkiaShader() {
|
||||
assert(experimentalUseSkia);
|
||||
|
||||
var jsColors = makeColorList(colors);
|
||||
|
||||
return canvasKitJs.SkShader.MakeRadialGradient(
|
||||
toSkPoint(center),
|
||||
radius,
|
||||
jsColors,
|
||||
toSkFloatColorList(colors),
|
||||
toSkColorStops(colorStops),
|
||||
toSkTileMode(tileMode),
|
||||
matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null,
|
||||
@ -248,15 +246,12 @@ class GradientConical extends EngineGradient {
|
||||
@override
|
||||
SkShader createSkiaShader() {
|
||||
assert(experimentalUseSkia);
|
||||
|
||||
var jsColors = makeColorList(colors);
|
||||
|
||||
return canvasKitJs.SkShader.MakeTwoPointConicalGradient(
|
||||
toSkPoint(focal),
|
||||
focalRadius,
|
||||
toSkPoint(center),
|
||||
radius,
|
||||
jsColors,
|
||||
toSkFloatColorList(colors),
|
||||
toSkColorStops(colorStops),
|
||||
toSkTileMode(tileMode),
|
||||
matrix4 != null ? toSkMatrixFromFloat32(matrix4!) : null,
|
||||
|
||||
@ -10,6 +10,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
group('CanvasKit API', () {
|
||||
setUpAll(() async {
|
||||
@ -27,6 +29,11 @@ void main() {
|
||||
_filterQualityTests();
|
||||
_blurStyleTests();
|
||||
_tileModeTests();
|
||||
_fillTypeTests();
|
||||
_pathOpTests();
|
||||
_clipOpTests();
|
||||
_pointModeTests();
|
||||
_vertexModeTests();
|
||||
_imageTests();
|
||||
_shaderTests();
|
||||
_paintTests();
|
||||
@ -39,14 +46,15 @@ void main() {
|
||||
_toSkColorStopsTests();
|
||||
_toSkMatrixFromFloat32Tests();
|
||||
_skSkRectTests();
|
||||
_skVerticesTests();
|
||||
group('SkPath', () {
|
||||
_pathTests();
|
||||
});
|
||||
},
|
||||
// This test failed on iOS Safari.
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
skip: (browserEngine == BrowserEngine.webkit &&
|
||||
operatingSystem == OperatingSystem.iOs));
|
||||
group('SkCanvas', () {
|
||||
_canvasTests();
|
||||
});
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
}, skip: isIosSafari);
|
||||
}
|
||||
|
||||
void _blendModeTests() {
|
||||
@ -174,6 +182,77 @@ void _tileModeTests() {
|
||||
});
|
||||
}
|
||||
|
||||
void _fillTypeTests() {
|
||||
test('fill type mapping is correct', () {
|
||||
expect(canvasKitJs.FillType.Winding.value, ui.PathFillType.nonZero.index);
|
||||
expect(canvasKitJs.FillType.EvenOdd.value, ui.PathFillType.evenOdd.index);
|
||||
});
|
||||
|
||||
test('ui.PathFillType converts to SkFillType', () {
|
||||
for (ui.PathFillType type in ui.PathFillType.values) {
|
||||
expect(toSkFillType(type).value, type.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _pathOpTests() {
|
||||
// TODO(yjbanov): https://github.com/flutter/flutter/issues/61403
|
||||
// test('path op mapping is correct', () {
|
||||
// expect(canvasKitJs.PathOp.Difference.value, ui.PathOperation.difference.index);
|
||||
// expect(canvasKitJs.PathOp.Intersect.value, ui.PathOperation.intersect.index);
|
||||
// expect(canvasKitJs.PathOp.Union.value, ui.PathOperation.union.index);
|
||||
// expect(canvasKitJs.PathOp.XOR.value, ui.PathOperation.xor.index);
|
||||
// expect(canvasKitJs.PathOp.ReverseDifference, ui.PathOperation.reverseDifference.index);
|
||||
// });
|
||||
|
||||
// test('ui.PathOperation converts to SkPathOp', () {
|
||||
// for (ui.PathOperation op in ui.PathOperation.values) {
|
||||
// expect(toSkPathOp(op).value, op.index);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
void _clipOpTests() {
|
||||
test('clip op mapping is correct', () {
|
||||
expect(canvasKitJs.ClipOp.Difference.value, ui.ClipOp.difference.index);
|
||||
expect(canvasKitJs.ClipOp.Intersect.value, ui.ClipOp.intersect.index);
|
||||
});
|
||||
|
||||
test('ui.ClipOp converts to SkClipOp', () {
|
||||
for (ui.ClipOp op in ui.ClipOp.values) {
|
||||
expect(toSkClipOp(op).value, op.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _pointModeTests() {
|
||||
test('point mode mapping is correct', () {
|
||||
expect(canvasKitJs.PointMode.Points.value, ui.PointMode.points.index);
|
||||
expect(canvasKitJs.PointMode.Lines.value, ui.PointMode.lines.index);
|
||||
expect(canvasKitJs.PointMode.Polygon.value, ui.PointMode.polygon.index);
|
||||
});
|
||||
|
||||
test('ui.PointMode converts to SkPointMode', () {
|
||||
for (ui.PointMode op in ui.PointMode.values) {
|
||||
expect(toSkPointMode(op).value, op.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _vertexModeTests() {
|
||||
test('vertex mode mapping is correct', () {
|
||||
expect(canvasKitJs.VertexMode.Triangles.value, ui.VertexMode.triangles.index);
|
||||
expect(canvasKitJs.VertexMode.TrianglesStrip.value, ui.VertexMode.triangleStrip.index);
|
||||
expect(canvasKitJs.VertexMode.TriangleFan.value, ui.VertexMode.triangleFan.index);
|
||||
});
|
||||
|
||||
test('ui.VertexMode converts to SkVertexMode', () {
|
||||
for (ui.VertexMode op in ui.VertexMode.values) {
|
||||
expect(toSkVertexMode(op).value, op.index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _imageTests() {
|
||||
test('MakeAnimatedImageFromEncoded makes a non-animated image', () {
|
||||
final SkAnimatedImage nonAnimated = canvasKitJs.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
@ -247,7 +326,9 @@ SkShader _makeTestShader() {
|
||||
return canvasKitJs.SkShader.MakeLinearGradient(
|
||||
Float32List.fromList([0, 0]),
|
||||
Float32List.fromList([1, 1]),
|
||||
Uint32List.fromList(<int>[0x000000FF]),
|
||||
[
|
||||
Float32List.fromList([255, 0, 0, 255]),
|
||||
],
|
||||
Float32List.fromList([0, 1]),
|
||||
canvasKitJs.TileMode.Repeat,
|
||||
);
|
||||
@ -444,6 +525,15 @@ void _toSkMatrixFromFloat32Tests() {
|
||||
});
|
||||
}
|
||||
|
||||
SkPath _testClosedSkPath() {
|
||||
return SkPath()
|
||||
..moveTo(10, 10)
|
||||
..lineTo(20, 10)
|
||||
..lineTo(20, 20)
|
||||
..lineTo(10, 20)
|
||||
..close();
|
||||
}
|
||||
|
||||
void _pathTests() {
|
||||
SkPath path;
|
||||
|
||||
@ -471,15 +561,6 @@ void _pathTests() {
|
||||
);
|
||||
});
|
||||
|
||||
SkPath _testClosedSkPath() {
|
||||
return SkPath()
|
||||
..moveTo(10, 10)
|
||||
..lineTo(20, 10)
|
||||
..lineTo(20, 20)
|
||||
..lineTo(10, 20)
|
||||
..close();
|
||||
}
|
||||
|
||||
test('addPath', () {
|
||||
path.addPath(_testClosedSkPath(), 1, 0, 0, 0, 1, 0, 0, 0, 0, false);
|
||||
});
|
||||
@ -692,6 +773,411 @@ void _skSkRectTests() {
|
||||
});
|
||||
}
|
||||
|
||||
SkVertices _testVertices() {
|
||||
return canvasKitJs.MakeSkVertices(
|
||||
canvasKitJs.VertexMode.Triangles,
|
||||
[
|
||||
Float32List.fromList([0, 0]),
|
||||
Float32List.fromList([10, 10]),
|
||||
Float32List.fromList([0, 20]),
|
||||
],
|
||||
[
|
||||
Float32List.fromList([0, 0]),
|
||||
Float32List.fromList([10, 10]),
|
||||
Float32List.fromList([0, 20]),
|
||||
],
|
||||
[
|
||||
Float32List.fromList([255, 0, 0, 255]),
|
||||
Float32List.fromList([0, 255, 0, 255]),
|
||||
Float32List.fromList([0, 0, 255, 255]),
|
||||
],
|
||||
Uint16List.fromList([0, 1, 2]),
|
||||
);
|
||||
}
|
||||
|
||||
void _skVerticesTests() {
|
||||
test('SkVertices', () {
|
||||
expect(_testVertices(), isNotNull);
|
||||
});
|
||||
}
|
||||
|
||||
void _canvasTests() {
|
||||
SkPictureRecorder recorder;
|
||||
SkCanvas canvas;
|
||||
|
||||
setUp(() {
|
||||
recorder = SkPictureRecorder();
|
||||
canvas = recorder.beginRecording(SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
));
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
expect(recorder.finishRecordingAsPicture(), isNotNull);
|
||||
});
|
||||
|
||||
test('save/getSaveCount/restore/restoreToCount', () {
|
||||
expect(canvas.save(), 1);
|
||||
expect(canvas.save(), 2);
|
||||
expect(canvas.save(), 3);
|
||||
expect(canvas.save(), 4);
|
||||
expect(canvas.getSaveCount(), 5);
|
||||
canvas.restoreToCount(2);
|
||||
expect(canvas.getSaveCount(), 2);
|
||||
canvas.restore();
|
||||
expect(canvas.getSaveCount(), 1);
|
||||
});
|
||||
|
||||
test('saveLayer', () {
|
||||
canvas.saveLayer(
|
||||
SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('SkCanvasSaveLayerWithoutBoundsOverride.saveLayer', () {
|
||||
final SkCanvasSaveLayerWithoutBoundsOverride override = debugJsObjectWrapper.castToSkCanvasSaveLayerWithoutBoundsOverride(canvas);
|
||||
override.saveLayer(SkPaint());
|
||||
});
|
||||
|
||||
test('SkCanvasSaveLayerWithFilterOverride.saveLayer', () {
|
||||
final SkCanvasSaveLayerWithFilterOverride override = debugJsObjectWrapper.castToSkCanvasSaveLayerWithFilterOverride(canvas);
|
||||
override.saveLayer(
|
||||
SkPaint(),
|
||||
canvasKitJs.SkImageFilter.MakeBlur(1, 2, canvasKitJs.TileMode.Repeat, null),
|
||||
0,
|
||||
SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test('clear', () {
|
||||
canvas.clear(Float32List.fromList([0, 0, 0, 0]));
|
||||
});
|
||||
|
||||
test('clipPath', () {
|
||||
canvas.clipPath(
|
||||
_testClosedSkPath(),
|
||||
canvasKitJs.ClipOp.Intersect,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
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,
|
||||
),
|
||||
canvasKitJs.ClipOp.Intersect,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('clipRect', () {
|
||||
canvas.clipRect(
|
||||
SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
),
|
||||
canvasKitJs.ClipOp.Intersect,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('drawArc', () {
|
||||
canvas.drawArc(
|
||||
SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 50,
|
||||
),
|
||||
0,
|
||||
100,
|
||||
true,
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawAtlas', () {
|
||||
final SkAnimatedImage image = canvasKitJs.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
canvas.drawAtlas(
|
||||
image.getCurrentFrame(),
|
||||
Float32List.fromList([0, 0, 1, 1]),
|
||||
Float32List.fromList([1, 0, 2, 3]),
|
||||
SkPaint(),
|
||||
canvasKitJs.BlendMode.SrcOver,
|
||||
[
|
||||
Float32List.fromList([0, 0, 0, 1]),
|
||||
Float32List.fromList([1, 1, 1, 1]),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('drawCircle', () {
|
||||
canvas.drawCircle(1, 2, 3, SkPaint());
|
||||
});
|
||||
|
||||
test('drawColorInt', () {
|
||||
canvas.drawColorInt(0xFFFFFFFF, canvasKitJs.BlendMode.SoftLight);
|
||||
});
|
||||
|
||||
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,
|
||||
),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawImage', () {
|
||||
final SkAnimatedImage image = canvasKitJs.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
canvas.drawImage(
|
||||
image.getCurrentFrame(),
|
||||
10,
|
||||
20,
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawImageRect', () {
|
||||
final SkAnimatedImage image = canvasKitJs.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
canvas.drawImageRect(
|
||||
image.getCurrentFrame(),
|
||||
SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1),
|
||||
SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1),
|
||||
SkPaint(),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test('drawImageNine', () {
|
||||
final SkAnimatedImage image = canvasKitJs.MakeAnimatedImageFromEncoded(kTransparentImage);
|
||||
canvas.drawImageNine(
|
||||
image.getCurrentFrame(),
|
||||
SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1),
|
||||
SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawLine', () {
|
||||
canvas.drawLine(0, 1, 2, 3, SkPaint());
|
||||
});
|
||||
|
||||
test('drawOval', () {
|
||||
canvas.drawOval(SkRect(fLeft: 0, fTop: 0, fRight: 1, fBottom: 1), SkPaint());
|
||||
});
|
||||
|
||||
test('drawPaint', () {
|
||||
canvas.drawPaint(SkPaint());
|
||||
});
|
||||
|
||||
test('drawPath', () {
|
||||
canvas.drawPath(
|
||||
_testClosedSkPath(),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawPoints', () {
|
||||
canvas.drawPoints(
|
||||
canvasKitJs.PointMode.Lines,
|
||||
Float32List.fromList([0, 0, 10, 10, 0, 10]),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
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,
|
||||
),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawRect', () {
|
||||
canvas.drawRect(
|
||||
SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
),
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('drawShadow', () {
|
||||
for (int flags in const <int>[0x01, 0x00]) {
|
||||
const double devicePixelRatio = 2.0;
|
||||
const double elevation = 4.0;
|
||||
const double ambientAlpha = 0.039;
|
||||
const double spotAlpha = 0.25;
|
||||
|
||||
final SkPath path = _testClosedSkPath();
|
||||
final ui.Rect bounds = path.getBounds().toRect();
|
||||
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 inSpot = color.withAlpha((color.alpha * spotAlpha).round());
|
||||
|
||||
final SkTonalColors inTonalColors = SkTonalColors(
|
||||
ambient: makeFreshSkColor(inAmbient),
|
||||
spot: makeFreshSkColor(inSpot),
|
||||
);
|
||||
|
||||
final SkTonalColors tonalColors =
|
||||
canvasKitJs.computeTonalColors(inTonalColors);
|
||||
|
||||
canvas.drawShadow(
|
||||
path,
|
||||
Float32List(3)
|
||||
..[2] = devicePixelRatio * elevation,
|
||||
Float32List(3)
|
||||
..[0] = shadowX
|
||||
..[1] = shadowY
|
||||
..[2] = devicePixelRatio * kLightHeight,
|
||||
devicePixelRatio * kLightRadius,
|
||||
tonalColors.ambient,
|
||||
tonalColors.spot,
|
||||
flags,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('drawVertices', () {
|
||||
canvas.drawVertices(
|
||||
_testVertices(),
|
||||
canvasKitJs.BlendMode.SrcOver,
|
||||
SkPaint(),
|
||||
);
|
||||
});
|
||||
|
||||
test('rotate', () {
|
||||
canvas.rotate(5, 10, 20);
|
||||
});
|
||||
|
||||
test('scale', () {
|
||||
canvas.scale(2, 3);
|
||||
});
|
||||
|
||||
test('skew', () {
|
||||
canvas.skew(4, 5);
|
||||
});
|
||||
|
||||
test('concat', () {
|
||||
canvas.concat(toSkMatrixFromFloat32(Matrix4.identity().storage));
|
||||
});
|
||||
|
||||
test('translate', () {
|
||||
canvas.translate(4, 5);
|
||||
});
|
||||
|
||||
test('flush', () {
|
||||
canvas.flush();
|
||||
});
|
||||
|
||||
test('drawPicture', () {
|
||||
final SkPictureRecorder otherRecorder = SkPictureRecorder();
|
||||
final SkCanvas otherCanvas = otherRecorder.beginRecording(SkRect(
|
||||
fLeft: 0,
|
||||
fTop: 0,
|
||||
fRight: 100,
|
||||
fBottom: 100,
|
||||
));
|
||||
otherCanvas.drawLine(0, 0, 10, 10, SkPaint());
|
||||
canvas.drawPicture(otherRecorder.finishRecordingAsPicture());
|
||||
});
|
||||
|
||||
test('drawParagraph', () {
|
||||
final CkParagraphBuilder builder = CkParagraphBuilder(
|
||||
CkParagraphStyle(),
|
||||
);
|
||||
builder.addText('Hello');
|
||||
final CkParagraph paragraph = builder.build();
|
||||
paragraph.layout(const ui.ParagraphConstraints(width: 100));
|
||||
canvas.drawParagraph(
|
||||
debugJsObjectWrapper.unwrapSkParagraph(paragraph.legacySkiaObject),
|
||||
10,
|
||||
20,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
final Uint8List kTransparentImage = Uint8List.fromList(<int>[
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x49,
|
||||
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x06,
|
||||
|
||||
11
lib/web_ui/test/canvaskit/common.dart
Normal file
11
lib/web_ui/test/canvaskit/common.dart
Normal file
@ -0,0 +1,11 @@
|
||||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.6
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
/// Whether we are running on iOS Safari.
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
bool get isIosSafari => browserEngine == BrowserEngine.webkit &&
|
||||
operatingSystem == OperatingSystem.iOs;
|
||||
@ -8,6 +8,8 @@ import 'package:test/test.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
group('Path Metrics', () {
|
||||
setUpAll(() async {
|
||||
@ -65,9 +67,5 @@ void main() {
|
||||
expect(() => iter1.current, throwsRangeError);
|
||||
expect(() => iter2.current, throwsRangeError);
|
||||
});
|
||||
},
|
||||
// This test failed on iOS Safari.
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
skip: (browserEngine == BrowserEngine.webkit &&
|
||||
operatingSystem == OperatingSystem.iOs));
|
||||
}, skip: isIosSafari); // TODO: https://github.com/flutter/flutter/issues/60040
|
||||
}
|
||||
|
||||
@ -8,10 +8,25 @@ import 'dart:js';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
import 'common.dart';
|
||||
|
||||
void main() {
|
||||
group('skia_objects_cache', () {
|
||||
_tests();
|
||||
// TODO: https://github.com/flutter/flutter/issues/60040
|
||||
}, skip: isIosSafari);
|
||||
}
|
||||
|
||||
void _tests() {
|
||||
SkiaObjects.maximumCacheSize = 4;
|
||||
|
||||
setUpAll(() async {
|
||||
await ui.webOnlyInitializePlatform();
|
||||
});
|
||||
|
||||
group(ResurrectableSkiaObject, () {
|
||||
test('implements create, cache, delete, resurrect, delete lifecycle', () {
|
||||
int addPostFrameCallbackCount = 0;
|
||||
@ -30,7 +45,7 @@ void main() {
|
||||
expect(testObject.deleteCount, 0);
|
||||
|
||||
// Check that the getter does not have side-effects
|
||||
final JsObject skiaObject1 = testObject.skiaObject;
|
||||
final JsObject skiaObject1 = testObject.legacySkiaObject;
|
||||
expect(skiaObject1, isNotNull);
|
||||
expect(SkiaObjects.resurrectableObjects.single, testObject);
|
||||
expect(testObject.createDefaultCount, 1);
|
||||
@ -46,7 +61,7 @@ void main() {
|
||||
expect(testObject.deleteCount, 1);
|
||||
|
||||
// Trigger resurrect
|
||||
final JsObject skiaObject2 = testObject.skiaObject;
|
||||
final JsObject skiaObject2 = testObject.legacySkiaObject;
|
||||
expect(skiaObject2, isNotNull);
|
||||
expect(skiaObject2, isNot(same(skiaObject1)));
|
||||
expect(SkiaObjects.resurrectableObjects.single, testObject);
|
||||
@ -96,20 +111,12 @@ void main() {
|
||||
|
||||
group(OneShotSkiaObject, () {
|
||||
test('is added to SkiaObjects cache', () {
|
||||
int deleteCount = 0;
|
||||
JsObject _makeJsObject() {
|
||||
return JsObject.jsify({
|
||||
'delete': allowInterop(() {
|
||||
deleteCount++;
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
OneShotSkiaObject object1 = OneShotSkiaObject(_makeJsObject());
|
||||
TestOneShotSkiaObject.deleteCount = 0;
|
||||
OneShotSkiaObject object1 = TestOneShotSkiaObject();
|
||||
expect(SkiaObjects.oneShotCache.length, 1);
|
||||
expect(SkiaObjects.oneShotCache.debugContains(object1), isTrue);
|
||||
|
||||
OneShotSkiaObject object2 = OneShotSkiaObject(_makeJsObject());
|
||||
OneShotSkiaObject object2 = TestOneShotSkiaObject();
|
||||
expect(SkiaObjects.oneShotCache.length, 2);
|
||||
expect(SkiaObjects.oneShotCache.debugContains(object2), isTrue);
|
||||
|
||||
@ -119,14 +126,14 @@ void main() {
|
||||
expect(SkiaObjects.oneShotCache.debugContains(object2), isTrue);
|
||||
|
||||
// Add 3 more objects to the cache to overflow it.
|
||||
OneShotSkiaObject(_makeJsObject());
|
||||
OneShotSkiaObject(_makeJsObject());
|
||||
OneShotSkiaObject(_makeJsObject());
|
||||
TestOneShotSkiaObject();
|
||||
TestOneShotSkiaObject();
|
||||
TestOneShotSkiaObject();
|
||||
expect(SkiaObjects.oneShotCache.length, 5);
|
||||
expect(SkiaObjects.cachesToResize.length, 1);
|
||||
|
||||
SkiaObjects.postFrameCleanUp();
|
||||
expect(deleteCount, 2);
|
||||
expect(TestOneShotSkiaObject.deleteCount, 2);
|
||||
expect(SkiaObjects.oneShotCache.length, 3);
|
||||
expect(SkiaObjects.oneShotCache.debugContains(object1), isFalse);
|
||||
expect(SkiaObjects.oneShotCache.debugContains(object2), isFalse);
|
||||
@ -134,7 +141,22 @@ void main() {
|
||||
});
|
||||
}
|
||||
|
||||
class TestSkiaObject extends ResurrectableSkiaObject {
|
||||
class TestOneShotSkiaObject extends OneShotSkiaObject<SkPaint> {
|
||||
static int deleteCount = 0;
|
||||
|
||||
TestOneShotSkiaObject() : super(SkPaint());
|
||||
|
||||
@override
|
||||
JsObject get legacySkiaObject => debugJsObjectWrapper.wrapSkPaint(skiaObject);
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
deleteCount++;
|
||||
}
|
||||
}
|
||||
|
||||
class TestSkiaObject extends ResurrectableSkiaObject<SkPaint> {
|
||||
int createDefaultCount = 0;
|
||||
int resurrectCount = 0;
|
||||
int deleteCount = 0;
|
||||
@ -143,26 +165,27 @@ class TestSkiaObject extends ResurrectableSkiaObject {
|
||||
|
||||
TestSkiaObject({this.isExpensive = false});
|
||||
|
||||
JsObject _makeJsObject() {
|
||||
return JsObject.jsify({
|
||||
'delete': allowInterop(() {
|
||||
deleteCount++;
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
JsObject createDefault() {
|
||||
SkPaint createDefault() {
|
||||
createDefaultCount++;
|
||||
return _makeJsObject();
|
||||
return SkPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
JsObject resurrect() {
|
||||
SkPaint resurrect() {
|
||||
resurrectCount++;
|
||||
return _makeJsObject();
|
||||
return SkPaint();
|
||||
}
|
||||
|
||||
@override
|
||||
void delete() {
|
||||
rawSkiaObject?.delete();
|
||||
deleteCount++;
|
||||
}
|
||||
|
||||
@override
|
||||
JsObject get legacySkiaObject => debugJsObjectWrapper.wrapSkPaint(skiaObject);
|
||||
|
||||
@override
|
||||
bool get isResurrectionExpensive => isExpensive;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user