Commit 51ba6b37 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Fix DecorationImage.centerSlice (#10257)

...and rearrange a bunch of code so that all these arguments/members
are always in the same order.
parent 61e938aa
......@@ -1239,9 +1239,9 @@ void paintImage({
@required ui.Image image,
ColorFilter colorFilter,
BoxFit fit,
ImageRepeat repeat: ImageRepeat.noRepeat,
FractionalOffset alignment,
Rect centerSlice,
FractionalOffset alignment
ImageRepeat repeat: ImageRepeat.noRepeat,
}) {
assert(canvas != null);
assert(image != null);
......@@ -1266,7 +1266,7 @@ void paintImage({
destinationSize += sliceBorder;
// We don't have the ability to draw a subset of the image at the same time
// as we apply a nine-patch stretch.
assert(sourceSize == inputSize);
assert(sourceSize == inputSize, 'centerSlice was used with a BoxFit that does not guarantee that the image is fully visible.');
}
if (repeat != ImageRepeat.noRepeat && destinationSize == outputSize) {
// There's no need to repeat the image because we're exactly filling the
......@@ -1315,11 +1315,11 @@ class DecorationImage {
/// The [image] argument must not be null.
const DecorationImage({
@required this.image,
this.fit,
this.repeat: ImageRepeat.noRepeat,
this.centerSlice,
this.colorFilter,
this.fit,
this.alignment,
this.centerSlice,
this.repeat: ImageRepeat.noRepeat,
}) : assert(image != null);
/// The image to be painted into the decoration.
......@@ -1328,6 +1328,9 @@ class DecorationImage {
/// application) or a [NetworkImage] (for an image obtained from the network).
final ImageProvider image;
/// A color filter to apply to the image before painting it.
final ColorFilter colorFilter;
/// How the image should be inscribed into the box.
///
/// The default is [BoxFit.scaleDown] if [centerSlice] is null, and
......@@ -1336,9 +1339,14 @@ class DecorationImage {
/// See the discussion at [paintImage] for more details.
final BoxFit fit;
/// How to paint any portions of the box that would not otherwise be covered
/// by the image.
final ImageRepeat repeat;
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
///
/// Defaults to [FractionalOffset.center].
final FractionalOffset alignment;
/// The center slice for a nine-patch image.
///
......@@ -1357,17 +1365,9 @@ class DecorationImage {
/// scaling, as if it wasn't specified).
final Rect centerSlice;
/// A color filter to apply to the image before painting it.
final ColorFilter colorFilter;
/// How to align the image within its bounds.
///
/// An alignment of (0.0, 0.0) aligns the image to the top-left corner of its
/// layout bounds. An alignment of (1.0, 0.5) aligns the image to the middle
/// of the right edge of its layout bounds.
///
/// Defaults to [FractionalOffset.center].
final FractionalOffset alignment;
/// How to paint any portions of the box that would not otherwise be covered
/// by the image.
final ImageRepeat repeat;
@override
bool operator ==(dynamic other) {
......@@ -1376,19 +1376,35 @@ class DecorationImage {
if (runtimeType != other.runtimeType)
return false;
final DecorationImage typedOther = other;
return image == typedOther.image &&
fit == typedOther.fit &&
repeat == typedOther.repeat &&
centerSlice == typedOther.centerSlice &&
colorFilter == typedOther.colorFilter &&
alignment == typedOther.alignment;
return image == typedOther.image
&& colorFilter == typedOther.colorFilter
&& fit == typedOther.fit
&& alignment == typedOther.alignment
&& centerSlice == typedOther.centerSlice
&& repeat == typedOther.repeat;
}
@override
int get hashCode => hashValues(image, fit, repeat, centerSlice, colorFilter, alignment);
int get hashCode => hashValues(image, colorFilter, fit, alignment, centerSlice, repeat);
@override
String toString() => '$runtimeType($image, $fit, $repeat)';
String toString() {
final List<String> properties = <String>[];
properties.add('$image');
if (colorFilter != null)
properties.add('$colorFilter');
if (fit != null &&
!(fit == BoxFit.fill && centerSlice != null) &&
!(fit == BoxFit.scaleDown && centerSlice == null))
properties.add('$fit');
if (alignment != null)
properties.add('$alignment');
if (centerSlice != null)
properties.add('centerSlice: $centerSlice');
if (repeat != ImageRepeat.noRepeat)
properties.add('$repeat');
return '$runtimeType(${properties.join(", ")})';
}
}
/// An immutable description of how to paint a box.
......@@ -1452,7 +1468,7 @@ class BoxDecoration extends Decoration {
this.borderRadius,
this.boxShadow,
this.gradient,
this.shape: BoxShape.rectangle
this.shape: BoxShape.rectangle,
});
@override
......@@ -1505,7 +1521,7 @@ class BoxDecoration extends Decoration {
borderRadius: BorderRadius.lerp(null, borderRadius, factor),
boxShadow: BoxShadow.lerpList(null, boxShadow, factor),
gradient: gradient,
shape: shape
shape: shape,
);
}
......@@ -1532,7 +1548,7 @@ class BoxDecoration extends Decoration {
borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t),
boxShadow: BoxShadow.lerpList(a.boxShadow, b.boxShadow, t),
gradient: b.gradient,
shape: b.shape
shape: b.shape,
);
}
......@@ -1575,7 +1591,7 @@ class BoxDecoration extends Decoration {
borderRadius,
boxShadow,
gradient,
shape
shape,
);
}
......@@ -1741,9 +1757,10 @@ class _BoxDecorationPainter extends BoxPainter {
rect: rect,
image: image,
colorFilter: backgroundImage.colorFilter,
alignment: backgroundImage.alignment,
fit: backgroundImage.fit,
repeat: backgroundImage.repeat
alignment: backgroundImage.alignment,
centerSlice: backgroundImage.centerSlice,
repeat: backgroundImage.repeat,
);
if (clipPath != null)
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui show Image;
import 'dart:ui' as ui show Image, ColorFilter;
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
......@@ -33,7 +33,7 @@ class SynchronousTestImageProvider extends ImageProvider<int> {
@override
ImageStreamCompleter load(int key) {
return new OneFrameImageStreamCompleter(
new SynchronousFuture<ImageInfo>(new TestImageInfo(key))
new SynchronousFuture<ImageInfo>(new TestImageInfo(key, image: new TestImage(), scale: 1.0))
);
}
}
......@@ -90,7 +90,7 @@ class TestImage extends ui.Image {
}
void main() {
test("Decoration.lerp()", () {
test('Decoration.lerp()', () {
final BoxDecoration a = const BoxDecoration(color: const Color(0xFFFFFFFF));
final BoxDecoration b = const BoxDecoration(color: const Color(0x00000000));
......@@ -104,7 +104,7 @@ void main() {
expect(c.color, equals(b.color));
});
test("BoxDecorationImageListenerSync", () {
test('BoxDecorationImageListenerSync', () {
final ImageProvider imageProvider = new SynchronousTestImageProvider();
final DecorationImage backgroundImage = new DecorationImage(image: imageProvider);
......@@ -122,7 +122,7 @@ void main() {
expect(onChangedCalled, equals(false));
});
test("BoxDecorationImageListenerAsync", () {
test('BoxDecorationImageListenerAsync', () {
new FakeAsync().run((FakeAsync async) {
final ImageProvider imageProvider = new AsyncTestImageProvider();
final DecorationImage backgroundImage = new DecorationImage(image: imageProvider);
......@@ -146,7 +146,7 @@ void main() {
// Regression test for https://github.com/flutter/flutter/issues/7289.
// A reference test would be better.
test("BoxDecoration backgroundImage clip", () {
test('BoxDecoration backgroundImage clip', () {
void testDecoration({ BoxShape shape, BorderRadius borderRadius, bool expectClip}) {
new FakeAsync().run((FakeAsync async) {
final DelayedImageProvider imageProvider = new DelayedImageProvider();
......@@ -198,4 +198,32 @@ void main() {
testDecoration(borderRadius: const BorderRadius.all(const Radius.circular(16.0)), expectClip: true);
testDecoration(expectClip: false);
});
test('DecorationImage test', () {
final ColorFilter colorFilter = const ui.ColorFilter.mode(const Color(0xFF00FF00), BlendMode.src);
final DecorationImage backgroundImage = new DecorationImage(
image: new SynchronousTestImageProvider(),
colorFilter: colorFilter,
fit: BoxFit.contain,
alignment: FractionalOffset.bottomLeft,
centerSlice: new Rect.fromLTWH(10.0, 20.0, 30.0, 40.0),
repeat: ImageRepeat.repeatY,
);
final BoxDecoration boxDecoration = new BoxDecoration(image: backgroundImage);
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { assert(false); });
final TestCanvas canvas = new TestCanvas(<Invocation>[]);
boxPainter.paint(canvas, Offset.zero, const ImageConfiguration(size: const Size(10.0, 10.0)));
final Invocation call = canvas.invocations.singleWhere((Invocation call) => call.memberName == #drawImageNine);
expect(call.isMethod, isTrue);
expect(call.positionalArguments, hasLength(4));
expect(call.positionalArguments[0], const isInstanceOf<TestImage>());
expect(call.positionalArguments[1], new Rect.fromLTRB(10.0, 20.0, 40.0, 60.0));
expect(call.positionalArguments[2], new Rect.fromLTRB(0.0, 0.0, 32.5, 10.0));
expect(call.positionalArguments[3], const isInstanceOf<Paint>());
expect(call.positionalArguments[3].isAntiAlias, false);
expect(call.positionalArguments[3].colorFilter, colorFilter);
expect(call.positionalArguments[3].filterQuality, FilterQuality.low);
});
}
......@@ -31,7 +31,7 @@ class TestCanvas implements Canvas {
}
void main() {
test("Cover and align", () {
test('Cover and align', () {
final TestImage image = new TestImage(width: 300, height: 300);
final TestCanvas canvas = new TestCanvas();
paintImage(
......@@ -51,4 +51,6 @@ void main() {
expect(command.positionalArguments[1], equals(new Rect.fromLTWH(0.0, 75.0, 300.0, 150.0)));
expect(command.positionalArguments[2], equals(new Rect.fromLTWH(50.0, 75.0, 200.0, 100.0)));
});
// See also the DecorationImage tests in: decoration_test.dart
}
......@@ -9,13 +9,13 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
class TestImageInfo implements ImageInfo {
const TestImageInfo(this.value) : image = null, scale = null;
const TestImageInfo(this.value, { this.image, this.scale });
@override
final ui.Image image; // ignored in test
final ui.Image image;
@override
final double scale; // ignored in test
final double scale;
final int value;
......
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