Commit c9c577ae authored by Hans Muller's avatar Hans Muller Committed by GitHub

BoxDecoration should clip its backgroundImage if shape is BoxShape.circle (#7292)

parent 82fc87fc
......@@ -1051,7 +1051,7 @@ class BoxDecoration extends Decoration {
/// * If [backgroundColor] is null, this decoration does not paint a background color.
/// * If [backgroundImage] is null, this decoration does not paint a background image.
/// * If [border] is null, this decoration does not paint a border.
/// * If [borderRadius] is null, this decoration use more efficient background
/// * If [borderRadius] is null, this decoration uses more efficient background
/// painting commands. The [borderRadius] argument must be be null if [shape] is
/// [BoxShape.circle].
/// * If [boxShadow] is null, this decoration does not paint a shadow.
......@@ -1079,7 +1079,8 @@ class BoxDecoration extends Decoration {
/// potentially with a border radius, or a circle).
final Color backgroundColor;
/// An image to paint above the background color.
/// An image to paint above the background color. If [shape] is [BoxShape.circle]
/// then the image is clipped to the circle's boundary.
final BackgroundImage backgroundImage;
/// A border to draw above the background.
......@@ -1333,6 +1334,17 @@ class _BoxDecorationPainter extends BoxPainter {
final ui.Image image = _image?.image;
if (image == null)
return;
Path clipPath;
if (_decoration.shape == BoxShape.circle)
clipPath = new Path()..addOval(rect);
else if (_decoration.borderRadius != null)
clipPath = new Path()..addRRect(_decoration.borderRadius.toRRect(rect));
if (clipPath != null) {
canvas.save();
canvas.clipPath(clipPath);
}
paintImage(
canvas: canvas,
rect: rect,
......@@ -1342,6 +1354,9 @@ class _BoxDecorationPainter extends BoxPainter {
fit: backgroundImage.fit,
repeat: backgroundImage.repeat
);
if (clipPath != null)
canvas.restore();
}
void _imageListener(ImageInfo value, bool synchronousCall) {
......
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui show Image;
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
......@@ -13,8 +14,14 @@ import 'package:test/test.dart';
import '../services/mocks_for_image_cache.dart';
class TestCanvas implements Canvas {
TestCanvas([this.invocations]);
final List<Invocation> invocations;
@override
void noSuchMethod(Invocation invocation) {}
void noSuchMethod(Invocation invocation) {
invocations?.add(invocation);
}
}
class SynchronousTestImageProvider extends ImageProvider<int> {
......@@ -45,6 +52,43 @@ class AsyncTestImageProvider extends ImageProvider<int> {
}
}
class BackgroundImageProvider extends ImageProvider<BackgroundImageProvider> {
final Completer<ImageInfo> _completer = new Completer<ImageInfo>();
@override
Future<BackgroundImageProvider> obtainKey(ImageConfiguration configuration) {
return new SynchronousFuture<BackgroundImageProvider>(this);
}
@override
ImageStream resolve(ImageConfiguration configuration) {
return super.resolve(configuration);
}
@override
ImageStreamCompleter load(BackgroundImageProvider key) {
return new OneFrameImageStreamCompleter(_completer.future);
}
void complete() {
_completer.complete(new ImageInfo(image: new TestImage()));
}
@override
String toString() => '$runtimeType($hashCode)';
}
class TestImage extends ui.Image {
@override
int get width => 100;
@override
int get height => 100;
@override
void dispose() { }
}
void main() {
test("Decoration.lerp()", () {
BoxDecoration a = const BoxDecoration(backgroundColor: const Color(0xFFFFFFFF));
......@@ -99,4 +143,59 @@ void main() {
expect(onChangedCalled, equals(true));
});
});
// Regression test for https://github.com/flutter/flutter/issues/7289.
// A reference test would be better.
test("BoxDecoration backgroundImage clip", () {
void testDecoration({ BoxShape shape, BorderRadius borderRadius, bool expectClip}) {
new FakeAsync().run((FakeAsync async) {
BackgroundImageProvider imageProvider = new BackgroundImageProvider();
BackgroundImage backgroundImage = new BackgroundImage(image: imageProvider);
BoxDecoration boxDecoration = new BoxDecoration(
shape: shape,
borderRadius: borderRadius,
backgroundImage: backgroundImage,
);
List<Invocation> invocations = <Invocation>[];
TestCanvas canvas = new TestCanvas(invocations);
ImageConfiguration imageConfiguration = const ImageConfiguration(
size: const Size(100.0, 100.0)
);
bool onChangedCalled = false;
BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
onChangedCalled = true;
});
// _BoxDecorationPainter._paintBackgroundImage() resolves the background
// image and adds a listener to the resolved image stream.
boxPainter.paint(canvas, Offset.zero, imageConfiguration);
imageProvider.complete();
// Run the listener which calls onChanged() which saves an internal
// reference to the TestImage.
async.flushMicrotasks();
expect(onChangedCalled, isTrue);
boxPainter.paint(canvas, Offset.zero, imageConfiguration);
// We expect a clip to preceed the drawImageRect call.
List<Invocation> commands = canvas.invocations.where((Invocation invocation) {
return invocation.memberName == #clipPath || invocation.memberName == #drawImageRect;
}).toList();
if (expectClip) { // We expect a clip to preceed the drawImageRect call.
expect(commands.length, 2);
expect(commands[0].memberName, equals(#clipPath));
expect(commands[1].memberName, equals(#drawImageRect));
} else {
expect(commands.length, 1);
expect(commands[0].memberName, equals(#drawImageRect));
}
});
}
testDecoration(shape: BoxShape.circle, expectClip: true);
testDecoration(borderRadius: new BorderRadius.all(const Radius.circular(16.0)), expectClip: true);
testDecoration(expectClip: false);
});
}
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