mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
BoxDecoration should clip its backgroundImage if shape is BoxShape.circle (#7292)
This commit is contained in:
parent
82fc87fc4e
commit
c9c577aeeb
@ -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);
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user