reviewed thumbnail requests & cached thumbnail usage

This commit is contained in:
Thibault Deckers 2021-06-07 16:11:54 +09:00
parent b332739eac
commit a81e934fbe
8 changed files with 64 additions and 49 deletions

View file

@ -4,7 +4,16 @@ import 'package:aves/image_providers/thumbnail_provider.dart';
import 'package:aves/image_providers/uri_image_provider.dart'; import 'package:aves/image_providers/uri_image_provider.dart';
class EntryCache { class EntryCache {
static final requestExtents = <double>{}; // ordered descending
static final thumbnailRequestExtents = <double>[];
static void markThumbnailExtent(double extent) {
if (!thumbnailRequestExtents.contains(extent)) {
thumbnailRequestExtents
..add(extent)
..sort((a, b) => b.compareTo(a));
}
}
static Future<void> evict( static Future<void> evict(
String uri, String uri,
@ -36,7 +45,7 @@ class EntryCache {
)).evict(); )).evict();
await Future.forEach<double>( await Future.forEach<double>(
requestExtents, thumbnailRequestExtents,
(extent) => ThumbnailProvider(ThumbnailProviderKey( (extent) => ThumbnailProvider(ThumbnailProviderKey(
uri: uri, uri: uri,
mimeType: mimeType, mimeType: mimeType,

View file

@ -6,6 +6,7 @@ import 'package:aves/image_providers/thumbnail_provider.dart';
import 'package:aves/image_providers/uri_image_provider.dart'; import 'package:aves/image_providers/uri_image_provider.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_cache.dart'; import 'package:aves/model/entry_cache.dart';
import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
extension ExtraAvesEntry on AvesEntry { extension ExtraAvesEntry on AvesEntry {
@ -14,7 +15,7 @@ extension ExtraAvesEntry on AvesEntry {
} }
ThumbnailProviderKey _getThumbnailProviderKey(double extent) { ThumbnailProviderKey _getThumbnailProviderKey(double extent) {
EntryCache.requestExtents.add(extent); EntryCache.markThumbnailExtent(extent);
return ThumbnailProviderKey( return ThumbnailProviderKey(
uri: uri, uri: uri,
mimeType: mimeType, mimeType: mimeType,
@ -54,10 +55,10 @@ extension ExtraAvesEntry on AvesEntry {
bool _isReady(Object providerKey) => imageCache!.statusForKey(providerKey).keepAlive; bool _isReady(Object providerKey) => imageCache!.statusForKey(providerKey).keepAlive;
ImageProvider getBestThumbnail(double extent) { List<ThumbnailProvider> get cachedThumbnails => EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).where(_isReady).map((key) => ThumbnailProvider(key)).toList();
final sizedThumbnailKey = _getThumbnailProviderKey(extent);
if (_isReady(sizedThumbnailKey)) return ThumbnailProvider(sizedThumbnailKey);
return getThumbnail(); ThumbnailProvider get bestCachedThumbnail {
final sizedThumbnailKey = EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).firstWhereOrNull(_isReady);
return sizedThumbnailKey != null ? ThumbnailProvider(sizedThumbnailKey) : getThumbnail();
} }
} }

View file

@ -204,11 +204,15 @@ class _CollectionScaler extends StatelessWidget {
), ),
scaledBuilder: (entry, extent) => ThumbnailTheme( scaledBuilder: (entry, extent) => ThumbnailTheme(
extent: extent, extent: extent,
child: DecoratedThumbnail( child: Container(
entry: entry, width: extent,
tileExtent: extent, height: extent,
selectable: false, child: DecoratedThumbnail(
highlightable: false, entry: entry,
tileExtent: context.read<TileExtentController>().effectiveExtentMax,
selectable: false,
highlightable: false,
),
), ),
), ),
getScaledItemTileRect: (context, entry) { getScaledItemTileRect: (context, entry) {

View file

@ -13,6 +13,7 @@ import 'package:flutter/material.dart';
class RasterImageThumbnail extends StatefulWidget { class RasterImageThumbnail extends StatefulWidget {
final AvesEntry entry; final AvesEntry entry;
final double extent; final double extent;
final BoxFit fit;
final ValueNotifier<bool>? cancellableNotifier; final ValueNotifier<bool>? cancellableNotifier;
final Object? heroTag; final Object? heroTag;
@ -20,6 +21,7 @@ class RasterImageThumbnail extends StatefulWidget {
Key? key, Key? key,
required this.entry, required this.entry,
required this.extent, required this.extent,
this.fit = BoxFit.cover,
this.cancellableNotifier, this.cancellableNotifier,
this.heroTag, this.heroTag,
}) : super(key: key); }) : super(key: key);
@ -182,7 +184,7 @@ class _RasterImageThumbnailState extends State<RasterImageThumbnail> {
width: extent, width: extent,
height: extent, height: extent,
scale: imageInfo.scale, scale: imageInfo.scale,
fit: BoxFit.cover, fit: widget.fit,
); );
return widget.heroTag != null return widget.heroTag != null
@ -190,7 +192,7 @@ class _RasterImageThumbnailState extends State<RasterImageThumbnail> {
tag: widget.heroTag!, tag: widget.heroTag!,
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) { flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
return TransitionImage( return TransitionImage(
image: entry.getBestThumbnail(extent), image: entry.bestCachedThumbnail,
animation: animation, animation: animation,
); );
}, },

View file

@ -117,37 +117,36 @@ class ViewerDebugPage extends StatelessWidget {
} }
Widget _buildThumbnailsTabView() { Widget _buildThumbnailsTabView() {
const extent = 128.0; final children = <Widget>[];
if (entry.isSvg) {
const extent = 128.0;
children.addAll([
Text('SVG ($extent)'),
SvgPicture(
UriPicture(
uri: entry.uri,
mimeType: entry.mimeType,
),
width: extent,
height: extent,
)
]);
} else {
children.addAll(
entry.cachedThumbnails.expand((provider) => [
Text('Raster (${provider.key.extent})'),
Center(
child: Image(
image: provider,
),
),
SizedBox(height: 16),
]),
);
}
return ListView( return ListView(
padding: EdgeInsets.all(16), padding: EdgeInsets.all(16),
children: [ children: children,
if (entry.isSvg) ...[
Text('SVG ($extent)'),
SvgPicture(
UriPicture(
uri: entry.uri,
mimeType: entry.mimeType,
),
width: extent,
height: extent,
)
],
if (!entry.isSvg) ...[
Text('Raster (fast)'),
Center(
child: Image(
image: entry.getThumbnail(),
),
),
SizedBox(height: 16),
Text('Raster ($extent)'),
Center(
child: Image(
image: entry.getThumbnail(extent: extent),
),
),
],
],
); );
} }
} }

View file

@ -16,7 +16,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart';
import 'package:aves/widgets/dialogs/rename_entry_dialog.dart'; import 'package:aves/widgets/dialogs/rename_entry_dialog.dart';
import 'package:aves/widgets/filter_grids/album_pick.dart'; import 'package:aves/widgets/filter_grids/album_pick.dart';
import 'package:aves/widgets/viewer/debug_page.dart'; import 'package:aves/widgets/viewer/debug/debug_page.dart';
import 'package:aves/widgets/viewer/info/notifications.dart'; import 'package:aves/widgets/viewer/info/notifications.dart';
import 'package:aves/widgets/viewer/printer.dart'; import 'package:aves/widgets/viewer/printer.dart';
import 'package:aves/widgets/viewer/source_viewer_page.dart'; import 'package:aves/widgets/viewer/source_viewer_page.dart';

View file

@ -2,12 +2,11 @@ import 'dart:async';
import 'package:aves/image_providers/uri_picture_provider.dart'; import 'package:aves/image_providers/uri_picture_provider.dart';
import 'package:aves/model/entry.dart'; import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_images.dart';
import 'package:aves/model/settings/entry_background.dart'; import 'package:aves/model/settings/entry_background.dart';
import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/enums.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/thumbnail/raster.dart';
import 'package:aves/widgets/common/magnifier/controller/controller.dart'; import 'package:aves/widgets/common/magnifier/controller/controller.dart';
import 'package:aves/widgets/common/magnifier/controller/state.dart'; import 'package:aves/widgets/common/magnifier/controller/state.dart';
import 'package:aves/widgets/common/magnifier/magnifier.dart'; import 'package:aves/widgets/common/magnifier/magnifier.dart';
@ -213,8 +212,9 @@ class _EntryPageViewState extends State<EntryPageView> {
duration: Durations.viewerVideoPlayerTransition, duration: Durations.viewerVideoPlayerTransition,
child: GestureDetector( child: GestureDetector(
onTap: _onTap, onTap: _onTap,
child: Image( child: RasterImageThumbnail(
image: entry.getBestThumbnail(settings.getTileExtent(CollectionPage.routeName)), entry: entry,
extent: context.select<MediaQueryData, double>((mq) => mq.size.shortestSide),
fit: BoxFit.contain, fit: BoxFit.contain,
), ),
), ),

View file

@ -48,7 +48,7 @@ class _RasterImageViewState extends State<RasterImageView> {
ViewState get viewState => viewStateNotifier.value; ViewState get viewState => viewStateNotifier.value;
ImageProvider get thumbnailProvider => entry.getBestThumbnail(settings.getTileExtent(CollectionPage.routeName)); ImageProvider get thumbnailProvider => entry.bestCachedThumbnail;
ImageProvider get fullImageProvider { ImageProvider get fullImageProvider {
if (entry.useTiles) { if (entry.useTiles) {