Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 56c3037

Browse files
committed
stack-scope all SkImageFilter instances
1 parent 780c6fc commit 56c3037

File tree

7 files changed

+94
-98
lines changed

7 files changed

+94
-98
lines changed

lib/web_ui/lib/src/engine/canvaskit/canvas.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ class CkCanvas {
333333
} else {
334334
convertible = filter as CkManagedSkImageFilterConvertible;
335335
}
336-
convertible.imageFilter((SkImageFilter filter) {
336+
convertible.withSkImageFilter((SkImageFilter filter) {
337337
final skPaint = paint?.toSkPaint();
338338
skCanvas.saveLayer(
339339
skPaint,

lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,9 @@ class SkImageFilter {}
15161516
extension SkImageFilterExtension on SkImageFilter {
15171517
external JSVoid delete();
15181518

1519+
@JS('isDeleted')
1520+
external JSBoolean _isDeleted();
1521+
bool isDeleted() => _isDeleted().toDart;
15191522

15201523
@JS('getOutputBounds')
15211524
external JSInt32Array _getOutputBounds(JSFloat32Array bounds);

lib/web_ui/lib/src/engine/canvaskit/color_filter.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ abstract class CkColorFilter implements CkManagedSkImageFilterConvertible {
7171
SkColorFilter _initRawColorFilter();
7272

7373
@override
74-
void imageFilter(SkImageFilterBorrow borrow) {
74+
void withSkImageFilter(SkImageFilterBorrow borrow) {
7575
// Since ColorFilter has a const constructor it cannot store dynamically
7676
// created Skia objects. Therefore a new SkImageFilter is created every time
7777
// it's used. However, once used it's no longer needed, so it's deleted

lib/web_ui/lib/src/engine/canvaskit/image_filter.dart

Lines changed: 61 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import 'package:ui/ui.dart' as ui;
1010
import '../util.dart';
1111
import 'canvaskit_api.dart';
1212
import 'color_filter.dart';
13-
import 'native_memory.dart';
1413

1514
typedef SkImageFilterBorrow = void Function(SkImageFilter);
1615

@@ -22,7 +21,12 @@ typedef SkImageFilterBorrow = void Function(SkImageFilter);
2221
///
2322
/// Currently implemented by [CkImageFilter] and [CkColorFilter].
2423
abstract class CkManagedSkImageFilterConvertible implements ui.ImageFilter {
25-
void imageFilter(SkImageFilterBorrow borrow);
24+
/// Creates a temporary [SkImageFilter], passes it to [borrow], and then
25+
/// immediately deletes it.
26+
///
27+
/// [SkImageFilter] objects are not kept around so that their memory is
28+
/// reclaimed immediately, rather than waiting for the GC cycle.
29+
void withSkImageFilter(SkImageFilterBorrow borrow);
2630

2731
Matrix4 get transform;
2832
}
@@ -56,22 +60,15 @@ abstract class CkImageFilter implements CkManagedSkImageFilterConvertible {
5660
}
5761

5862
class CkColorFilterImageFilter extends CkImageFilter {
59-
CkColorFilterImageFilter({required this.colorFilter}) : super._() {
60-
final SkImageFilter skImageFilter = colorFilter.initRawImageFilter();
61-
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.color');
62-
}
63+
CkColorFilterImageFilter({required this.colorFilter}) : super._();
6364

6465
final CkColorFilter colorFilter;
6566

66-
late final UniqueRef<SkImageFilter> _ref;
67-
6867
@override
69-
void imageFilter(SkImageFilterBorrow borrow) {
70-
borrow(_ref.nativeObject);
71-
}
72-
73-
void dispose() {
74-
_ref.dispose();
68+
void withSkImageFilter(SkImageFilterBorrow borrow) {
69+
final skImageFilter = colorFilter.initRawImageFilter();
70+
borrow(skImageFilter);
71+
skImageFilter.delete();
7572
}
7673

7774
@override
@@ -93,7 +90,14 @@ class CkColorFilterImageFilter extends CkImageFilter {
9390
class _CkBlurImageFilter extends CkImageFilter {
9491
_CkBlurImageFilter(
9592
{required this.sigmaX, required this.sigmaY, required this.tileMode})
96-
: super._() {
93+
: super._();
94+
95+
final double sigmaX;
96+
final double sigmaY;
97+
final ui.TileMode tileMode;
98+
99+
@override
100+
void withSkImageFilter(SkImageFilterBorrow borrow) {
97101
/// Return the identity matrix when both sigmaX and sigmaY are 0. Replicates
98102
/// effect of applying no filter
99103
final SkImageFilter skImageFilter;
@@ -110,18 +114,9 @@ class _CkBlurImageFilter extends CkImageFilter {
110114
null,
111115
);
112116
}
113-
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.blur');
114-
}
115-
116-
final double sigmaX;
117-
final double sigmaY;
118-
final ui.TileMode tileMode;
119117

120-
late final UniqueRef<SkImageFilter> _ref;
121-
122-
@override
123-
void imageFilter(SkImageFilterBorrow borrow) {
124-
borrow(_ref.nativeObject);
118+
borrow(skImageFilter);
119+
skImageFilter.delete();
125120
}
126121

127122
@override
@@ -149,25 +144,22 @@ class _CkMatrixImageFilter extends CkImageFilter {
149144
{required Float64List matrix, required this.filterQuality})
150145
: matrix = Float64List.fromList(matrix),
151146
_transform = Matrix4.fromFloat32List(toMatrix32(matrix)),
152-
super._() {
153-
final SkImageFilter skImageFilter =
154-
canvasKit.ImageFilter.MakeMatrixTransform(
155-
toSkMatrixFromFloat64(matrix),
156-
toSkFilterOptions(filterQuality),
157-
null,
158-
);
159-
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.matrix');
160-
}
147+
super._();
161148

162149
final Float64List matrix;
163150
final ui.FilterQuality filterQuality;
164151
final Matrix4 _transform;
165152

166-
late final UniqueRef<SkImageFilter> _ref;
167-
168153
@override
169-
void imageFilter(SkImageFilterBorrow borrow) {
170-
borrow(_ref.nativeObject);
154+
void withSkImageFilter(SkImageFilterBorrow borrow) {
155+
final skImageFilter =
156+
canvasKit.ImageFilter.MakeMatrixTransform(
157+
toSkMatrixFromFloat64(matrix),
158+
toSkFilterOptions(filterQuality),
159+
null,
160+
);
161+
borrow(skImageFilter);
162+
skImageFilter.delete();
171163
}
172164

173165
@override
@@ -192,23 +184,20 @@ class _CkMatrixImageFilter extends CkImageFilter {
192184

193185
class _CkDilateImageFilter extends CkImageFilter {
194186
_CkDilateImageFilter({required this.radiusX, required this.radiusY})
195-
: super._() {
196-
final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeDilate(
197-
radiusX,
198-
radiusY,
199-
null,
200-
);
201-
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.dilate');
202-
}
187+
: super._();
203188

204189
final double radiusX;
205190
final double radiusY;
206191

207-
late final UniqueRef<SkImageFilter> _ref;
208-
209192
@override
210-
void imageFilter(SkImageFilterBorrow borrow) {
211-
borrow(_ref.nativeObject);
193+
void withSkImageFilter(SkImageFilterBorrow borrow) {
194+
final skImageFilter = canvasKit.ImageFilter.MakeDilate(
195+
radiusX,
196+
radiusY,
197+
null,
198+
);
199+
borrow(skImageFilter);
200+
skImageFilter.delete();
212201
}
213202

214203
@override
@@ -232,23 +221,20 @@ class _CkDilateImageFilter extends CkImageFilter {
232221

233222
class _CkErodeImageFilter extends CkImageFilter {
234223
_CkErodeImageFilter({required this.radiusX, required this.radiusY})
235-
: super._() {
236-
final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeErode(
237-
radiusX,
238-
radiusY,
239-
null,
240-
);
241-
_ref = UniqueRef<SkImageFilter>(this, skImageFilter, 'ImageFilter.erode');
242-
}
224+
: super._();
243225

244226
final double radiusX;
245227
final double radiusY;
246228

247-
late final UniqueRef<SkImageFilter> _ref;
248-
249229
@override
250-
void imageFilter(SkImageFilterBorrow borrow) {
251-
borrow(_ref.nativeObject);
230+
void withSkImageFilter(SkImageFilterBorrow borrow) {
231+
final skImageFilter = canvasKit.ImageFilter.MakeErode(
232+
radiusX,
233+
radiusY,
234+
null,
235+
);
236+
borrow(skImageFilter);
237+
skImageFilter.delete();
252238
}
253239

254240
@override
@@ -272,27 +258,23 @@ class _CkErodeImageFilter extends CkImageFilter {
272258

273259
class _CkComposeImageFilter extends CkImageFilter {
274260
_CkComposeImageFilter({required this.outer, required this.inner})
275-
: super._() {
276-
outer.imageFilter((SkImageFilter outerFilter) {
277-
inner.imageFilter((SkImageFilter innerFilter) {
278-
final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeCompose(
279-
outerFilter,
280-
innerFilter,
281-
);
282-
_ref = UniqueRef<SkImageFilter>(
283-
this, skImageFilter, 'ImageFilter.compose');
284-
});
285-
});
286-
}
261+
: super._();
287262

288263
final CkImageFilter outer;
289264
final CkImageFilter inner;
290265

291-
late final UniqueRef<SkImageFilter> _ref;
292-
293266
@override
294-
void imageFilter(SkImageFilterBorrow borrow) {
295-
borrow(_ref.nativeObject);
267+
void withSkImageFilter(SkImageFilterBorrow borrow) {
268+
outer.withSkImageFilter((skOuter) {
269+
inner.withSkImageFilter((skInner) {
270+
final skImageFilter = canvasKit.ImageFilter.MakeCompose(
271+
skOuter,
272+
skInner,
273+
);
274+
borrow(skImageFilter);
275+
skImageFilter.delete();
276+
});
277+
});
296278
}
297279

298280
@override

lib/web_ui/lib/src/engine/canvaskit/layer.dart

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,16 +417,17 @@ class ImageFilterEngineLayer extends ContainerLayer
417417
}
418418
final ui.Rect childPaintBounds =
419419
prerollChildren(prerollContext, childMatrix);
420-
convertible.imageFilter((SkImageFilter filter) {
421-
// If the filter is a ColorFilter, the extended paint bounds will be the
422-
// entire screen, which is not what we want.
423420
if (_filter is ui.ColorFilter) {
421+
// If the filter is a ColorFilter, the extended paint bounds will be the
422+
// entire screen, which is not what we want.
424423
paintBounds = childPaintBounds;
425424
} else {
426-
paintBounds =
427-
rectFromSkIRect(filter.getOutputBounds(toSkRect(childPaintBounds)));
425+
convertible.withSkImageFilter((skFilter) {
426+
paintBounds = rectFromSkIRect(
427+
skFilter.getOutputBounds(toSkRect(childPaintBounds)),
428+
);
429+
});
428430
}
429-
});
430431
prerollContext.mutatorsStack.pop();
431432
}
432433

lib/web_ui/lib/src/engine/canvaskit/painting.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'shader.dart';
2121
///
2222
/// This class is backed by a Skia object that must be explicitly
2323
/// deleted to avoid a memory leak. This is done by extending [SkiaObject].
24+
// TODO(154281): try to unify with SkwasmPaint
2425
class CkPaint implements ui.Paint {
2526
CkPaint();
2627

@@ -63,7 +64,7 @@ class CkPaint implements ui.Paint {
6364

6465
final localImageFilter = _imageFilter;
6566
if (localImageFilter != null) {
66-
localImageFilter.imageFilter((SkImageFilter skImageFilter) {
67+
localImageFilter.withSkImageFilter((skImageFilter) {
6768
skPaint.setImageFilter(skImageFilter);
6869
});
6970
}

lib/web_ui/test/canvaskit/filter_test.dart

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,25 @@ void testMain() {
5454
setUpCanvasKitTest(withImplicitView: true);
5555

5656
group('ImageFilters', () {
57-
test('can be constructed', () {
58-
final CkImageFilter imageFilter = CkImageFilter.blur(sigmaX: 5, sigmaY: 10, tileMode: ui.TileMode.clamp);
59-
expect(imageFilter, isA<CkImageFilter>());
60-
SkImageFilter? skFilter;
61-
imageFilter.imageFilter((SkImageFilter value) {
62-
skFilter = value;
63-
});
64-
expect(skFilter, isNotNull);
65-
});
66-
57+
{
58+
final testFilters = createImageFilters();
59+
for (final imageFilter in testFilters) {
60+
test('${imageFilter.runtimeType}.withSkImageFilter creates temp SkImageFilter', () {
61+
expect(imageFilter, isA<CkImageFilter>());
62+
SkImageFilter? skFilter;
63+
imageFilter.withSkImageFilter((value) {
64+
expect(value.isDeleted(), isFalse);
65+
skFilter = value;
66+
});
67+
expect(skFilter, isNotNull);
68+
expect(
69+
reason: 'Because the SkImageFilter instance is temporary',
70+
skFilter!.isDeleted(),
71+
isTrue,
72+
);
73+
});
74+
}
75+
}
6776

6877
test('== operator', () {
6978
final List<ui.ImageFilter> filters1 = <ui.ImageFilter>[

0 commit comments

Comments
 (0)