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 [backgroundColor] is null, this decoration does not paint a background color.
|
||||||
/// * If [backgroundImage] is null, this decoration does not paint a background image.
|
/// * If [backgroundImage] is null, this decoration does not paint a background image.
|
||||||
/// * If [border] is null, this decoration does not paint a border.
|
/// * 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
|
/// painting commands. The [borderRadius] argument must be be null if [shape] is
|
||||||
/// [BoxShape.circle].
|
/// [BoxShape.circle].
|
||||||
/// * If [boxShadow] is null, this decoration does not paint a shadow.
|
/// * 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).
|
/// potentially with a border radius, or a circle).
|
||||||
final Color backgroundColor;
|
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;
|
final BackgroundImage backgroundImage;
|
||||||
|
|
||||||
/// A border to draw above the background.
|
/// A border to draw above the background.
|
||||||
@ -1333,6 +1334,17 @@ class _BoxDecorationPainter extends BoxPainter {
|
|||||||
final ui.Image image = _image?.image;
|
final ui.Image image = _image?.image;
|
||||||
if (image == null)
|
if (image == null)
|
||||||
return;
|
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(
|
paintImage(
|
||||||
canvas: canvas,
|
canvas: canvas,
|
||||||
rect: rect,
|
rect: rect,
|
||||||
@ -1342,6 +1354,9 @@ class _BoxDecorationPainter extends BoxPainter {
|
|||||||
fit: backgroundImage.fit,
|
fit: backgroundImage.fit,
|
||||||
repeat: backgroundImage.repeat
|
repeat: backgroundImage.repeat
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (clipPath != null)
|
||||||
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _imageListener(ImageInfo value, bool synchronousCall) {
|
void _imageListener(ImageInfo value, bool synchronousCall) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui' as ui show Image;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
@ -13,8 +14,14 @@ import 'package:test/test.dart';
|
|||||||
import '../services/mocks_for_image_cache.dart';
|
import '../services/mocks_for_image_cache.dart';
|
||||||
|
|
||||||
class TestCanvas implements Canvas {
|
class TestCanvas implements Canvas {
|
||||||
|
TestCanvas([this.invocations]);
|
||||||
|
|
||||||
|
final List<Invocation> invocations;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void noSuchMethod(Invocation invocation) {}
|
void noSuchMethod(Invocation invocation) {
|
||||||
|
invocations?.add(invocation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SynchronousTestImageProvider extends ImageProvider<int> {
|
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() {
|
void main() {
|
||||||
test("Decoration.lerp()", () {
|
test("Decoration.lerp()", () {
|
||||||
BoxDecoration a = const BoxDecoration(backgroundColor: const Color(0xFFFFFFFF));
|
BoxDecoration a = const BoxDecoration(backgroundColor: const Color(0xFFFFFFFF));
|
||||||
@ -99,4 +143,59 @@ void main() {
|
|||||||
expect(onChangedCalled, equals(true));
|
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