mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

This contains the following commit: flutter/engine@cb3376c Support different encodings in Image.toByteData() (#16635)
351 lines
12 KiB
Dart
351 lines
12 KiB
Dart
// Copyright 2016 The Chromium 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:async';
|
|
import 'dart:typed_data';
|
|
import 'dart:ui' as ui show Image, ImageByteFormat, ColorFilter;
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/painting.dart';
|
|
import 'package:quiver/testing/async.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
import '../painting/mocks_for_image_cache.dart';
|
|
import '../rendering/rendering_tester.dart';
|
|
|
|
class TestCanvas implements Canvas {
|
|
TestCanvas([this.invocations]);
|
|
|
|
final List<Invocation> invocations;
|
|
|
|
@override
|
|
void noSuchMethod(Invocation invocation) {
|
|
invocations?.add(invocation);
|
|
}
|
|
}
|
|
|
|
class SynchronousTestImageProvider extends ImageProvider<int> {
|
|
@override
|
|
Future<int> obtainKey(ImageConfiguration configuration) {
|
|
return new SynchronousFuture<int>(1);
|
|
}
|
|
|
|
@override
|
|
ImageStreamCompleter load(int key) {
|
|
return new OneFrameImageStreamCompleter(
|
|
new SynchronousFuture<ImageInfo>(new TestImageInfo(key, image: new TestImage(), scale: 1.0))
|
|
);
|
|
}
|
|
}
|
|
|
|
class AsyncTestImageProvider extends ImageProvider<int> {
|
|
@override
|
|
Future<int> obtainKey(ImageConfiguration configuration) {
|
|
return new Future<int>.value(2);
|
|
}
|
|
|
|
@override
|
|
ImageStreamCompleter load(int key) {
|
|
return new OneFrameImageStreamCompleter(
|
|
new Future<ImageInfo>.value(new TestImageInfo(key))
|
|
);
|
|
}
|
|
}
|
|
|
|
class DelayedImageProvider extends ImageProvider<DelayedImageProvider> {
|
|
final Completer<ImageInfo> _completer = new Completer<ImageInfo>();
|
|
|
|
@override
|
|
Future<DelayedImageProvider> obtainKey(ImageConfiguration configuration) {
|
|
return new SynchronousFuture<DelayedImageProvider>(this);
|
|
}
|
|
|
|
@override
|
|
ImageStream resolve(ImageConfiguration configuration) {
|
|
return super.resolve(configuration);
|
|
}
|
|
|
|
@override
|
|
ImageStreamCompleter load(DelayedImageProvider key) {
|
|
return new OneFrameImageStreamCompleter(_completer.future);
|
|
}
|
|
|
|
void complete() {
|
|
_completer.complete(new ImageInfo(image: new TestImage()));
|
|
}
|
|
|
|
@override
|
|
String toString() => '${describeIdentity(this)}}()';
|
|
}
|
|
|
|
class TestImage implements ui.Image {
|
|
@override
|
|
int get width => 100;
|
|
|
|
@override
|
|
int get height => 100;
|
|
|
|
@override
|
|
void dispose() { }
|
|
|
|
@override
|
|
Future<ByteData> toByteData({ui.ImageByteFormat format}) async {
|
|
throw new UnsupportedError('Cannot encode test image');
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
new TestRenderingFlutterBinding(); // initializes the imageCache
|
|
|
|
test('Decoration.lerp()', () {
|
|
const BoxDecoration a = const BoxDecoration(color: const Color(0xFFFFFFFF));
|
|
const BoxDecoration b = const BoxDecoration(color: const Color(0x00000000));
|
|
|
|
BoxDecoration c = Decoration.lerp(a, b, 0.0);
|
|
expect(c.color, equals(a.color));
|
|
|
|
c = Decoration.lerp(a, b, 0.25);
|
|
expect(c.color, equals(Color.lerp(const Color(0xFFFFFFFF), const Color(0x00000000), 0.25)));
|
|
|
|
c = Decoration.lerp(a, b, 1.0);
|
|
expect(c.color, equals(b.color));
|
|
});
|
|
|
|
test('BoxDecorationImageListenerSync', () {
|
|
final ImageProvider imageProvider = new SynchronousTestImageProvider();
|
|
final DecorationImage backgroundImage = new DecorationImage(image: imageProvider);
|
|
|
|
final BoxDecoration boxDecoration = new BoxDecoration(image: backgroundImage);
|
|
bool onChangedCalled = false;
|
|
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
|
|
onChangedCalled = true;
|
|
});
|
|
|
|
final TestCanvas canvas = new TestCanvas();
|
|
const ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero);
|
|
boxPainter.paint(canvas, Offset.zero, imageConfiguration);
|
|
|
|
// The onChanged callback should not be invoked during the call to boxPainter.paint
|
|
expect(onChangedCalled, equals(false));
|
|
});
|
|
|
|
test('BoxDecorationImageListenerAsync', () {
|
|
new FakeAsync().run((FakeAsync async) {
|
|
final ImageProvider imageProvider = new AsyncTestImageProvider();
|
|
final DecorationImage backgroundImage = new DecorationImage(image: imageProvider);
|
|
|
|
final BoxDecoration boxDecoration = new BoxDecoration(image: backgroundImage);
|
|
bool onChangedCalled = false;
|
|
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
|
|
onChangedCalled = true;
|
|
});
|
|
|
|
final TestCanvas canvas = new TestCanvas();
|
|
const ImageConfiguration imageConfiguration = const ImageConfiguration(size: Size.zero);
|
|
boxPainter.paint(canvas, Offset.zero, imageConfiguration);
|
|
|
|
// The onChanged callback should be invoked asynchronously.
|
|
expect(onChangedCalled, equals(false));
|
|
async.flushMicrotasks();
|
|
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: BoxShape.rectangle, BorderRadius borderRadius, bool expectClip}) {
|
|
assert(shape != null);
|
|
new FakeAsync().run((FakeAsync async) {
|
|
final DelayedImageProvider imageProvider = new DelayedImageProvider();
|
|
final DecorationImage backgroundImage = new DecorationImage(image: imageProvider);
|
|
|
|
final BoxDecoration boxDecoration = new BoxDecoration(
|
|
shape: shape,
|
|
borderRadius: borderRadius,
|
|
image: backgroundImage,
|
|
);
|
|
|
|
final List<Invocation> invocations = <Invocation>[];
|
|
final TestCanvas canvas = new TestCanvas(invocations);
|
|
const ImageConfiguration imageConfiguration = const ImageConfiguration(
|
|
size: const Size(100.0, 100.0)
|
|
);
|
|
bool onChangedCalled = false;
|
|
final BoxPainter boxPainter = boxDecoration.createBoxPainter(() {
|
|
onChangedCalled = true;
|
|
});
|
|
|
|
// _BoxDecorationPainter._paintDecorationImage() 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 precede the drawImageRect call.
|
|
final List<Invocation> commands = canvas.invocations.where((Invocation invocation) {
|
|
return invocation.memberName == #clipPath || invocation.memberName == #drawImageRect;
|
|
}).toList();
|
|
if (expectClip) { // We expect a clip to precede 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: const BorderRadius.all(const Radius.circular(16.0)), expectClip: true);
|
|
testDecoration(expectClip: false);
|
|
});
|
|
|
|
test('DecorationImage test', () {
|
|
const 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: Alignment.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(100.0, 100.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, 100.0, 100.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);
|
|
});
|
|
|
|
test('BoxDecoration.lerp - shapes', () {
|
|
// We don't lerp the shape, we just switch from one to the other at t=0.5.
|
|
// (Use a ShapeDecoration and ShapeBorder if you want to lerp the shapes...)
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
-1.0,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.rectangle)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
0.0,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.rectangle)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
0.25,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.rectangle)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
0.75,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.circle)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
1.0,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.circle)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(shape: BoxShape.rectangle),
|
|
const BoxDecoration(shape: BoxShape.circle),
|
|
2.0,
|
|
),
|
|
const BoxDecoration(shape: BoxShape.circle)
|
|
);
|
|
});
|
|
|
|
test('BoxDecoration.lerp - gradients', () {
|
|
const Gradient gradient = const LinearGradient(colors: const <Color>[ const Color(0x00000000), const Color(0xFFFFFFFF) ]);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
-1.0,
|
|
),
|
|
const BoxDecoration(gradient: const LinearGradient(colors: const <Color>[ const Color(0x00000000), const Color(0x00FFFFFF) ]))
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
0.0,
|
|
),
|
|
const BoxDecoration()
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
0.25,
|
|
),
|
|
const BoxDecoration(gradient: const LinearGradient(colors: const <Color>[ const Color(0x00000000), const Color(0x40FFFFFF) ]))
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
0.75,
|
|
),
|
|
const BoxDecoration(gradient: const LinearGradient(colors: const <Color>[ const Color(0x00000000), const Color(0xBFFFFFFF) ]))
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
1.0,
|
|
),
|
|
const BoxDecoration(gradient: gradient)
|
|
);
|
|
expect(
|
|
BoxDecoration.lerp(
|
|
const BoxDecoration(),
|
|
const BoxDecoration(gradient: gradient),
|
|
2.0,
|
|
),
|
|
const BoxDecoration(gradient: gradient)
|
|
);
|
|
});
|
|
|
|
test('Decoration.lerp with unrelated decorations', () {
|
|
expect(Decoration.lerp(const FlutterLogoDecoration(), const BoxDecoration(), 0.0), const isInstanceOf<FlutterLogoDecoration>()); // ignore: CONST_EVAL_THROWS_EXCEPTION
|
|
expect(Decoration.lerp(const FlutterLogoDecoration(), const BoxDecoration(), 0.25), const isInstanceOf<FlutterLogoDecoration>()); // ignore: CONST_EVAL_THROWS_EXCEPTION
|
|
expect(Decoration.lerp(const FlutterLogoDecoration(), const BoxDecoration(), 0.75), const isInstanceOf<BoxDecoration>()); // ignore: CONST_EVAL_THROWS_EXCEPTION
|
|
expect(Decoration.lerp(const FlutterLogoDecoration(), const BoxDecoration(), 1.0), const isInstanceOf<BoxDecoration>()); // ignore: CONST_EVAL_THROWS_EXCEPTION
|
|
});
|
|
}
|