Skip to content

Commit

Permalink
#1388 info: generate palette in another isolate
Browse files Browse the repository at this point in the history
  • Loading branch information
deckerst committed Jan 21, 2025
1 parent b282d0a commit 43b8326
Showing 1 changed file with 45 additions and 11 deletions.
56 changes: 45 additions & 11 deletions lib/widgets/viewer/info/color_section.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'dart:async';
import 'dart:isolate';
import 'dart:math';
import 'dart:ui' as ui;

import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/images.dart';
Expand All @@ -24,27 +26,22 @@ class ColorSectionSliver extends StatefulWidget {
}

class _ColorSectionSliverState extends State<ColorSectionSliver> {
late final Future<PaletteGenerator> _paletteLoader;
late final Future<List<Color>> _paletteLoader;

@override
void initState() {
super.initState();
final provider = widget.entry.getThumbnail(extent: min(200, widget.entry.displaySize.longestSide));
_paletteLoader = PaletteGenerator.fromImageProvider(
provider,
maximumColorCount: 10,
// do not use the default palette filter
filters: [],
);
_paletteLoader = _loadPalette(provider);
}

@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: FutureBuilder<PaletteGenerator>(
child: FutureBuilder<List<Color>>(
future: _paletteLoader,
builder: (context, snapshot) {
final colors = snapshot.data?.paletteColors;
final colors = snapshot.data;
if (colors == null || colors.isEmpty) return const SizedBox();

final durations = context.watch<DurationsData>();
Expand All @@ -67,12 +64,12 @@ class _ColorSectionSliverState extends State<ColorSectionSliver> {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
ColorIndicator(value: v.color),
ColorIndicator(value: v),
const SizedBox(width: 8),
Directionality(
textDirection: TextDirection.ltr,
child: SelectableText(
'#${v.color.hex}',
'#${v.hex}',
style: const TextStyle(fontFamily: 'monospace'),
),
),
Expand All @@ -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: [],
));
}
}

0 comments on commit 43b8326

Please sign in to comment.