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

[canvaskit] Add ImageFilter.compose #48546

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions lib/web_ui/lib/src/engine/canvaskit/image_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ abstract class CkImageFilter implements CkManagedSkImageFilterConvertible {
factory CkImageFilter.matrix(
{required Float64List matrix,
required ui.FilterQuality filterQuality}) = _CkMatrixImageFilter;
factory CkImageFilter.compose(
{required CkImageFilter outer,
required CkImageFilter inner}) = _CkComposeImageFilter;

CkImageFilter._();

Expand Down Expand Up @@ -194,3 +197,47 @@ class _CkMatrixImageFilter extends CkImageFilter {
@override
Matrix4 get transform => _transform;
}

class _CkComposeImageFilter extends CkImageFilter {
_CkComposeImageFilter({required this.outer, required this.inner})
: super._() {
outer.imageFilter((SkImageFilter outerFilter) {
inner.imageFilter((SkImageFilter innerFilter) {
final SkImageFilter skImageFilter = canvasKit.ImageFilter.MakeCompose(
outerFilter,
innerFilter,
);
_ref = UniqueRef<SkImageFilter>(
this, skImageFilter, 'ImageFilter.compose');
});
});
}

final CkImageFilter outer;
final CkImageFilter inner;

late final UniqueRef<SkImageFilter> _ref;

@override
void imageFilter(SkImageFilterBorrow borrow) {
borrow(_ref.nativeObject);
}

@override
bool operator ==(Object other) {
if (runtimeType != other.runtimeType) {
return false;
}
return other is _CkComposeImageFilter &&
other.outer == outer &&
other.inner == inner;
}

@override
int get hashCode => Object.hash(outer, inner);

@override
String toString() {
return 'ImageFilter.compose($outer, $inner)';
}
}
15 changes: 12 additions & 3 deletions lib/web_ui/lib/src/engine/canvaskit/renderer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,18 @@ class CanvasKitRenderer implements Renderer {
}) => CkImageFilter.matrix(matrix: matrix4, filterQuality: filterQuality);

@override
ui.ImageFilter composeImageFilters({required ui.ImageFilter outer, required ui.ImageFilter inner}) {
// TODO(ferhat): add implementation
throw UnimplementedError('ImageFilter.compose not implemented for CanvasKit.');
ui.ImageFilter composeImageFilters(
{required ui.ImageFilter outer, required ui.ImageFilter inner}) {
if (outer is EngineColorFilter) {
final CkColorFilter colorFilter = createCkColorFilter(outer)!;
outer = CkColorFilterImageFilter(colorFilter: colorFilter);
}
if (inner is EngineColorFilter) {
final CkColorFilter colorFilter = createCkColorFilter(inner)!;
inner = CkColorFilterImageFilter(colorFilter: colorFilter);
}
return CkImageFilter.compose(
outer: outer as CkImageFilter, inner: inner as CkImageFilter);
}

@override
Expand Down
47 changes: 45 additions & 2 deletions lib/web_ui/test/canvaskit/filter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ void testMain() {
}

List<CkImageFilter> createImageFilters() {
return <CkImageFilter>[
final List<CkImageFilter> filters = <CkImageFilter>[
CkImageFilter.blur(sigmaX: 5, sigmaY: 6, tileMode: ui.TileMode.clamp),
CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.clamp),
CkImageFilter.blur(sigmaX: 6, sigmaY: 5, tileMode: ui.TileMode.decal),
for (final CkColorFilter colorFilter in createColorFilters()) CkImageFilter.color(colorFilter: colorFilter),
];
filters.add(CkImageFilter.compose(outer: filters[0], inner: filters[1]));
filters.add(CkImageFilter.compose(outer: filters[1], inner: filters[3]));
return filters;
}

setUpCanvasKitTest();
Expand Down Expand Up @@ -156,7 +159,47 @@ void testMain() {
builder.addPicture(ui.Offset.zero, redCircle1);
// The drawn red circle should actually be green with the colorFilter.

await matchSceneGolden('canvaskit_imageFilter_using_colorFilter.png', builder.build(), region: region);
await matchSceneGolden(
'canvaskit_imageFilter_using_colorFilter.png', builder.build(),
region: region);
});

test('using a compose filter', () async {
final CkImageFilter blurFilter = CkImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
tileMode: ui.TileMode.clamp,
);
final CkColorFilter colorFilter = createCkColorFilter(
const EngineColorFilter.mode(
ui.Color.fromARGB(255, 0, 255, 0), ui.BlendMode.srcIn))!;
final CkImageFilter colorImageFilter =
CkImageFilter.color(colorFilter: colorFilter);
final CkImageFilter composeFilter =
CkImageFilter.compose(outer: blurFilter, inner: colorImageFilter);

const ui.Rect region = ui.Rect.fromLTRB(0, 0, 500, 250);

final LayerSceneBuilder builder = LayerSceneBuilder();
builder.pushOffset(0, 0);

builder.pushImageFilter(composeFilter);

final CkPictureRecorder recorder = CkPictureRecorder();
final CkCanvas canvas = recorder.beginRecording(region);

canvas.drawCircle(
const ui.Offset(75, 125),
50,
CkPaint()..color = const ui.Color.fromARGB(255, 255, 0, 0),
);
final CkPicture redCircle1 = recorder.endRecording();
builder.addPicture(ui.Offset.zero, redCircle1);
// The drawn red circle should actually be green and blurred.

await matchSceneGolden(
'canvaskit_composeImageFilter.png', builder.build(),
region: region);
});
});

Expand Down
4 changes: 2 additions & 2 deletions lib/web_ui/test/ui/filters_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ Future<void> testMain() async {
);
await drawTestImageWithPaint(ui.Paint()..imageFilter = filter);
await matchGoldenFile('ui_filter_composed_imagefilters.png', region: region);
}, skip: !isSkwasm); // Only Skwasm implements composable filters right now.
}, skip: isHtml); // Only Skwasm and CanvasKit implement composable filters right now.

test('compose with colorfilter', () async {
final ui.ImageFilter filter = ui.ImageFilter.compose(
Expand All @@ -116,7 +116,7 @@ Future<void> testMain() async {
);
await drawTestImageWithPaint(ui.Paint()..imageFilter = filter);
await matchGoldenFile('ui_filter_composed_colorfilter.png', region: region);
}, skip: !isSkwasm); // Only Skwasm implements composable filters right now.
}, skip: isHtml); // Only Skwasm and CanvasKit implements composable filters right now.

test('color filter as image filter', () async {
const ui.ColorFilter colorFilter = ui.ColorFilter.mode(
Expand Down