reviewed thumbnail requests & cached thumbnail usage
This commit is contained in:
parent
b332739eac
commit
a81e934fbe
8 changed files with 64 additions and 49 deletions
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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';
|
||||||
|
|
|
@ -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,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in a new issue