Commit fcaf9c40 authored by Jim Graham's avatar Jim Graham Committed by Flutter GitHub Bot

Support for ImageFiltered widget to apply ImageFilter to children. (#47489)

parent 49c78459
......@@ -1548,6 +1548,48 @@ class ColorFilterLayer extends ContainerLayer {
}
}
/// A composite layer that applies an [ImageFilter] to its children.
class ImageFilterLayer extends ContainerLayer {
/// Creates a layer that applies an [ImageFilter] to its children.
///
/// The [imageFilter] property must be non-null before the compositing phase
/// of the pipeline.
ImageFilterLayer({
ui.ImageFilter imageFilter,
}) : _imageFilter = imageFilter;
/// The image filter to apply to children.
///
/// The scene must be explicitly recomposited after this property is changed
/// (as described at [Layer]).
ui.ImageFilter get imageFilter => _imageFilter;
ui.ImageFilter _imageFilter;
set imageFilter(ui.ImageFilter value) {
assert(value != null);
if (value != _imageFilter) {
_imageFilter = value;
markNeedsAddToScene();
}
}
@override
void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
assert(imageFilter != null);
engineLayer = builder.pushImageFilter(
imageFilter,
oldLayer: _engineLayer as ui.ImageFilterEngineLayer,
);
addChildrenToScene(builder, layerOffset);
builder.pop();
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<ui.ImageFilter>('imageFilter', imageFilter));
}
}
/// A composited layer that applies a given transformation matrix to its
/// children.
///
......
// Copyright 2014 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:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'framework.dart';
/// Applies an [ImageFilter] to its child.
@immutable
class ImageFiltered extends SingleChildRenderObjectWidget {
/// Creates a widget that applies an [ImageFilter] to its child.
///
/// The [imageFilter] must not be null.
const ImageFiltered({
Key key,
@required this.imageFilter,
Widget child,
}) : assert(imageFilter != null),
super(key: key, child: child);
/// The image filter to apply to the child of this widget.
final ImageFilter imageFilter;
@override
RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter);
@override
void updateRenderObject(BuildContext context, _ImageFilterRenderObject renderObject) {
renderObject..imageFilter = imageFilter;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DiagnosticsProperty<ImageFilter>('imageFilter', imageFilter));
}
}
class _ImageFilterRenderObject extends RenderProxyBox {
_ImageFilterRenderObject(this._imageFilter);
ImageFilter get imageFilter => _imageFilter;
ImageFilter _imageFilter;
set imageFilter(ImageFilter value) {
assert(value != null);
if (value != _imageFilter) {
_imageFilter = value;
markNeedsPaint();
}
}
@override
bool get alwaysNeedsCompositing => child != null;
@override
void paint(PaintingContext context, Offset offset) {
assert(imageFilter != null);
if (layer == null) {
layer = ImageFilterLayer(imageFilter: imageFilter);
} else {
final ImageFilterLayer filterLayer = layer as ImageFilterLayer;
filterLayer
..imageFilter = imageFilter;
}
context.pushLayer(layer, super.paint, offset);
assert(layer != null);
}
}
......@@ -48,6 +48,7 @@ export 'src/widgets/icon_data.dart';
export 'src/widgets/icon_theme.dart';
export 'src/widgets/icon_theme_data.dart';
export 'src/widgets/image.dart';
export 'src/widgets/image_filter.dart';
export 'src/widgets/image_icon.dart';
export 'src/widgets/implicit_animations.dart';
export 'src/widgets/inherited_model.dart';
......
// Copyright 2014 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:typed_data';
import 'dart:ui';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() {
testWidgets('Image filter - blur', (WidgetTester tester) async {
await tester.pumpWidget(
RepaintBoundary(
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: const Placeholder(),
),
),
);
await expectLater(
find.byType(ImageFiltered),
matchesGoldenFile('image_filter_blur.png'),
);
});
testWidgets('Image filter - matrix', (WidgetTester tester) async {
final ImageFilter matrix = ImageFilter.matrix(Float64List.fromList(<double>[
0.5, 0.0, 0.0, 0.0, //
0.0, 0.5, 0.0, 0.0, //
0.0, 0.0, 1.0, 0.0, //
0.0, 0.0, 0.0, 1.0, //
]));
await tester.pumpWidget(
RepaintBoundary(
child: ImageFiltered(
imageFilter: matrix,
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: const Text('Matrix ImageFilter Test'),
),
body: const Center(
child:Text('Hooray!'),
),
floatingActionButton: FloatingActionButton(
onPressed: () { },
tooltip: 'Increment',
child: const Icon(Icons.add),
),
),
),
),
),
);
await expectLater(
find.byType(ImageFiltered),
matchesGoldenFile('image_filter_matrix.png'),
);
}, skip: isBrowser);
testWidgets('Image filter - reuses its layer', (WidgetTester tester) async {
Future<void> pumpWithSigma(double sigma) async {
await tester.pumpWidget(
RepaintBoundary(
child: ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: sigma, sigmaY: sigma),
child: const Placeholder(),
),
),
);
}
await pumpWithSigma(5.0);
final RenderObject renderObject = tester.firstRenderObject(find.byType(ImageFiltered));
final ImageFilterLayer originalLayer = renderObject.debugLayer as ImageFilterLayer;
expect(originalLayer, isNotNull);
// Change blur sigma to force a repaint.
await pumpWithSigma(10.0);
expect(renderObject.debugLayer, same(originalLayer));
});
}
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