Unverified Commit f15295cc authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[framework] allow disabling image filter layer (#102085)

parent 0411a6f8
...@@ -11,6 +11,19 @@ import 'framework.dart'; ...@@ -11,6 +11,19 @@ import 'framework.dart';
/// Applies an [ImageFilter] to its child. /// Applies an [ImageFilter] to its child.
/// ///
/// An image filter will always apply its filter operation to the child widget,
/// even if said filter is conceptually a "no-op", such as an ImageFilter.blur
/// with a radius of 0 or an ImageFilter.matrix with an identity matrix. Setting
/// [ImageFiltered.enabled] to `false` is a more efficient manner of disabling
/// an image filter.
///
/// The framework does not attempt to optimize out "no-op" filters because it
/// cannot tell the difference between an intentional no-op and a filter that is
/// only incidentally a no-op. Consider an ImageFilter.matrix that is animated
/// and happens to pass through the identity matrix. If the framework identified it
/// as a no-op it would drop and then recreate the layer during the animation which
/// would be more expensive than keeping it around.
///
/// {@youtube 560 315 https://www.youtube.com/watch?v=7Lftorq4i2o} /// {@youtube 560 315 https://www.youtube.com/watch?v=7Lftorq4i2o}
/// ///
/// See also: /// See also:
...@@ -27,17 +40,27 @@ class ImageFiltered extends SingleChildRenderObjectWidget { ...@@ -27,17 +40,27 @@ class ImageFiltered extends SingleChildRenderObjectWidget {
super.key, super.key,
required this.imageFilter, required this.imageFilter,
super.child, super.child,
this.enabled = true,
}) : assert(imageFilter != null); }) : assert(imageFilter != null);
/// The image filter to apply to the child of this widget. /// The image filter to apply to the child of this widget.
final ImageFilter imageFilter; final ImageFilter imageFilter;
/// Whether or not to apply the image filter opation to the child of this
/// widget.
///
/// Prefer setting enabled to `false` instead of creating a "no-op" filter
/// type for performance reasons.
final bool enabled;
@override @override
RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter); RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter, enabled);
@override @override
void updateRenderObject(BuildContext context, RenderObject renderObject) { void updateRenderObject(BuildContext context, RenderObject renderObject) {
(renderObject as _ImageFilterRenderObject).imageFilter = imageFilter; (renderObject as _ImageFilterRenderObject)
..enabled = enabled
..imageFilter = imageFilter;
} }
@override @override
...@@ -48,7 +71,17 @@ class ImageFiltered extends SingleChildRenderObjectWidget { ...@@ -48,7 +71,17 @@ class ImageFiltered extends SingleChildRenderObjectWidget {
} }
class _ImageFilterRenderObject extends RenderProxyBox { class _ImageFilterRenderObject extends RenderProxyBox {
_ImageFilterRenderObject(this._imageFilter); _ImageFilterRenderObject(this._imageFilter, this._enabled);
bool get enabled => _enabled;
bool _enabled;
set enabled(bool value) {
if (enabled == value) {
return;
}
_enabled = value;
markNeedsPaint();
}
ImageFilter get imageFilter => _imageFilter; ImageFilter get imageFilter => _imageFilter;
ImageFilter _imageFilter; ImageFilter _imageFilter;
...@@ -61,11 +94,16 @@ class _ImageFilterRenderObject extends RenderProxyBox { ...@@ -61,11 +94,16 @@ class _ImageFilterRenderObject extends RenderProxyBox {
} }
@override @override
bool get alwaysNeedsCompositing => child != null; bool get alwaysNeedsCompositing => child != null && enabled;
@override @override
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
assert(imageFilter != null); assert(imageFilter != null);
if (!enabled) {
layer = null;
return super.paint(context, offset);
}
if (layer == null) { if (layer == null) {
layer = ImageFilterLayer(imageFilter: imageFilter); layer = ImageFilterLayer(imageFilter: imageFilter);
} else { } else {
......
...@@ -86,4 +86,25 @@ void main() { ...@@ -86,4 +86,25 @@ void main() {
await pumpWithSigma(10.0); await pumpWithSigma(10.0);
expect(renderObject.debugLayer, same(originalLayer)); expect(renderObject.debugLayer, same(originalLayer));
}); });
testWidgets('Image filter - enabled and disabled', (WidgetTester tester) async {
Future<void> pumpWithEnabledStaet(bool enabled) async {
await tester.pumpWidget(
RepaintBoundary(
child: ImageFiltered(
enabled: enabled,
imageFilter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
child: const Placeholder(),
),
),
);
}
await pumpWithEnabledStaet(false);
expect(tester.layers, isNot(contains(isA<ImageFilterLayer>())));
await pumpWithEnabledStaet(true);
expect(tester.layers, contains(isA<ImageFilterLayer>()));
});
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment