Proper memory management in Skwasm (flutter/engine#42328)
This fixes https://github.com/flutter/flutter/issues/127243. This ensures that native skwasm objects are cleaned up when their associated dart-side objects are garbage collected.
This commit is contained in:
@@ -2024,6 +2024,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.da
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart + ../../../flutter/LICENSE
|
||||
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart + ../../../flutter/LICENSE
|
||||
@@ -4682,6 +4683,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/font_collection.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/image.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/layers.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/memory.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paint.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/paragraph.dart
|
||||
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart
|
||||
|
||||
@@ -3384,45 +3384,6 @@ extension JsConstructorExtension on JsConstructor {
|
||||
String get name => _name.toDart;
|
||||
}
|
||||
|
||||
/// Attaches a weakly referenced object to another object and calls a finalizer
|
||||
/// with the latter when weakly referenced object is garbage collected.
|
||||
///
|
||||
/// We use this to delete Skia objects when their "Ck" wrapper is garbage
|
||||
/// collected.
|
||||
///
|
||||
/// Example sequence of events:
|
||||
///
|
||||
/// 1. A (CkPaint, SkPaint) pair created.
|
||||
/// 2. The paint is used to paint some picture.
|
||||
/// 3. CkPaint is dropped by the app.
|
||||
/// 4. GC decides to perform a GC cycle and collects CkPaint.
|
||||
/// 5. The finalizer function is called with the SkPaint as the sole argument.
|
||||
/// 6. We call `delete` on SkPaint.
|
||||
@JS('window.FinalizationRegistry')
|
||||
@staticInterop
|
||||
class SkObjectFinalizationRegistry {}
|
||||
|
||||
SkObjectFinalizationRegistry createSkObjectFinalizationRegistry(JSFunction cleanup) {
|
||||
return js_util.callConstructor(
|
||||
_finalizationRegistryConstructor!.toObjectShallow,
|
||||
<Object>[cleanup],
|
||||
);
|
||||
}
|
||||
|
||||
extension SkObjectFinalizationRegistryExtension on SkObjectFinalizationRegistry {
|
||||
@JS('register')
|
||||
external JSVoid _register(JSAny ckObject, JSAny skObject);
|
||||
void register(Object ckObject, Object skObject) =>
|
||||
_register(ckObject.toJSAnyShallow, skObject.toJSAnyShallow);
|
||||
}
|
||||
|
||||
@JS('window.FinalizationRegistry')
|
||||
external JSAny? get _finalizationRegistryConstructor;
|
||||
|
||||
/// Whether the current browser supports `FinalizationRegistry`.
|
||||
bool browserSupportsFinalizationRegistry =
|
||||
_finalizationRegistryConstructor != null;
|
||||
|
||||
@JS()
|
||||
@staticInterop
|
||||
class SkData {}
|
||||
|
||||
@@ -4,13 +4,23 @@
|
||||
|
||||
import 'dart:js_interop';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../../engine.dart' show Instrumentation;
|
||||
import 'canvaskit_api.dart';
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
/// Collects native objects that weren't explicitly disposed of using
|
||||
/// [UniqueRef.dispose] or [CountedRef.unref].
|
||||
SkObjectFinalizationRegistry _finalizationRegistry = createSkObjectFinalizationRegistry(
|
||||
///
|
||||
/// We use this to delete Skia objects when their "Ck" wrapper is garbage
|
||||
/// collected.
|
||||
///
|
||||
/// Example sequence of events:
|
||||
///
|
||||
/// 1. A (CkPaint, SkPaint) pair created.
|
||||
/// 2. The paint is used to paint some picture.
|
||||
/// 3. CkPaint is dropped by the app.
|
||||
/// 4. GC decides to perform a GC cycle and collects CkPaint.
|
||||
/// 5. The finalizer function is called with the SkPaint as the sole argument.
|
||||
/// 6. We call `delete` on SkPaint.
|
||||
DomFinalizationRegistry _finalizationRegistry = DomFinalizationRegistry(
|
||||
(UniqueRef<Object> uniq) {
|
||||
uniq.collect();
|
||||
}.toJS
|
||||
@@ -18,7 +28,7 @@ SkObjectFinalizationRegistry _finalizationRegistry = createSkObjectFinalizationR
|
||||
|
||||
NativeMemoryFinalizationRegistry nativeMemoryFinalizationRegistry = NativeMemoryFinalizationRegistry();
|
||||
|
||||
/// An indirection to [SkObjectFinalizationRegistry] to enable tests provide a
|
||||
/// An indirection to [DomFinalizationRegistry] to enable tests provide a
|
||||
/// mock implementation of a finalization registry.
|
||||
class NativeMemoryFinalizationRegistry {
|
||||
void register(Object owner, UniqueRef<Object> ref) {
|
||||
|
||||
@@ -3435,3 +3435,35 @@ class DomTextDecoder {
|
||||
extension DomTextDecoderExtension on DomTextDecoder {
|
||||
external JSString decode(JSTypedArray buffer);
|
||||
}
|
||||
|
||||
@JS('window.FinalizationRegistry')
|
||||
@staticInterop
|
||||
class DomFinalizationRegistry {
|
||||
external factory DomFinalizationRegistry(JSFunction cleanup);
|
||||
}
|
||||
|
||||
extension DomFinalizationRegistryExtension on DomFinalizationRegistry {
|
||||
@JS('register')
|
||||
external JSVoid _register1(JSAny target, JSAny value);
|
||||
|
||||
@JS('register')
|
||||
external JSVoid _register2(JSAny target, JSAny value, JSAny token);
|
||||
void register(Object target, Object value, [Object? token]) {
|
||||
if (token != null) {
|
||||
_register2(target.toJSAnyShallow, value.toJSAnyShallow, token.toJSAnyShallow);
|
||||
} else {
|
||||
_register1(target.toJSAnyShallow, value.toJSAnyShallow);
|
||||
}
|
||||
}
|
||||
|
||||
@JS('unregister')
|
||||
external JSVoid _unregister(JSAny token);
|
||||
void unregister(Object token) => _unregister(token.toJSAnyShallow);
|
||||
}
|
||||
|
||||
@JS('window.FinalizationRegistry')
|
||||
external JSAny? get _finalizationRegistryConstructor;
|
||||
|
||||
/// Whether the current browser supports `FinalizationRegistry`.
|
||||
bool browserSupportsFinalizationRegistry =
|
||||
_finalizationRegistryConstructor != null;
|
||||
|
||||
@@ -15,6 +15,7 @@ export 'skwasm_impl/filters.dart';
|
||||
export 'skwasm_impl/font_collection.dart';
|
||||
export 'skwasm_impl/image.dart';
|
||||
export 'skwasm_impl/layers.dart';
|
||||
export 'skwasm_impl/memory.dart';
|
||||
export 'skwasm_impl/paint.dart';
|
||||
export 'skwasm_impl/paragraph.dart';
|
||||
export 'skwasm_impl/path.dart';
|
||||
|
||||
@@ -18,9 +18,9 @@ class SkwasmCanvas implements SceneCanvas {
|
||||
SkwasmCanvas.fromHandle(this._handle);
|
||||
CanvasHandle _handle;
|
||||
|
||||
void delete() {
|
||||
canvasDestroy(_handle);
|
||||
}
|
||||
// Note that we do not need to deal with the finalizer registry here, because
|
||||
// the underlying native skia object is tied directly to the lifetime of the
|
||||
// associated SkPictureRecorder.
|
||||
|
||||
@override
|
||||
void save() {
|
||||
|
||||
@@ -9,8 +9,8 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
class SkwasmImageFilter implements SceneImageFilter {
|
||||
SkwasmImageFilter._(this.handle);
|
||||
class SkwasmImageFilter extends SkwasmObjectWrapper<RawImageFilter> implements SceneImageFilter {
|
||||
SkwasmImageFilter._(ImageFilterHandle handle) : super(handle, _registry);
|
||||
|
||||
factory SkwasmImageFilter.blur({
|
||||
double sigmaX = 0.0,
|
||||
@@ -60,15 +60,8 @@ class SkwasmImageFilter implements SceneImageFilter {
|
||||
return SkwasmImageFilter._(imageFilterCompose(nativeOuter.handle, nativeInner.handle));
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
imageFilterDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
final ImageFilterHandle handle;
|
||||
bool _isDisposed = false;
|
||||
static final SkwasmFinalizationRegistry<RawImageFilter> _registry =
|
||||
SkwasmFinalizationRegistry<RawImageFilter>(imageFilterDispose);
|
||||
|
||||
@override
|
||||
ui.Rect filterBounds(ui.Rect inputBounds) => withStackScope((StackScope scope) {
|
||||
@@ -78,8 +71,8 @@ class SkwasmImageFilter implements SceneImageFilter {
|
||||
});
|
||||
}
|
||||
|
||||
class SkwasmColorFilter {
|
||||
SkwasmColorFilter._(this.handle);
|
||||
class SkwasmColorFilter extends SkwasmObjectWrapper<RawColorFilter> {
|
||||
SkwasmColorFilter._(ColorFilterHandle handle) : super(handle, _registry);
|
||||
|
||||
factory SkwasmColorFilter.fromEngineColorFilter(EngineColorFilter colorFilter) =>
|
||||
switch (colorFilter.type) {
|
||||
@@ -100,19 +93,12 @@ class SkwasmColorFilter {
|
||||
SkwasmColorFilter inner,
|
||||
) => SkwasmColorFilter._(colorFilterCompose(outer.handle, inner.handle));
|
||||
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
colorFilterDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
final ColorFilterHandle handle;
|
||||
bool _isDisposed = false;
|
||||
static final SkwasmFinalizationRegistry<RawColorFilter> _registry =
|
||||
SkwasmFinalizationRegistry<RawColorFilter>(colorFilterDispose);
|
||||
}
|
||||
|
||||
class SkwasmMaskFilter {
|
||||
SkwasmMaskFilter._(this.handle);
|
||||
class SkwasmMaskFilter extends SkwasmObjectWrapper<RawMaskFilter> {
|
||||
SkwasmMaskFilter._(MaskFilterHandle handle) : super(handle, _registry);
|
||||
|
||||
factory SkwasmMaskFilter.fromUiMaskFilter(ui.MaskFilter maskFilter) =>
|
||||
SkwasmMaskFilter._(maskFilterCreateBlur(
|
||||
@@ -120,13 +106,6 @@ class SkwasmMaskFilter {
|
||||
maskFilter.webOnlySigma
|
||||
));
|
||||
|
||||
final MaskFilterHandle handle;
|
||||
bool _isDisposed = false;
|
||||
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
maskFilterDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
static final SkwasmFinalizationRegistry<RawMaskFilter> _registry =
|
||||
SkwasmFinalizationRegistry<RawMaskFilter>(maskFilterDispose);
|
||||
}
|
||||
|
||||
@@ -19,19 +19,11 @@ import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
const String _robotoUrl =
|
||||
'https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Me5WZLCzYlKw.ttf';
|
||||
|
||||
class SkwasmTypeface {
|
||||
SkwasmTypeface(SkDataHandle data) : handle = typefaceCreate(data);
|
||||
class SkwasmTypeface extends SkwasmObjectWrapper<RawTypeface> {
|
||||
SkwasmTypeface(SkDataHandle data) : super(typefaceCreate(data), _registry);
|
||||
|
||||
bool _isDisposed = false;
|
||||
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
_isDisposed = true;
|
||||
typefaceDispose(handle);
|
||||
}
|
||||
}
|
||||
|
||||
TypefaceHandle handle;
|
||||
static final SkwasmFinalizationRegistry<RawTypeface> _registry =
|
||||
SkwasmFinalizationRegistry<RawTypeface>(typefaceDispose);
|
||||
}
|
||||
|
||||
class SkwasmFontCollection implements FlutterFontCollection {
|
||||
@@ -39,8 +31,13 @@ class SkwasmFontCollection implements FlutterFontCollection {
|
||||
setDefaultFontFamilies(<String>['Roboto']);
|
||||
}
|
||||
|
||||
// Most of the time, when an object deals with native handles to skwasm objects,
|
||||
// we register it with a finalization registry so that it can clean up the handle
|
||||
// when the dart side of object gets GC'd. However, this object is basically a
|
||||
// singleton (the renderer creates one and just hangs onto it forever) so it's
|
||||
// not really worth it to do the finalization dance here.
|
||||
FontCollectionHandle handle = fontCollectionCreate();
|
||||
TextStyleHandle defaultTextStyle = textStyleCreate();
|
||||
SkwasmNativeTextStyle defaultTextStyle = SkwasmNativeTextStyle.defaultTextStyle();
|
||||
final Map<String, List<SkwasmTypeface>> registeredTypefaces = <String, List<SkwasmTypeface>>{};
|
||||
|
||||
void setDefaultFontFamilies(List<String> families) => withStackScope((StackScope scope) {
|
||||
@@ -49,8 +46,8 @@ class SkwasmFontCollection implements FlutterFontCollection {
|
||||
for (int i = 0; i < families.length; i++) {
|
||||
familyPointers[i] = skStringFromDartString(families[i]);
|
||||
}
|
||||
textStyleClearFontFamilies(defaultTextStyle);
|
||||
textStyleAddFontFamilies(defaultTextStyle, familyPointers, families.length);
|
||||
textStyleClearFontFamilies(defaultTextStyle.handle);
|
||||
textStyleAddFontFamilies(defaultTextStyle.handle, familyPointers, families.length);
|
||||
for (int i = 0; i < families.length; i++) {
|
||||
skStringFree(familyPointers[i]);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,11 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
class SkwasmImage implements ui.Image {
|
||||
SkwasmImage(this.handle);
|
||||
class SkwasmImage extends SkwasmObjectWrapper<RawImage> implements ui.Image {
|
||||
SkwasmImage(ImageHandle handle) : super(handle, _registry)
|
||||
{
|
||||
ui.Image.onCreate?.call(this);
|
||||
}
|
||||
|
||||
factory SkwasmImage.fromPixels(
|
||||
Uint8List pixels,
|
||||
@@ -35,8 +38,14 @@ class SkwasmImage implements ui.Image {
|
||||
return SkwasmImage(imageHandle);
|
||||
}
|
||||
|
||||
final ImageHandle handle;
|
||||
bool _isDisposed = false;
|
||||
static final SkwasmFinalizationRegistry<RawImage> _registry =
|
||||
SkwasmFinalizationRegistry<RawImage>(imageDispose);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
ui.Image.onDispose?.call(this);
|
||||
}
|
||||
|
||||
@override
|
||||
int get width => imageGetWidth(handle);
|
||||
@@ -53,17 +62,6 @@ class SkwasmImage implements ui.Image {
|
||||
@override
|
||||
ui.ColorSpace get colorSpace => ui.ColorSpace.sRGB;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
imageDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed => _isDisposed;
|
||||
|
||||
@override
|
||||
SkwasmImage clone() {
|
||||
imageRef(handle);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// 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.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:js_interop';
|
||||
|
||||
import 'package:ui/src/engine.dart';
|
||||
|
||||
class SkwasmObjectWrapper<T extends NativeType> {
|
||||
SkwasmObjectWrapper(this.handle, this.registry) {
|
||||
registry.register(this);
|
||||
}
|
||||
final SkwasmFinalizationRegistry<T> registry;
|
||||
final Pointer<T> handle;
|
||||
bool _isDisposed = false;
|
||||
|
||||
void dispose() {
|
||||
assert(!_isDisposed);
|
||||
registry.evict(this);
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
bool get debugDisposed => _isDisposed;
|
||||
}
|
||||
|
||||
typedef DisposeFunction<T extends NativeType> = void Function(Pointer<T>);
|
||||
|
||||
class SkwasmFinalizationRegistry<T extends NativeType> {
|
||||
SkwasmFinalizationRegistry(this.dispose)
|
||||
: registry = DomFinalizationRegistry(((JSNumber address) =>
|
||||
dispose(Pointer<T>.fromAddress(address.toDart.toInt()))
|
||||
).toJS);
|
||||
|
||||
final DomFinalizationRegistry registry;
|
||||
final DisposeFunction<T> dispose;
|
||||
|
||||
void register(SkwasmObjectWrapper<T> wrapper) {
|
||||
registry.register(wrapper, wrapper.handle.address, wrapper);
|
||||
}
|
||||
|
||||
void evict(SkwasmObjectWrapper<T> wrapper) {
|
||||
registry.unregister(wrapper);
|
||||
dispose(wrapper.handle);
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,11 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
class SkwasmPaint implements ui.Paint {
|
||||
factory SkwasmPaint() {
|
||||
return SkwasmPaint._fromHandle(paintCreate());
|
||||
}
|
||||
class SkwasmPaint extends SkwasmObjectWrapper<RawPaint> implements ui.Paint {
|
||||
SkwasmPaint() : super(paintCreate(), _registry);
|
||||
|
||||
SkwasmPaint._fromHandle(this._handle);
|
||||
PaintHandle _handle;
|
||||
|
||||
PaintHandle get handle => _handle;
|
||||
static final SkwasmFinalizationRegistry<RawPaint> _registry =
|
||||
SkwasmFinalizationRegistry<RawPaint>(paintDispose);
|
||||
|
||||
ui.BlendMode _cachedBlendMode = ui.BlendMode.srcOver;
|
||||
|
||||
@@ -47,51 +43,51 @@ class SkwasmPaint implements ui.Paint {
|
||||
set blendMode(ui.BlendMode blendMode) {
|
||||
if (_cachedBlendMode != blendMode) {
|
||||
_cachedBlendMode = blendMode;
|
||||
paintSetBlendMode(_handle, blendMode.index);
|
||||
paintSetBlendMode(handle, blendMode.index);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ui.PaintingStyle get style => ui.PaintingStyle.values[paintGetStyle(_handle)];
|
||||
ui.PaintingStyle get style => ui.PaintingStyle.values[paintGetStyle(handle)];
|
||||
|
||||
@override
|
||||
set style(ui.PaintingStyle style) => paintSetStyle(_handle, style.index);
|
||||
set style(ui.PaintingStyle style) => paintSetStyle(handle, style.index);
|
||||
|
||||
@override
|
||||
double get strokeWidth => paintGetStrokeWidth(_handle);
|
||||
double get strokeWidth => paintGetStrokeWidth(handle);
|
||||
|
||||
@override
|
||||
set strokeWidth(double width) => paintSetStrokeWidth(_handle, width);
|
||||
set strokeWidth(double width) => paintSetStrokeWidth(handle, width);
|
||||
|
||||
@override
|
||||
ui.StrokeCap get strokeCap => ui.StrokeCap.values[paintGetStrokeCap(_handle)];
|
||||
ui.StrokeCap get strokeCap => ui.StrokeCap.values[paintGetStrokeCap(handle)];
|
||||
|
||||
@override
|
||||
set strokeCap(ui.StrokeCap cap) => paintSetStrokeCap(_handle, cap.index);
|
||||
set strokeCap(ui.StrokeCap cap) => paintSetStrokeCap(handle, cap.index);
|
||||
|
||||
@override
|
||||
ui.StrokeJoin get strokeJoin => ui.StrokeJoin.values[paintGetStrokeJoin(_handle)];
|
||||
ui.StrokeJoin get strokeJoin => ui.StrokeJoin.values[paintGetStrokeJoin(handle)];
|
||||
|
||||
@override
|
||||
set strokeJoin(ui.StrokeJoin join) => paintSetStrokeJoin(_handle, join.index);
|
||||
set strokeJoin(ui.StrokeJoin join) => paintSetStrokeJoin(handle, join.index);
|
||||
|
||||
@override
|
||||
bool get isAntiAlias => paintGetAntiAlias(_handle);
|
||||
bool get isAntiAlias => paintGetAntiAlias(handle);
|
||||
|
||||
@override
|
||||
set isAntiAlias(bool value) => paintSetAntiAlias(_handle, value);
|
||||
set isAntiAlias(bool value) => paintSetAntiAlias(handle, value);
|
||||
|
||||
@override
|
||||
ui.Color get color => ui.Color(paintGetColorInt(_handle));
|
||||
ui.Color get color => ui.Color(paintGetColorInt(handle));
|
||||
|
||||
@override
|
||||
set color(ui.Color color) => paintSetColorInt(_handle, color.value);
|
||||
set color(ui.Color color) => paintSetColorInt(handle, color.value);
|
||||
|
||||
@override
|
||||
double get strokeMiterLimit => paintGetMiterLimit(_handle);
|
||||
double get strokeMiterLimit => paintGetMiterLimit(handle);
|
||||
|
||||
@override
|
||||
set strokeMiterLimit(double limit) => paintSetMiterLimit(_handle, limit);
|
||||
set strokeMiterLimit(double limit) => paintSetMiterLimit(handle, limit);
|
||||
|
||||
@override
|
||||
ui.Shader? get shader => _shader;
|
||||
@@ -102,7 +98,7 @@ class SkwasmPaint implements ui.Paint {
|
||||
_shader = skwasmShader;
|
||||
final ShaderHandle shaderHandle =
|
||||
skwasmShader != null ? skwasmShader.handle : nullptr;
|
||||
paintSetShader(_handle, shaderHandle);
|
||||
paintSetShader(handle, shaderHandle);
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -13,7 +13,7 @@ import 'package:ui/ui.dart' as ui;
|
||||
const int _kSoftLineBreak = 0;
|
||||
const int _kHardLineBreak = 100;
|
||||
|
||||
class SkwasmLineMetrics implements ui.LineMetrics {
|
||||
class SkwasmLineMetrics extends SkwasmObjectWrapper<RawLineMetrics> implements ui.LineMetrics {
|
||||
factory SkwasmLineMetrics({
|
||||
required bool hardBreak,
|
||||
required double ascent,
|
||||
@@ -36,10 +36,10 @@ class SkwasmLineMetrics implements ui.LineMetrics {
|
||||
lineNumber,
|
||||
));
|
||||
|
||||
SkwasmLineMetrics._(this.handle);
|
||||
SkwasmLineMetrics._(LineMetricsHandle handle) : super(handle, _registry);
|
||||
|
||||
final LineMetricsHandle handle;
|
||||
bool _isDisposed = false;
|
||||
static final SkwasmFinalizationRegistry<RawLineMetrics> _registry =
|
||||
SkwasmFinalizationRegistry<RawLineMetrics>(lineMetricsDispose);
|
||||
|
||||
@override
|
||||
bool get hardBreak => lineMetricsGetHardBreak(handle);
|
||||
@@ -67,20 +67,14 @@ class SkwasmLineMetrics implements ui.LineMetrics {
|
||||
|
||||
@override
|
||||
int get lineNumber => lineMetricsGetLineNumber(handle);
|
||||
|
||||
void dispose() {
|
||||
if (_isDisposed) {
|
||||
lineMetricsDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmParagraph implements ui.Paragraph {
|
||||
SkwasmParagraph(this.handle);
|
||||
class SkwasmParagraph extends SkwasmObjectWrapper<RawParagraph> implements ui.Paragraph {
|
||||
SkwasmParagraph(ParagraphHandle handle) : super(handle, _registry);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawParagraph> _registry =
|
||||
SkwasmFinalizationRegistry<RawParagraph>(paragraphDispose);
|
||||
|
||||
final ParagraphHandle handle;
|
||||
bool _isDisposed = false;
|
||||
bool _hasCheckedForMissingCodePoints = false;
|
||||
|
||||
@override
|
||||
@@ -220,17 +214,6 @@ class SkwasmParagraph implements ui.Paragraph {
|
||||
(int index) => SkwasmLineMetrics._(paragraphGetLineMetricsAtIndex(handle, index))
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed => _isDisposed;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
paragraphDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void withScopedFontList(
|
||||
@@ -251,6 +234,19 @@ void withScopedFontList(
|
||||
});
|
||||
}
|
||||
|
||||
class SkwasmNativeTextStyle extends SkwasmObjectWrapper<RawTextStyle> {
|
||||
SkwasmNativeTextStyle(TextStyleHandle handle) : super(handle, _registry);
|
||||
|
||||
factory SkwasmNativeTextStyle.defaultTextStyle() => SkwasmNativeTextStyle(textStyleCreate());
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawTextStyle> _registry =
|
||||
SkwasmFinalizationRegistry<RawTextStyle>(textStyleDispose);
|
||||
|
||||
SkwasmNativeTextStyle copy() {
|
||||
return SkwasmNativeTextStyle(textStyleCopy(handle));
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmTextStyle implements ui.TextStyle {
|
||||
SkwasmTextStyle({
|
||||
this.color,
|
||||
@@ -276,7 +272,8 @@ class SkwasmTextStyle implements ui.TextStyle {
|
||||
this.fontVariations,
|
||||
});
|
||||
|
||||
void applyToHandle(TextStyleHandle handle) {
|
||||
void applyToNative(SkwasmNativeTextStyle style) {
|
||||
final TextStyleHandle handle = style.handle;
|
||||
if (color != null) {
|
||||
textStyleSetColor(handle, color!.value);
|
||||
}
|
||||
@@ -409,7 +406,7 @@ class SkwasmTextStyle implements ui.TextStyle {
|
||||
final List<ui.FontVariation>? fontVariations;
|
||||
}
|
||||
|
||||
class SkwasmStrutStyle implements ui.StrutStyle {
|
||||
class SkwasmStrutStyle extends SkwasmObjectWrapper<RawStrutStyle> implements ui.StrutStyle {
|
||||
factory SkwasmStrutStyle({
|
||||
String? fontFamily,
|
||||
List<String>? fontFamilyFallback,
|
||||
@@ -458,12 +455,13 @@ class SkwasmStrutStyle implements ui.StrutStyle {
|
||||
return SkwasmStrutStyle._(handle);
|
||||
}
|
||||
|
||||
SkwasmStrutStyle._(this.handle);
|
||||
SkwasmStrutStyle._(StrutStyleHandle handle) : super(handle, _registry);
|
||||
|
||||
final StrutStyleHandle handle;
|
||||
static final SkwasmFinalizationRegistry<RawStrutStyle> _registry =
|
||||
SkwasmFinalizationRegistry<RawStrutStyle>(strutStyleDispose);
|
||||
}
|
||||
|
||||
class SkwasmParagraphStyle implements ui.ParagraphStyle {
|
||||
class SkwasmParagraphStyle extends SkwasmObjectWrapper<RawParagraphStyle> implements ui.ParagraphStyle {
|
||||
factory SkwasmParagraphStyle({
|
||||
ui.TextAlign? textAlign,
|
||||
ui.TextDirection? textDirection,
|
||||
@@ -507,9 +505,10 @@ class SkwasmParagraphStyle implements ui.ParagraphStyle {
|
||||
strutStyle as SkwasmStrutStyle;
|
||||
paragraphStyleSetStrutStyle(handle, strutStyle.handle);
|
||||
}
|
||||
final TextStyleHandle textStyleHandle = textStyleCopy(
|
||||
(renderer.fontCollection as SkwasmFontCollection).defaultTextStyle,
|
||||
);
|
||||
final SkwasmNativeTextStyle textStyle =
|
||||
(renderer.fontCollection as SkwasmFontCollection).defaultTextStyle.copy();
|
||||
final TextStyleHandle textStyleHandle = textStyle.handle;
|
||||
|
||||
if (fontFamily != null) {
|
||||
withScopedFontList(<String>[fontFamily],
|
||||
(Pointer<SkStringHandle> families, int count) =>
|
||||
@@ -536,24 +535,23 @@ class SkwasmParagraphStyle implements ui.ParagraphStyle {
|
||||
skStringFree(localeHandle);
|
||||
}
|
||||
paragraphStyleSetTextStyle(handle, textStyleHandle);
|
||||
return SkwasmParagraphStyle._(handle, textStyleHandle, fontFamily);
|
||||
return SkwasmParagraphStyle._(handle, textStyle, fontFamily);
|
||||
}
|
||||
|
||||
SkwasmParagraphStyle._(this.handle, this.textStyleHandle, this.defaultFontFamily);
|
||||
SkwasmParagraphStyle._(
|
||||
ParagraphStyleHandle handle,
|
||||
this.textStyle,
|
||||
this.defaultFontFamily
|
||||
) : super(handle, _registry);
|
||||
|
||||
final ParagraphStyleHandle handle;
|
||||
final TextStyleHandle textStyleHandle;
|
||||
static final SkwasmFinalizationRegistry<RawParagraphStyle> _registry =
|
||||
SkwasmFinalizationRegistry<RawParagraphStyle>(paragraphStyleDispose);
|
||||
|
||||
final SkwasmNativeTextStyle textStyle;
|
||||
final String? defaultFontFamily;
|
||||
}
|
||||
|
||||
class _TextStyleStackEntry {
|
||||
_TextStyleStackEntry(this.style, this.handle);
|
||||
|
||||
SkwasmTextStyle style;
|
||||
TextStyleHandle handle;
|
||||
}
|
||||
|
||||
class SkwasmParagraphBuilder implements ui.ParagraphBuilder {
|
||||
class SkwasmParagraphBuilder extends SkwasmObjectWrapper<RawParagraphBuilder> implements ui.ParagraphBuilder {
|
||||
factory SkwasmParagraphBuilder(
|
||||
SkwasmParagraphStyle style,
|
||||
SkwasmFontCollection collection,
|
||||
@@ -562,10 +560,13 @@ class SkwasmParagraphBuilder implements ui.ParagraphBuilder {
|
||||
collection.handle,
|
||||
), style);
|
||||
|
||||
SkwasmParagraphBuilder._(this.handle, this.style);
|
||||
final ParagraphBuilderHandle handle;
|
||||
SkwasmParagraphBuilder._(ParagraphBuilderHandle handle, this.style) : super(handle, _registry);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawParagraphBuilder> _registry =
|
||||
SkwasmFinalizationRegistry<RawParagraphBuilder>(paragraphBuilderDispose);
|
||||
|
||||
final SkwasmParagraphStyle style;
|
||||
final List<_TextStyleStackEntry> textStyleStack = <_TextStyleStackEntry>[];
|
||||
final List<SkwasmNativeTextStyle> textStyleStack = <SkwasmNativeTextStyle>[];
|
||||
|
||||
@override
|
||||
List<double> placeholderScales = <double>[];
|
||||
@@ -695,28 +696,20 @@ class SkwasmParagraphBuilder implements ui.ParagraphBuilder {
|
||||
|
||||
@override
|
||||
void pop() {
|
||||
final TextStyleHandle textStyleHandle = textStyleStack.removeLast().handle;
|
||||
textStyleDispose(textStyleHandle);
|
||||
final SkwasmNativeTextStyle style = textStyleStack.removeLast();
|
||||
style.dispose();
|
||||
paragraphBuilderPop(handle);
|
||||
}
|
||||
|
||||
@override
|
||||
void pushStyle(ui.TextStyle textStyle) {
|
||||
textStyle as SkwasmTextStyle;
|
||||
TextStyleHandle sourceStyleHandle = nullptr;
|
||||
if (textStyleStack.isNotEmpty) {
|
||||
sourceStyleHandle = textStyleStack.last.handle;
|
||||
}
|
||||
if (sourceStyleHandle == nullptr) {
|
||||
sourceStyleHandle = style.textStyleHandle;
|
||||
}
|
||||
if (sourceStyleHandle == nullptr) {
|
||||
sourceStyleHandle =
|
||||
(renderer.fontCollection as SkwasmFontCollection).defaultTextStyle;
|
||||
}
|
||||
final TextStyleHandle styleHandle = textStyleCopy(sourceStyleHandle);
|
||||
textStyle.applyToHandle(styleHandle);
|
||||
textStyleStack.add(_TextStyleStackEntry(textStyle, styleHandle));
|
||||
paragraphBuilderPushStyle(handle, styleHandle);
|
||||
final SkwasmNativeTextStyle baseStyle = textStyleStack.isNotEmpty
|
||||
? textStyleStack.last
|
||||
: style.textStyle;
|
||||
final SkwasmNativeTextStyle nativeStyle = baseStyle.copy();
|
||||
textStyle.applyToNative(nativeStyle);
|
||||
textStyleStack.add(nativeStyle);
|
||||
paragraphBuilderPushStyle(handle, nativeStyle.handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,49 +19,45 @@ enum PathArcSize {
|
||||
large,
|
||||
}
|
||||
|
||||
class SkwasmPath implements ui.Path {
|
||||
class SkwasmPath extends SkwasmObjectWrapper<RawPath> implements ui.Path {
|
||||
factory SkwasmPath() {
|
||||
return SkwasmPath.fromHandle(pathCreate());
|
||||
}
|
||||
|
||||
factory SkwasmPath.from(SkwasmPath source) {
|
||||
return SkwasmPath.fromHandle(pathCopy(source._handle));
|
||||
return SkwasmPath.fromHandle(pathCopy(source.handle));
|
||||
}
|
||||
|
||||
SkwasmPath.fromHandle(this._handle);
|
||||
final PathHandle _handle;
|
||||
SkwasmPath.fromHandle(PathHandle handle) : super(handle, _registry);
|
||||
|
||||
PathHandle get handle => _handle;
|
||||
|
||||
void delete() {
|
||||
pathDestroy(_handle);
|
||||
}
|
||||
static final SkwasmFinalizationRegistry<RawPath> _registry =
|
||||
SkwasmFinalizationRegistry<RawPath>(pathDispose);
|
||||
|
||||
@override
|
||||
ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(_handle)];
|
||||
ui.PathFillType get fillType => ui.PathFillType.values[pathGetFillType(handle)];
|
||||
|
||||
@override
|
||||
set fillType(ui.PathFillType fillType) => pathSetFillType(_handle, fillType.index);
|
||||
set fillType(ui.PathFillType fillType) => pathSetFillType(handle, fillType.index);
|
||||
|
||||
@override
|
||||
void moveTo(double x, double y) => pathMoveTo(_handle, x, y);
|
||||
void moveTo(double x, double y) => pathMoveTo(handle, x, y);
|
||||
|
||||
@override
|
||||
void relativeMoveTo(double x, double y) => pathRelativeMoveTo(_handle, x, y);
|
||||
void relativeMoveTo(double x, double y) => pathRelativeMoveTo(handle, x, y);
|
||||
|
||||
@override
|
||||
void lineTo(double x, double y) => pathLineTo(_handle, x, y);
|
||||
void lineTo(double x, double y) => pathLineTo(handle, x, y);
|
||||
|
||||
@override
|
||||
void relativeLineTo(double x, double y) => pathRelativeMoveTo(_handle, x, y);
|
||||
void relativeLineTo(double x, double y) => pathRelativeMoveTo(handle, x, y);
|
||||
|
||||
@override
|
||||
void quadraticBezierTo(double x1, double y1, double x2, double y2) =>
|
||||
pathQuadraticBezierTo(_handle, x1, y1, x2, y2);
|
||||
pathQuadraticBezierTo(handle, x1, y1, x2, y2);
|
||||
|
||||
@override
|
||||
void relativeQuadraticBezierTo(double x1, double y1, double x2, double y2) =>
|
||||
pathRelativeQuadraticBezierTo(_handle, x1, y1, x2, y2);
|
||||
pathRelativeQuadraticBezierTo(handle, x1, y1, x2, y2);
|
||||
|
||||
@override
|
||||
void cubicTo(
|
||||
@@ -71,7 +67,7 @@ class SkwasmPath implements ui.Path {
|
||||
double y2,
|
||||
double x3,
|
||||
double y3) =>
|
||||
pathCubicTo(_handle, x1, y1, x2, y2, x3, y3);
|
||||
pathCubicTo(handle, x1, y1, x2, y2, x3, y3);
|
||||
|
||||
@override
|
||||
void relativeCubicTo(
|
||||
@@ -81,22 +77,22 @@ class SkwasmPath implements ui.Path {
|
||||
double y2,
|
||||
double x3,
|
||||
double y3) =>
|
||||
pathRelativeCubicTo(_handle, x1, y1, x2, y2, x3, y3);
|
||||
pathRelativeCubicTo(handle, x1, y1, x2, y2, x3, y3);
|
||||
|
||||
@override
|
||||
void conicTo(double x1, double y1, double x2, double y2, double w) =>
|
||||
pathConicTo(_handle, x1, y1, x2, y2, w);
|
||||
pathConicTo(handle, x1, y1, x2, y2, w);
|
||||
|
||||
@override
|
||||
void relativeConicTo(double x1, double y1, double x2, double y2, double w) =>
|
||||
pathRelativeConicTo(_handle, x1, y1, x2, y2, w);
|
||||
pathRelativeConicTo(handle, x1, y1, x2, y2, w);
|
||||
|
||||
@override
|
||||
void arcTo(
|
||||
ui.Rect rect, double startAngle, double sweepAngle, bool forceMoveTo) {
|
||||
withStackScope((StackScope s) {
|
||||
pathArcToOval(
|
||||
_handle,
|
||||
handle,
|
||||
s.convertRectToNative(rect),
|
||||
ui.toDegrees(startAngle),
|
||||
ui.toDegrees(sweepAngle),
|
||||
@@ -118,7 +114,7 @@ class SkwasmPath implements ui.Path {
|
||||
final PathDirection pathDirection =
|
||||
clockwise ? PathDirection.clockwise : PathDirection.counterClockwise;
|
||||
pathArcToRotated(
|
||||
_handle,
|
||||
handle,
|
||||
radius.x,
|
||||
radius.y,
|
||||
ui.toDegrees(rotation),
|
||||
@@ -142,7 +138,7 @@ class SkwasmPath implements ui.Path {
|
||||
final PathDirection pathDirection =
|
||||
clockwise ? PathDirection.clockwise : PathDirection.counterClockwise;
|
||||
pathRelativeArcToRotated(
|
||||
_handle,
|
||||
handle,
|
||||
radius.x,
|
||||
radius.y,
|
||||
ui.toDegrees(rotation),
|
||||
@@ -156,14 +152,14 @@ class SkwasmPath implements ui.Path {
|
||||
@override
|
||||
void addRect(ui.Rect rect) {
|
||||
withStackScope((StackScope s) {
|
||||
pathAddRect(_handle, s.convertRectToNative(rect));
|
||||
pathAddRect(handle, s.convertRectToNative(rect));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void addOval(ui.Rect rect) {
|
||||
withStackScope((StackScope s) {
|
||||
pathAddOval(_handle, s.convertRectToNative(rect));
|
||||
pathAddOval(handle, s.convertRectToNative(rect));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -171,7 +167,7 @@ class SkwasmPath implements ui.Path {
|
||||
void addArc(ui.Rect rect, double startAngle, double sweepAngle) {
|
||||
withStackScope((StackScope s) {
|
||||
pathAddArc(
|
||||
_handle,
|
||||
handle,
|
||||
s.convertRectToNative(rect),
|
||||
ui.toDegrees(startAngle),
|
||||
ui.toDegrees(sweepAngle)
|
||||
@@ -182,14 +178,14 @@ class SkwasmPath implements ui.Path {
|
||||
@override
|
||||
void addPolygon(List<ui.Offset> points, bool close) {
|
||||
withStackScope((StackScope s) {
|
||||
pathAddPolygon(_handle, s.convertPointArrayToNative(points), points.length, close);
|
||||
pathAddPolygon(handle, s.convertPointArrayToNative(points), points.length, close);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void addRRect(ui.RRect rrect) {
|
||||
withStackScope((StackScope s) {
|
||||
pathAddRRect(_handle, s.convertRRectToNative(rrect));
|
||||
pathAddRRect(handle, s.convertRRectToNative(rrect));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -210,18 +206,18 @@ class SkwasmPath implements ui.Path {
|
||||
s.convertMatrix4toSkMatrix(matrix4 ?? Matrix4.identity().toFloat64());
|
||||
convertedMatrix[2] += offset.dx;
|
||||
convertedMatrix[5] += offset.dy;
|
||||
pathAddPath(_handle, (path as SkwasmPath)._handle, convertedMatrix, extend);
|
||||
pathAddPath(handle, (path as SkwasmPath).handle, convertedMatrix, extend);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void close() => pathClose(_handle);
|
||||
void close() => pathClose(handle);
|
||||
|
||||
@override
|
||||
void reset() => pathReset(_handle);
|
||||
void reset() => pathReset(handle);
|
||||
|
||||
@override
|
||||
bool contains(ui.Offset point) => pathContains(_handle, point.dx, point.dy);
|
||||
bool contains(ui.Offset point) => pathContains(handle, point.dx, point.dy);
|
||||
|
||||
@override
|
||||
ui.Path shift(ui.Offset offset) =>
|
||||
@@ -230,7 +226,7 @@ class SkwasmPath implements ui.Path {
|
||||
@override
|
||||
ui.Path transform(Float64List matrix4) {
|
||||
return withStackScope((StackScope s) {
|
||||
final PathHandle newPathHandle = pathCopy(_handle);
|
||||
final PathHandle newPathHandle = pathCopy(handle);
|
||||
pathTransform(newPathHandle, s.convertMatrix4toSkMatrix(matrix4));
|
||||
return SkwasmPath.fromHandle(newPathHandle);
|
||||
});
|
||||
@@ -240,7 +236,7 @@ class SkwasmPath implements ui.Path {
|
||||
ui.Rect getBounds() {
|
||||
return withStackScope((StackScope s) {
|
||||
final Pointer<Float> rectBuffer = s.allocFloatArray(4);
|
||||
pathGetBounds(_handle, rectBuffer);
|
||||
pathGetBounds(handle, rectBuffer);
|
||||
return s.convertRectFromNative(rectBuffer);
|
||||
});
|
||||
}
|
||||
@@ -250,7 +246,7 @@ class SkwasmPath implements ui.Path {
|
||||
SkwasmPath path1,
|
||||
SkwasmPath path2) =>
|
||||
SkwasmPath.fromHandle(pathCombine(
|
||||
operation.index, path1._handle, path2._handle));
|
||||
operation.index, path1.handle, path2.handle));
|
||||
|
||||
@override
|
||||
ui.PathMetrics computeMetrics({bool forceClosed = false}) {
|
||||
|
||||
@@ -19,11 +19,13 @@ class SkwasmPathMetrics extends IterableBase<ui.PathMetric>
|
||||
late Iterator<ui.PathMetric> iterator = SkwasmPathMetricIterator(path, forceClosed);
|
||||
}
|
||||
|
||||
class SkwasmPathMetricIterator implements Iterator<ui.PathMetric> {
|
||||
class SkwasmPathMetricIterator extends SkwasmObjectWrapper<RawContourMeasureIter> implements Iterator<ui.PathMetric> {
|
||||
SkwasmPathMetricIterator(SkwasmPath path, bool forceClosed)
|
||||
: _handle = contourMeasureIterCreate(path.handle, forceClosed, 1.0);
|
||||
: super(contourMeasureIterCreate(path.handle, forceClosed, 1.0), _registry);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawContourMeasureIter> _registry =
|
||||
SkwasmFinalizationRegistry<RawContourMeasureIter>(contourMeasureIterDispose);
|
||||
|
||||
final ContourMeasureIterHandle _handle;
|
||||
SkwasmPathMetric? _current;
|
||||
int _nextIndex = 0;
|
||||
|
||||
@@ -40,7 +42,7 @@ class SkwasmPathMetricIterator implements Iterator<ui.PathMetric> {
|
||||
|
||||
@override
|
||||
bool moveNext() {
|
||||
final ContourMeasureHandle measureHandle = contourMeasureIterNext(_handle);
|
||||
final ContourMeasureHandle measureHandle = contourMeasureIterNext(handle);
|
||||
if (measureHandle == nullptr) {
|
||||
_current = null;
|
||||
return false;
|
||||
@@ -52,10 +54,11 @@ class SkwasmPathMetricIterator implements Iterator<ui.PathMetric> {
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmPathMetric implements ui.PathMetric {
|
||||
SkwasmPathMetric(this._handle, this.contourIndex);
|
||||
class SkwasmPathMetric extends SkwasmObjectWrapper<RawContourMeasure> implements ui.PathMetric {
|
||||
SkwasmPathMetric(ContourMeasureHandle handle, this.contourIndex) : super(handle, _registry);
|
||||
|
||||
final ContourMeasureHandle _handle;
|
||||
static final SkwasmFinalizationRegistry<RawContourMeasure> _registry =
|
||||
SkwasmFinalizationRegistry<RawContourMeasure>(contourMeasureDispose);
|
||||
|
||||
@override
|
||||
final int contourIndex;
|
||||
@@ -63,7 +66,7 @@ class SkwasmPathMetric implements ui.PathMetric {
|
||||
@override
|
||||
ui.Path extractPath(double start, double end, {bool startWithMoveTo = true}) {
|
||||
return SkwasmPath.fromHandle(
|
||||
contourMeasureGetSegment(_handle, start, end, startWithMoveTo));
|
||||
contourMeasureGetSegment(handle, start, end, startWithMoveTo));
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -73,7 +76,7 @@ class SkwasmPathMetric implements ui.PathMetric {
|
||||
final Pointer<Float> outTangent =
|
||||
Pointer<Float>.fromAddress(outPosition.address + sizeOf<Float>() * 2);
|
||||
final bool result =
|
||||
contourMeasureGetPosTan(_handle, distance, outPosition, outTangent);
|
||||
contourMeasureGetPosTan(handle, distance, outPosition, outTangent);
|
||||
assert(result);
|
||||
return ui.Tangent(
|
||||
ui.Offset(outPosition[0], outPosition[1]),
|
||||
@@ -83,8 +86,8 @@ class SkwasmPathMetric implements ui.PathMetric {
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isClosed => contourMeasureIsClosed(_handle);
|
||||
bool get isClosed => contourMeasureIsClosed(handle);
|
||||
|
||||
@override
|
||||
double get length => contourMeasureLength(_handle);
|
||||
double get length => contourMeasureLength(handle);
|
||||
}
|
||||
|
||||
@@ -6,28 +6,24 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
class SkwasmPicture implements ScenePicture {
|
||||
SkwasmPicture.fromHandle(this._handle);
|
||||
final PictureHandle _handle;
|
||||
class SkwasmPicture extends SkwasmObjectWrapper<RawPicture> implements ScenePicture {
|
||||
SkwasmPicture.fromHandle(PictureHandle handle) : super(handle, _registry);
|
||||
|
||||
PictureHandle get handle => _handle;
|
||||
static final SkwasmFinalizationRegistry<RawPicture> _registry =
|
||||
SkwasmFinalizationRegistry<RawPicture>(pictureDispose);
|
||||
|
||||
@override
|
||||
Future<ui.Image> toImage(int width, int height) async => toImageSync(width, height);
|
||||
|
||||
@override
|
||||
int get approximateBytesUsed => pictureApproximateBytesUsed(handle);
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
ui.Picture.onDispose?.call(this);
|
||||
pictureDispose(_handle);
|
||||
debugDisposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
int get approximateBytesUsed => pictureApproximateBytesUsed(_handle);
|
||||
|
||||
@override
|
||||
bool debugDisposed = false;
|
||||
|
||||
@override
|
||||
ui.Image toImageSync(int width, int height) =>
|
||||
SkwasmImage(imageCreateFromPicture(handle, width, height));
|
||||
@@ -42,22 +38,19 @@ class SkwasmPicture implements ScenePicture {
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmPictureRecorder implements ui.PictureRecorder {
|
||||
factory SkwasmPictureRecorder() =>
|
||||
SkwasmPictureRecorder._fromHandle(pictureRecorderCreate());
|
||||
class SkwasmPictureRecorder extends SkwasmObjectWrapper<RawPictureRecorder> implements ui.PictureRecorder {
|
||||
SkwasmPictureRecorder() : super(pictureRecorderCreate(), _registry);
|
||||
|
||||
SkwasmPictureRecorder._fromHandle(this._handle);
|
||||
final PictureRecorderHandle _handle;
|
||||
|
||||
PictureRecorderHandle get handle => _handle;
|
||||
|
||||
void delete() => pictureRecorderDestroy(_handle);
|
||||
static final SkwasmFinalizationRegistry<RawPictureRecorder> _registry =
|
||||
SkwasmFinalizationRegistry<RawPictureRecorder>(pictureRecorderDispose);
|
||||
|
||||
@override
|
||||
SkwasmPicture endRecording() {
|
||||
isRecording = false;
|
||||
|
||||
final SkwasmPicture picture = SkwasmPicture.fromHandle(pictureRecorderEndRecording(_handle));
|
||||
final SkwasmPicture picture = SkwasmPicture.fromHandle(
|
||||
pictureRecorderEndRecording(handle)
|
||||
);
|
||||
ui.Picture.onCreate?.call(picture);
|
||||
return picture;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,6 @@ final class RawCanvas extends Opaque {}
|
||||
|
||||
typedef CanvasHandle = Pointer<RawCanvas>;
|
||||
|
||||
@Native<Void Function(CanvasHandle)>(symbol: 'canvas_destroy', isLeaf: true)
|
||||
external void canvasDestroy(CanvasHandle canvas);
|
||||
|
||||
@Native<Void Function(CanvasHandle)>(symbol: 'canvas_save', isLeaf: true)
|
||||
external void canvasSave(CanvasHandle canvas);
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ typedef PaintHandle = Pointer<RawPaint>;
|
||||
@Native<PaintHandle Function()>(symbol: 'paint_create', isLeaf: true)
|
||||
external PaintHandle paintCreate();
|
||||
|
||||
@Native<Void Function(PaintHandle)>(symbol: 'paint_destroy', isLeaf: true)
|
||||
external void paintDestroy(PaintHandle paint);
|
||||
@Native<Void Function(PaintHandle)>(symbol: 'paint_dispose', isLeaf: true)
|
||||
external void paintDispose(PaintHandle paint);
|
||||
|
||||
@Native<Void Function(PaintHandle, Int)>(symbol: 'paint_setBlendMode', isLeaf: true)
|
||||
external void paintSetBlendMode(PaintHandle paint, int blendMode);
|
||||
|
||||
@@ -16,8 +16,8 @@ typedef PathHandle = Pointer<RawPath>;
|
||||
@Native<PathHandle Function()>(symbol: 'path_create', isLeaf: true)
|
||||
external PathHandle pathCreate();
|
||||
|
||||
@Native<Void Function(PathHandle)>(symbol: 'path_destroy', isLeaf: true)
|
||||
external void pathDestroy(PathHandle path);
|
||||
@Native<Void Function(PathHandle)>(symbol: 'path_dispose', isLeaf: true)
|
||||
external void pathDispose(PathHandle path);
|
||||
|
||||
@Native<PathHandle Function(PathHandle)>(symbol: 'path_copy', isLeaf: true)
|
||||
external PathHandle pathCopy(PathHandle path);
|
||||
|
||||
@@ -30,7 +30,7 @@ external ContourMeasureHandle contourMeasureIterNext(
|
||||
symbol: 'contourMeasureIter_dispose')
|
||||
external void contourMeasureIterDispose(ContourMeasureIterHandle handle);
|
||||
|
||||
@Native<Void Function(ContourMeasureHandle)>(symbol: 'contourMesaure_dispose')
|
||||
@Native<Void Function(ContourMeasureHandle)>(symbol: 'contourMeasure_dispose')
|
||||
external void contourMeasureDispose(ContourMeasureHandle handle);
|
||||
|
||||
@Native<Float Function(ContourMeasureHandle)>(symbol: 'contourMeasure_length')
|
||||
|
||||
@@ -21,9 +21,9 @@ typedef PictureHandle = Pointer<RawPicture>;
|
||||
external PictureRecorderHandle pictureRecorderCreate();
|
||||
|
||||
@Native<Void Function(PictureRecorderHandle)>(
|
||||
symbol: 'pictureRecorder_destroy',
|
||||
symbol: 'pictureRecorder_dispose',
|
||||
isLeaf: true)
|
||||
external void pictureRecorderDestroy(PictureRecorderHandle picture);
|
||||
external void pictureRecorderDispose(PictureRecorderHandle picture);
|
||||
|
||||
@Native<CanvasHandle Function(PictureRecorderHandle, RawRect)>(
|
||||
symbol: 'pictureRecorder_beginRecording',
|
||||
|
||||
@@ -24,7 +24,6 @@ class SkwasmRenderer implements Renderer {
|
||||
return SkwasmPath.combine(op, path1 as SkwasmPath, path2 as SkwasmPath);
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
ui.Path copyPath(ui.Path src) {
|
||||
return SkwasmPath.from(src as SkwasmPath);
|
||||
|
||||
@@ -10,21 +10,21 @@ import 'package:ui/src/engine.dart';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
// A shared interface for shaders for which you can acquire a native handle
|
||||
abstract class SkwasmShader implements ui.Shader {
|
||||
ShaderHandle get handle;
|
||||
|
||||
@override
|
||||
bool get debugDisposed => handle == nullptr;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (handle != nullptr) {
|
||||
shaderDispose(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmGradient extends SkwasmShader implements ui.Gradient {
|
||||
// An implementation that handles the storage, disposal, and finalization of
|
||||
// a native shader handle.
|
||||
class SkwasmNativeShader extends SkwasmObjectWrapper<RawShader> implements SkwasmShader {
|
||||
SkwasmNativeShader(ShaderHandle handle) : super(handle, _registry);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawShader> _registry =
|
||||
SkwasmFinalizationRegistry<RawShader>(shaderDispose);
|
||||
}
|
||||
|
||||
class SkwasmGradient extends SkwasmNativeShader implements ui.Gradient {
|
||||
factory SkwasmGradient.linear({
|
||||
required ui.Offset from,
|
||||
required ui.Offset to,
|
||||
@@ -143,20 +143,11 @@ class SkwasmGradient extends SkwasmShader implements ui.Gradient {
|
||||
return SkwasmGradient._(handle);
|
||||
});
|
||||
|
||||
SkwasmGradient._(this.handle);
|
||||
|
||||
@override
|
||||
ShaderHandle handle;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
handle = nullptr;
|
||||
}
|
||||
SkwasmGradient._(super.handle);
|
||||
}
|
||||
|
||||
class SkwasmImageShader extends SkwasmShader implements ui.ImageShader {
|
||||
SkwasmImageShader._(this.handle);
|
||||
class SkwasmImageShader extends SkwasmNativeShader implements ui.ImageShader {
|
||||
SkwasmImageShader._(super.handle);
|
||||
|
||||
factory SkwasmImageShader.imageShader(
|
||||
SkwasmImage image,
|
||||
@@ -186,24 +177,16 @@ class SkwasmImageShader extends SkwasmShader implements ui.ImageShader {
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ShaderHandle handle;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
handle = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmFragmentProgram implements ui.FragmentProgram {
|
||||
class SkwasmFragmentProgram extends SkwasmObjectWrapper<RawRuntimeEffect> implements ui.FragmentProgram {
|
||||
SkwasmFragmentProgram._(
|
||||
this.name,
|
||||
this.handle,
|
||||
RuntimeEffectHandle handle,
|
||||
this.floatUniformCount,
|
||||
this.childShaderCount,
|
||||
);
|
||||
) : super(handle, _registry);
|
||||
|
||||
factory SkwasmFragmentProgram.fromBytes(String name, Uint8List bytes) {
|
||||
final ShaderData shaderData = ShaderData.fromBytes(bytes);
|
||||
|
||||
@@ -226,76 +209,102 @@ class SkwasmFragmentProgram implements ui.FragmentProgram {
|
||||
);
|
||||
}
|
||||
|
||||
final RuntimeEffectHandle handle;
|
||||
static final SkwasmFinalizationRegistry<RawRuntimeEffect> _registry =
|
||||
SkwasmFinalizationRegistry<RawRuntimeEffect>(runtimeEffectDispose);
|
||||
|
||||
final String name;
|
||||
final int floatUniformCount;
|
||||
final int childShaderCount;
|
||||
bool _isDisposed = false;
|
||||
|
||||
@override
|
||||
ui.FragmentShader fragmentShader() => SkwasmFragmentShader(this);
|
||||
|
||||
int get uniformSize => runtimeEffectGetUniformSize(handle);
|
||||
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
runtimeEffectDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader {
|
||||
SkwasmFragmentShader(
|
||||
SkwasmFragmentProgram program
|
||||
) : _program = program,
|
||||
_uniformData = skDataCreate(program.uniformSize),
|
||||
_floatUniformCount = program.floatUniformCount,
|
||||
_childShaders = List<SkwasmShader?>.filled(program.childShaderCount, null);
|
||||
class SkwasmShaderData extends SkwasmObjectWrapper<RawSkData> {
|
||||
SkwasmShaderData(int size) : super(skDataCreate(size), _registry);
|
||||
|
||||
static final SkwasmFinalizationRegistry<RawSkData> _registry =
|
||||
SkwasmFinalizationRegistry<RawSkData>(skDataDispose);
|
||||
}
|
||||
|
||||
// This class does not inherit from SkwasmNativeShader, as its handle might
|
||||
// change over time if the uniforms or image shaders are changed. Instead this
|
||||
// wraps a SkwasmNativeShader that it creates and destroys on demand. It does
|
||||
// implement SkwasmShader though, in order to provide the handle for the
|
||||
// underlying shader object.
|
||||
class SkwasmFragmentShader implements SkwasmShader, ui.FragmentShader {
|
||||
SkwasmFragmentShader(SkwasmFragmentProgram program) :
|
||||
_program = program,
|
||||
_uniformData = SkwasmShaderData(program.uniformSize),
|
||||
_floatUniformCount = program.floatUniformCount,
|
||||
_childShaders = List<SkwasmShader?>.filled(program.childShaderCount, null);
|
||||
|
||||
@override
|
||||
ShaderHandle get handle {
|
||||
if (_handle == nullptr) {
|
||||
_handle = withStackScope((StackScope s) {
|
||||
if (_nativeShader == null) {
|
||||
final ShaderHandle newHandle = withStackScope((StackScope s) {
|
||||
Pointer<ShaderHandle> childShaders = nullptr;
|
||||
if (_childShaders.isNotEmpty) {
|
||||
childShaders = s.allocPointerArray(_childShaders.length)
|
||||
.cast<ShaderHandle>();
|
||||
for (int i = 0; i < _childShaders.length; i++) {
|
||||
childShaders[i] = _childShaders[i]!.handle;
|
||||
final SkwasmShader? child = _childShaders[i];
|
||||
childShaders[i] = child != null ? child.handle : nullptr;
|
||||
}
|
||||
}
|
||||
return shaderCreateRuntimeEffectShader(
|
||||
_program.handle,
|
||||
_uniformData,
|
||||
_uniformData.handle,
|
||||
childShaders,
|
||||
_childShaders.length,
|
||||
);
|
||||
});
|
||||
_nativeShader = SkwasmNativeShader(newHandle);
|
||||
}
|
||||
return _handle;
|
||||
return _nativeShader!.handle;
|
||||
}
|
||||
|
||||
ShaderHandle _handle = nullptr;
|
||||
SkwasmShader? _nativeShader;
|
||||
final SkwasmFragmentProgram _program;
|
||||
SkDataHandle _uniformData;
|
||||
final SkwasmShaderData _uniformData;
|
||||
bool _isDisposed = false;
|
||||
final int _floatUniformCount;
|
||||
final List<SkwasmShader?> _childShaders;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
assert(!_isDisposed);
|
||||
_nativeShader?.dispose();
|
||||
_uniformData.dispose();
|
||||
_isDisposed = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get debugDisposed => _isDisposed;
|
||||
|
||||
@override
|
||||
void setFloat(int index, double value) {
|
||||
if (_handle != nullptr) {
|
||||
if (_nativeShader != null) {
|
||||
// Invalidate the previous shader so that it is recreated with the new
|
||||
// uniform data.
|
||||
shaderDispose(_handle);
|
||||
_handle = nullptr;
|
||||
_nativeShader!.dispose();
|
||||
_nativeShader = null;
|
||||
}
|
||||
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData).cast<Float>();
|
||||
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData.handle).cast<Float>();
|
||||
dataPointer[index] = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void setImageSampler(int index, ui.Image image) {
|
||||
if (_nativeShader != null) {
|
||||
// Invalidate the previous shader so that it is recreated with the new
|
||||
// child shaders.
|
||||
_nativeShader!.dispose();
|
||||
_nativeShader = null;
|
||||
}
|
||||
|
||||
final SkwasmImageShader shader = SkwasmImageShader.imageShader(
|
||||
image as SkwasmImage,
|
||||
ui.TileMode.clamp,
|
||||
@@ -307,17 +316,8 @@ class SkwasmFragmentShader extends SkwasmShader implements ui.FragmentShader {
|
||||
_childShaders[index] = shader;
|
||||
oldShader?.dispose();
|
||||
|
||||
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData).cast<Float>();
|
||||
final Pointer<Float> dataPointer = skDataGetPointer(_uniformData.handle).cast<Float>();
|
||||
dataPointer[_floatUniformCount + index * 2] = image.width.toDouble();
|
||||
dataPointer[_floatUniformCount + index * 2 + 1] = image.height.toDouble();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
if (_uniformData != nullptr) {
|
||||
skDataDispose(_uniformData);
|
||||
_uniformData = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import 'dart:typed_data';
|
||||
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
|
||||
import 'package:ui/ui.dart' as ui;
|
||||
|
||||
class SkwasmVertices implements ui.Vertices {
|
||||
class SkwasmVertices extends SkwasmObjectWrapper<RawVertices> implements ui.Vertices {
|
||||
factory SkwasmVertices(
|
||||
ui.VertexMode mode,
|
||||
List<ui.Offset> positions, {
|
||||
@@ -69,19 +69,8 @@ class SkwasmVertices implements ui.Vertices {
|
||||
));
|
||||
});
|
||||
|
||||
SkwasmVertices._(this.handle);
|
||||
SkwasmVertices._(VerticesHandle handle) : super(handle, _registry);
|
||||
|
||||
final VerticesHandle handle;
|
||||
bool _isDisposed = false;
|
||||
|
||||
@override
|
||||
bool get debugDisposed => _isDisposed;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (!_isDisposed) {
|
||||
verticesDispose(handle);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
static final SkwasmFinalizationRegistry<RawVertices> _registry =
|
||||
SkwasmFinalizationRegistry<RawVertices>(verticesDispose);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ SKWASM_EXPORT SkContourMeasure* contourMeasureIter_next(
|
||||
return next.get();
|
||||
}
|
||||
|
||||
SKWASM_EXPORT void contourMeasureIter_dispose(SkContourMeasureIter* iter) {
|
||||
delete iter;
|
||||
}
|
||||
|
||||
SKWASM_EXPORT void contourMeasure_dispose(SkContourMeasure* measure) {
|
||||
measure->unref();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ SKWASM_EXPORT SkPaint* paint_create() {
|
||||
return paint;
|
||||
}
|
||||
|
||||
SKWASM_EXPORT void paint_destroy(SkPaint* paint) {
|
||||
SKWASM_EXPORT void paint_dispose(SkPaint* paint) {
|
||||
delete paint;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ SKWASM_EXPORT SkPath* path_create() {
|
||||
return new SkPath();
|
||||
}
|
||||
|
||||
SKWASM_EXPORT void path_destroy(SkPath* path) {
|
||||
SKWASM_EXPORT void path_dispose(SkPath* path) {
|
||||
delete path;
|
||||
}
|
||||
|
||||
|
||||
@@ -1907,7 +1907,7 @@ void _paragraphTests() {
|
||||
// FinalizationRegistry because it depends on GC, which cannot be controlled,
|
||||
// So the test simply tests that a FinalizationRegistry can be constructed
|
||||
// and its `register` method can be called.
|
||||
final SkObjectFinalizationRegistry registry = createSkObjectFinalizationRegistry((String arg) {}.toJS);
|
||||
final DomFinalizationRegistry registry = DomFinalizationRegistry((String arg) {}.toJS);
|
||||
registry.register(Object(), Object());
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user