#1388 info: generate palette in another isolate
This commit is contained in:
parent
b282d0a250
commit
43b832680c
1 changed files with 45 additions and 11 deletions
|
@ -1,5 +1,7 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:isolate';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/entry/extensions/images.dart';
|
import 'package:aves/model/entry/extensions/images.dart';
|
||||||
|
@ -24,27 +26,22 @@ class ColorSectionSliver extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ColorSectionSliverState extends State<ColorSectionSliver> {
|
class _ColorSectionSliverState extends State<ColorSectionSliver> {
|
||||||
late final Future<PaletteGenerator> _paletteLoader;
|
late final Future<List<Color>> _paletteLoader;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final provider = widget.entry.getThumbnail(extent: min(200, widget.entry.displaySize.longestSide));
|
final provider = widget.entry.getThumbnail(extent: min(200, widget.entry.displaySize.longestSide));
|
||||||
_paletteLoader = PaletteGenerator.fromImageProvider(
|
_paletteLoader = _loadPalette(provider);
|
||||||
provider,
|
|
||||||
maximumColorCount: 10,
|
|
||||||
// do not use the default palette filter
|
|
||||||
filters: [],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: FutureBuilder<PaletteGenerator>(
|
child: FutureBuilder<List<Color>>(
|
||||||
future: _paletteLoader,
|
future: _paletteLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
final colors = snapshot.data?.paletteColors;
|
final colors = snapshot.data;
|
||||||
if (colors == null || colors.isEmpty) return const SizedBox();
|
if (colors == null || colors.isEmpty) return const SizedBox();
|
||||||
|
|
||||||
final durations = context.watch<DurationsData>();
|
final durations = context.watch<DurationsData>();
|
||||||
|
@ -67,12 +64,12 @@ class _ColorSectionSliverState extends State<ColorSectionSliver> {
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ColorIndicator(value: v.color),
|
ColorIndicator(value: v),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Directionality(
|
Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: TextDirection.ltr,
|
||||||
child: SelectableText(
|
child: SelectableText(
|
||||||
'#${v.color.hex}',
|
'#${v.hex}',
|
||||||
style: const TextStyle(fontFamily: 'monospace'),
|
style: const TextStyle(fontFamily: 'monospace'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -87,4 +84,41 @@ class _ColorSectionSliverState extends State<ColorSectionSliver> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `PaletteGenerator.fromImage()` directly blocks the main isolate,
|
||||||
|
// so we use another isolate to compute the palette
|
||||||
|
Future<List<Color>> _loadPalette(ImageProvider provider) async {
|
||||||
|
final stream = provider.resolve(ImageConfiguration.empty);
|
||||||
|
final imageCompleter = Completer<ui.Image>();
|
||||||
|
late ImageStreamListener listener;
|
||||||
|
listener = ImageStreamListener((info, _) {
|
||||||
|
stream.removeListener(listener);
|
||||||
|
imageCompleter.complete(info.image);
|
||||||
|
});
|
||||||
|
stream.addListener(listener);
|
||||||
|
final image = await imageCompleter.future;
|
||||||
|
final imageData = await image.toByteData();
|
||||||
|
if (imageData == null) {
|
||||||
|
throw StateError('Failed to encode the image.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final encodedImage = EncodedImage(
|
||||||
|
imageData,
|
||||||
|
width: image.width,
|
||||||
|
height: image.height,
|
||||||
|
);
|
||||||
|
final generator = await _getPaletteGenerator(encodedImage);
|
||||||
|
return generator.paletteColors.map((v) => v.color).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// the isolate does not start unless called from a static method
|
||||||
|
static Future<PaletteGenerator> _getPaletteGenerator(EncodedImage encodedImage) {
|
||||||
|
// `Isolate.run()` closure supports passing `EncodedImage` but not `ui.Image`
|
||||||
|
return Isolate.run(() => PaletteGenerator.fromByteData(
|
||||||
|
encodedImage,
|
||||||
|
maximumColorCount: 10,
|
||||||
|
// do not use the default palette filter
|
||||||
|
filters: [],
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue