Improve thumbnail generation logic
This commit is contained in:
parent
bed4afc541
commit
6c768da91a
6 changed files with 80 additions and 43 deletions
|
@ -1,2 +1,4 @@
|
|||
const String ENDPOINT = "http://192.168.0.255:8080";
|
||||
const String USER = "umbu"; // TODO: Fix me
|
||||
const int THUMBNAIL_SMALL_SIZE = 128;
|
||||
const int THUMBNAIL_LARGE_SIZE = 512;
|
||||
|
|
|
@ -4,13 +4,31 @@ import 'package:myapp/core/lru_map.dart';
|
|||
import 'package:myapp/models/photo.dart';
|
||||
|
||||
class ThumbnailLruCache {
|
||||
static LRUMap<int, Uint8List> _map = LRUMap(500);
|
||||
static LRUMap<_ThumbnailCacheKey, Uint8List> _map = LRUMap(5000);
|
||||
|
||||
static Uint8List get(Photo photo) {
|
||||
return _map.get(photo.generatedId);
|
||||
static Uint8List get(Photo photo, int size) {
|
||||
return _map.get(_ThumbnailCacheKey(photo, size));
|
||||
}
|
||||
|
||||
static void put(Photo photo, Uint8List imageData) {
|
||||
_map.put(photo.generatedId, imageData);
|
||||
static void put(Photo photo, int size, Uint8List imageData) {
|
||||
_map.put(_ThumbnailCacheKey(photo, size), imageData);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThumbnailCacheKey {
|
||||
Photo photo;
|
||||
int size;
|
||||
|
||||
_ThumbnailCacheKey(this.photo, this.size);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is _ThumbnailCacheKey &&
|
||||
runtimeType == other.runtimeType &&
|
||||
photo.generatedId == other.photo.generatedId &&
|
||||
size == other.size;
|
||||
|
||||
@override
|
||||
int get hashCode => photo.hashCode * size.hashCode;
|
||||
}
|
||||
|
|
|
@ -32,8 +32,12 @@ class Photo {
|
|||
return photo;
|
||||
}
|
||||
|
||||
AssetEntity getAsset() {
|
||||
return AssetEntity(id: localId);
|
||||
}
|
||||
|
||||
Future<Uint8List> getBytes({int quality = 100}) {
|
||||
final asset = AssetEntity(id: localId);
|
||||
final asset = getAsset();
|
||||
if (extension(title) == ".HEIC" || quality != 100) {
|
||||
return asset.originBytes.then((bytes) =>
|
||||
FlutterImageCompress.compressWithList(bytes, quality: quality)
|
||||
|
|
|
@ -59,7 +59,7 @@ class _AlbumListWidgetState extends State<AlbumListWidget> {
|
|||
return GestureDetector(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
ThumbnailWidget(album.photos[0], size: 140),
|
||||
ThumbnailWidget(album.photos[0]),
|
||||
Padding(padding: EdgeInsets.all(2)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/thumbnail_cache.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:photo_manager/photo_manager.dart';
|
||||
import 'package:myapp/core/constants.dart';
|
||||
|
||||
class ThumbnailWidget extends StatefulWidget {
|
||||
final Photo photo;
|
||||
final int size;
|
||||
|
||||
const ThumbnailWidget(
|
||||
this.photo, {
|
||||
Key key,
|
||||
this.size,
|
||||
}) : super(key: key);
|
||||
@override
|
||||
_ThumbnailWidgetState createState() => _ThumbnailWidgetState();
|
||||
|
@ -24,41 +21,54 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
|
|||
color: Colors.grey[500],
|
||||
);
|
||||
|
||||
bool _loadedSmallThumbnail = false;
|
||||
bool _loadedLargeThumbnail = false;
|
||||
ImageProvider _imageProvider;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final size = widget.size == null ? 512 : widget.size;
|
||||
final cachedImageData = ThumbnailLruCache.get(widget.photo);
|
||||
|
||||
Widget image;
|
||||
|
||||
if (cachedImageData != null) {
|
||||
image = _buildImage(cachedImageData, size);
|
||||
} else {
|
||||
if (widget.photo.localId != null) {
|
||||
image = FutureBuilder<Uint8List>(
|
||||
future: AssetEntity(id: widget.photo.localId)
|
||||
.thumbDataWithSize(size, size),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
ThumbnailLruCache.put(widget.photo, snapshot.data);
|
||||
Image image = _buildImage(snapshot.data, size);
|
||||
return image;
|
||||
} else {
|
||||
return loadingWidget;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (!_loadedSmallThumbnail && !_loadedLargeThumbnail) {
|
||||
final cachedSmallThumbnail =
|
||||
ThumbnailLruCache.get(widget.photo, THUMBNAIL_SMALL_SIZE);
|
||||
if (cachedSmallThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedSmallThumbnail).image;
|
||||
_loadedSmallThumbnail = true;
|
||||
} else {
|
||||
// TODO
|
||||
return Text("Not Implemented");
|
||||
if (mounted) {
|
||||
widget.photo
|
||||
.getAsset()
|
||||
.thumbDataWithSize(THUMBNAIL_SMALL_SIZE, THUMBNAIL_SMALL_SIZE)
|
||||
.then((data) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
if (data != null) {
|
||||
_imageProvider = Image.memory(data).image;
|
||||
}
|
||||
_loadedSmallThumbnail = true;
|
||||
});
|
||||
}
|
||||
ThumbnailLruCache.put(widget.photo, THUMBNAIL_SMALL_SIZE, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
}
|
||||
if (!_loadedLargeThumbnail) {
|
||||
if (ThumbnailLruCache.get(widget.photo, THUMBNAIL_LARGE_SIZE) == null) {
|
||||
widget.photo
|
||||
.getAsset()
|
||||
.thumbDataWithSize(THUMBNAIL_LARGE_SIZE, THUMBNAIL_LARGE_SIZE)
|
||||
.then((data) {
|
||||
ThumbnailLruCache.put(widget.photo, THUMBNAIL_LARGE_SIZE, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Image _buildImage(Uint8List data, int size) {
|
||||
return Image.memory(data,
|
||||
width: size.toDouble(), height: size.toDouble(), fit: BoxFit.cover);
|
||||
if (_imageProvider != null) {
|
||||
return Image(
|
||||
image: _imageProvider, gaplessPlayback: true, fit: BoxFit.cover);
|
||||
} else {
|
||||
return loadingWidget;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ import 'dart:typed_data';
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:logger/logger.dart';
|
||||
import 'package:myapp/core/image_cache.dart';
|
||||
import 'package:myapp/core/lru_map.dart';
|
||||
import 'package:myapp/core/thumbnail_cache.dart';
|
||||
import 'package:myapp/models/photo.dart';
|
||||
import 'package:myapp/ui/loading_widget.dart';
|
||||
import 'package:photo_view/photo_view.dart';
|
||||
import 'package:myapp/core/constants.dart';
|
||||
|
||||
class ZoomableImage extends StatefulWidget {
|
||||
final Photo photo;
|
||||
|
@ -41,10 +41,13 @@ class _ZoomableImageState extends State<ZoomableImage> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!_loadedThumbnail && !_loadedFinalImage) {
|
||||
final cachedThumbnail = ThumbnailLruCache.get(widget.photo);
|
||||
final cachedThumbnail =
|
||||
ThumbnailLruCache.get(widget.photo, THUMBNAIL_LARGE_SIZE);
|
||||
if (cachedThumbnail != null) {
|
||||
_imageProvider = Image.memory(cachedThumbnail).image;
|
||||
_loadedThumbnail = true;
|
||||
} else {
|
||||
Logger().i("Thumbnail missing for " + widget.photo.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue