Skip to content

Commit

Permalink
Implement ImageFilter/ColorFilter/MaskFilter in Skwasm (#42088)
Browse files Browse the repository at this point in the history
This implements flutter/flutter#126342

This implements `ImageFilter`, `ColorFilter` and `MaskFilter` in Skwasm. This includes support on the `Paint` object, as well as the `SceneBuilder` layers that use these types.
  • Loading branch information
eyebrowsoffire committed May 20, 2023
1 parent 90b44e3 commit 18aec20
Show file tree
Hide file tree
Showing 21 changed files with 912 additions and 71 deletions.
8 changes: 8 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1974,6 +1974,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/raw_keyboard.dart + ../../../
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/renderer.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/scene_painting.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart + ../../../flutter/LICENSE
Expand All @@ -1997,6 +1998,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/shader_data.dart + ../../../f
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart + ../../../flutter/LICENSE
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
Expand All @@ -2006,6 +2008,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -2077,6 +2080,7 @@ ORIGIN: ../../../flutter/lib/web_ui/skwasm/canvas.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/data.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/export.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/filters.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/fonts.cpp + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/helpers.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/skwasm/image.cpp + ../../../flutter/LICENSE
Expand Down Expand Up @@ -4598,6 +4602,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/raw_keyboard.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/renderer.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/rrect_renderer.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/safe_browser_api.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/scene_painting.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/accessibility.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/semantics/checkable.dart
Expand All @@ -4621,6 +4626,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/shader_data.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/shadow.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart
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
Expand All @@ -4630,6 +4636,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/path_metrics.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/picture.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_filters.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_fonts.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_geometry.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw/raw_image.dart
Expand Down Expand Up @@ -4701,6 +4708,7 @@ FILE: ../../../flutter/lib/web_ui/skwasm/canvas.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/data.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/export.h
FILE: ../../../flutter/lib/web_ui/skwasm/filters.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/fonts.cpp
FILE: ../../../flutter/lib/web_ui/skwasm/helpers.h
FILE: ../../../flutter/lib/web_ui/skwasm/image.cpp
Expand Down
1 change: 1 addition & 0 deletions lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export 'engine/raw_keyboard.dart';
export 'engine/renderer.dart';
export 'engine/rrect_renderer.dart';
export 'engine/safe_browser_api.dart';
export 'engine/scene_painting.dart';
export 'engine/semantics/accessibility.dart';
export 'engine/semantics/checkable.dart';
export 'engine/semantics/dialog.dart';
Expand Down
7 changes: 6 additions & 1 deletion lib/web_ui/lib/src/engine/color_filter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:ui/src/engine.dart';
import 'package:ui/ui.dart' as ui;

enum ColorFilterType {
Expand All @@ -19,7 +20,7 @@ enum ColorFilterType {
///
/// Instances of this class are used with [Paint.colorFilter] on [Paint]
/// objects.
class EngineColorFilter implements ui.ColorFilter {
class EngineColorFilter implements SceneImageFilter, ui.ColorFilter {
/// Creates a color filter that applies the blend mode given as the second
/// argument. The source color is the one given as the first argument, and the
/// destination color is the one from the layer being composited.
Expand Down Expand Up @@ -116,4 +117,8 @@ class EngineColorFilter implements ui.ColorFilter {
final ui.BlendMode? blendMode;
final List<double>? matrix;
final ColorFilterType type;

/// Color filters don't affect the image bounds
@override
ui.Rect filterBounds(ui.Rect inputBounds) => inputBounds;
}
25 changes: 25 additions & 0 deletions lib/web_ui/lib/src/engine/scene_painting.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 'package:ui/ui.dart' as ui;

// These are additional APIs that are not part of the `dart:ui` interface that
// are needed internally to properly implement a `SceneBuilder` on top of the
// generic Canvas/Picture api.
abstract class SceneCanvas implements ui.Canvas {
// This is the same as a normal `saveLayer` call, but we can pass a backdrop image filter.
void saveLayerWithFilter(ui.Rect? bounds, ui.Paint paint, ui.ImageFilter backdropFilter);
}

abstract class ScenePicture implements ui.Picture {
// This is a conservative bounding box of all the drawing primitives in this picture.
ui.Rect get cullRect;
}

abstract class SceneImageFilter implements ui.ImageFilter {
// Since some image filters affect the actual drawing bounds of a given picture, this
// gives the maximum draw boundary for a picture with the given input bounds after it
// has been processed by the filter.
ui.Rect filterBounds(ui.Rect inputBounds);
}
2 changes: 2 additions & 0 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ library skwasm_impl;
import 'dart:ffi';

export 'skwasm_impl/canvas.dart';
export 'skwasm_impl/filters.dart';
export 'skwasm_impl/font_collection.dart';
export 'skwasm_impl/image.dart';
export 'skwasm_impl/layers.dart';
Expand All @@ -19,6 +20,7 @@ export 'skwasm_impl/path.dart';
export 'skwasm_impl/path_metrics.dart';
export 'skwasm_impl/picture.dart';
export 'skwasm_impl/raw/raw_canvas.dart';
export 'skwasm_impl/raw/raw_filters.dart';
export 'skwasm_impl/raw/raw_fonts.dart';
export 'skwasm_impl/raw/raw_geometry.dart';
export 'skwasm_impl/raw/raw_image.dart';
Expand Down
19 changes: 16 additions & 3 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl/canvas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:ui/src/engine.dart';
import 'package:ui/src/engine/skwasm/skwasm_impl.dart';
import 'package:ui/ui.dart' as ui;

class SkwasmCanvas implements ui.Canvas {
class SkwasmCanvas implements SceneCanvas {
factory SkwasmCanvas(SkwasmPictureRecorder recorder, ui.Rect cullRect) =>
SkwasmCanvas.fromHandle(withStackScope((StackScope s) =>
pictureRecorderBeginRecording(
Expand All @@ -32,10 +32,23 @@ class SkwasmCanvas implements ui.Canvas {
paint as SkwasmPaint;
if (bounds != null) {
withStackScope((StackScope s) {
canvasSaveLayer(_handle, s.convertRectToNative(bounds), paint.handle);
canvasSaveLayer(_handle, s.convertRectToNative(bounds), paint.handle, nullptr);
});
} else {
canvasSaveLayer(_handle, nullptr, paint.handle);
canvasSaveLayer(_handle, nullptr, paint.handle, nullptr);
}
}

@override
void saveLayerWithFilter(ui.Rect? bounds, ui.Paint paint, ui.ImageFilter imageFilter) {
final SkwasmImageFilter nativeFilter = SkwasmImageFilter.fromUiFilter(imageFilter);
paint as SkwasmPaint;
if (bounds != null) {
withStackScope((StackScope s) {
canvasSaveLayer(_handle, s.convertRectToNative(bounds), paint.handle, nativeFilter.handle);
});
} else {
canvasSaveLayer(_handle, nullptr, paint.handle, nativeFilter.handle);
}
}

Expand Down
132 changes: 132 additions & 0 deletions lib/web_ui/lib/src/engine/skwasm/skwasm_impl/filters.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// 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:typed_data';

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);

factory SkwasmImageFilter.blur({
double sigmaX = 0.0,
double sigmaY = 0.0,
ui.TileMode tileMode = ui.TileMode.clamp,
}) => SkwasmImageFilter._(imageFilterCreateBlur(sigmaX, sigmaY, tileMode.index));

factory SkwasmImageFilter.dilate({
double radiusX = 0.0,
double radiusY = 0.0,
}) => SkwasmImageFilter._(imageFilterCreateDilate(radiusX, radiusY));

factory SkwasmImageFilter.erode({
double radiusX = 0.0,
double radiusY = 0.0,
}) => SkwasmImageFilter._(imageFilterCreateErode(radiusX, radiusY));

factory SkwasmImageFilter.matrix(
Float64List matrix4, {
ui.FilterQuality filterQuality = ui.FilterQuality.low
}) => withStackScope((StackScope scope) => SkwasmImageFilter._(imageFilterCreateMatrix(
scope.convertMatrix4toSkMatrix(matrix4),
filterQuality.index
)));

factory SkwasmImageFilter.fromColorFilter(SkwasmColorFilter filter) =>
SkwasmImageFilter._(imageFilterCreateFromColorFilter(filter.handle));

factory SkwasmImageFilter.fromUiFilter(ui.ImageFilter filter) {
if (filter is ui.ColorFilter) {
final SkwasmColorFilter colorFilter =
SkwasmColorFilter.fromEngineColorFilter(filter as EngineColorFilter);
final SkwasmImageFilter outputFilter = SkwasmImageFilter.fromColorFilter(colorFilter);
colorFilter.dispose();
return outputFilter;
} else {
return filter as SkwasmImageFilter;
}
}

factory SkwasmImageFilter.compose(
ui.ImageFilter outer,
ui.ImageFilter inner,
) {
final SkwasmImageFilter nativeOuter = SkwasmImageFilter.fromUiFilter(outer);
final SkwasmImageFilter nativeInner = SkwasmImageFilter.fromUiFilter(inner);
return SkwasmImageFilter._(imageFilterCompose(nativeOuter.handle, nativeInner.handle));
}

void dispose() {
if (!_isDisposed) {
imageFilterDispose(handle);
_isDisposed = true;
}
}

final ImageFilterHandle handle;
bool _isDisposed = false;

@override
ui.Rect filterBounds(ui.Rect inputBounds) => withStackScope((StackScope scope) {
final RawIRect rawRect = scope.convertIRectToNative(inputBounds);
imageFilterGetFilterBounds(handle, rawRect);
return scope.convertIRectFromNative(rawRect);
});
}

class SkwasmColorFilter {
SkwasmColorFilter._(this.handle);

factory SkwasmColorFilter.fromEngineColorFilter(EngineColorFilter colorFilter) =>
switch (colorFilter.type) {
ColorFilterType.mode => SkwasmColorFilter._(colorFilterCreateMode(
colorFilter.color!.value,
colorFilter.blendMode!.index,
)),
ColorFilterType.linearToSrgbGamma => SkwasmColorFilter._(colorFilterCreateLinearToSRGBGamma()),
ColorFilterType.srgbToLinearGamma => SkwasmColorFilter._(colorFilterCreateSRGBToLinearGamma()),
ColorFilterType.matrix => withStackScope((StackScope scope) {
final Pointer<Float> nativeMatrix = scope.convertDoublesToNative(colorFilter.matrix!);
return SkwasmColorFilter._(colorFilterCreateMatrix(nativeMatrix));
}),
};

factory SkwasmColorFilter.composed(
SkwasmColorFilter outer,
SkwasmColorFilter inner,
) => SkwasmColorFilter._(colorFilterCompose(outer.handle, inner.handle));

void dispose() {
if (!_isDisposed) {
colorFilterDispose(handle);
_isDisposed = true;
}
}

final ColorFilterHandle handle;
bool _isDisposed = false;
}

class SkwasmMaskFilter {
SkwasmMaskFilter._(this.handle);

factory SkwasmMaskFilter.fromUiMaskFilter(ui.MaskFilter maskFilter) =>
SkwasmMaskFilter._(maskFilterCreateBlur(
maskFilter.webOnlyBlurStyle.index,
maskFilter.webOnlySigma
));

final MaskFilterHandle handle;
bool _isDisposed = false;

void dispose() {
if (!_isDisposed) {
maskFilterDispose(handle);
_isDisposed = true;
}
}
}

0 comments on commit 18aec20

Please sign in to comment.