Skip to content

Commit

Permalink
♻️ Make file type of the image provider asynchronized
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexV525 committed Oct 8, 2024
1 parent 4c00c16 commit c6df0fd
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 55 deletions.
4 changes: 4 additions & 0 deletions packages/photo_manager_image_provider/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.2.0

- **FEAT**: Make file type of the image provider asynchronized.

## 2.1.2

- **FEAT**: Integrate progress handler into the image provider.
Expand Down
3 changes: 3 additions & 0 deletions packages/photo_manager_image_provider/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ include: package:flutter_lints/flutter.yaml

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
linter:
rules:
require_trailing_commas: true
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import 'package:flutter/widgets.dart';

import 'package:photo_manager/photo_manager.dart';

final _providerLocks = <AssetEntityImageProvider, Completer<ui.Codec>>{};
final _lockDecode = <AssetEntityImageProvider, Completer<ui.Codec>>{};
final _lockImageTypes = <AssetEntityImageProvider, Completer<ImageFileType>>{};

const ThumbnailSize pmDefaultGridThumbnailSize = ThumbnailSize.square(200);
const String _libraryName = 'photo_manager';
Expand Down Expand Up @@ -66,11 +67,18 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {

/// File type for the image asset, use it for some special type detection.
/// 图片资源的类型,用于某些特殊类型的判断。
ImageFileType get imageFileType => _getType();
Future<ImageFileType> get imageFileType async {
if (_lockImageTypes[this] != null) {
return _lockImageTypes[this]!.future;
}
return _getType();
}

@override
ImageStreamCompleter loadImage(
AssetEntityImageProvider key, ImageDecoderCallback decode) {
AssetEntityImageProvider key,
ImageDecoderCallback decode,
) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: 1.0,
Expand Down Expand Up @@ -104,11 +112,11 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {
AssetEntityImageProvider key,
ImageDecoderCallback decode, // ignore: deprecated_member_use
) {
if (_providerLocks.containsKey(key)) {
return _providerLocks[key]!.future;
if (_lockDecode.containsKey(key)) {
return _lockDecode[key]!.future;
}
final lock = Completer<ui.Codec>();
_providerLocks[key] = lock;
_lockDecode[key] = lock;
Future(() async {
try {
assert(key == this);
Expand All @@ -118,15 +126,9 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {
'Image data for the ${key.entity.type} is not supported.',
);
}

// Define the image type.
final ImageFileType type;
if (key.imageFileType == ImageFileType.other) {
// Assume the title is invalid here, try again with the async getter.
type = _getType(await key.entity.titleAsync);
} else {
type = key.imageFileType;
}
final imageType =
await _lockImageTypes[key]?.future ?? await key.imageFileType;
_lockImageTypes[key] ??= Completer.sync()..complete(imageType);

typed_data.Uint8List? data;
if (isOriginal) {
Expand All @@ -137,7 +139,7 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {
);
} else {
final file = await key.entity.loadFile(
isOrigin: type != ImageFileType.heic,
isOrigin: imageType != ImageFileType.heic,
progressHandler: progressHandler,
);
data = await file?.readAsBytes();
Expand Down Expand Up @@ -169,12 +171,8 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {
Future<void>.microtask(() => _evictCache(key));
rethrow;
}
}).then((codec) {
lock.complete(codec);
}).catchError((e, s) {
lock.completeError(e, s);
}).whenComplete(() {
_providerLocks.remove(key);
}).then(lock.complete).catchError(lock.completeError).whenComplete(() {
_lockDecode.remove(key);
});
return lock.future;
}
Expand All @@ -188,39 +186,53 @@ class AssetEntityImageProvider extends ImageProvider<AssetEntityImageProvider> {

/// Get image type by reading the file extension.
/// 从图片后缀判断图片类型
///
/// ⚠ Not all the system version support read file name from the entity,
/// so this method might not work sometime.
/// 并非所有的系统版本都支持读取文件名,所以该方法有时无法返回正确的类型。
ImageFileType _getType([String? filename]) {
ImageFileType? type;
final String? extension = filename?.split('.').last ??
entity.mimeType?.split('/').last ??
entity.title?.split('.').last;
if (extension != null) {
switch (extension.toLowerCase()) {
case 'jpg':
case 'jpeg':
type = ImageFileType.jpg;
break;
case 'png':
type = ImageFileType.png;
break;
case 'gif':
type = ImageFileType.gif;
break;
case 'tiff':
type = ImageFileType.tiff;
break;
case 'heic':
type = ImageFileType.heic;
break;
default:
type = ImageFileType.other;
break;
}
Future<ImageFileType> _getType([String? filename]) async {
if (_lockImageTypes[this] != null) {
return _lockImageTypes[this]!.future;
}
return type ?? ImageFileType.other;
final lock = Completer<ImageFileType>();
_lockImageTypes[this] = lock;
Future(() async {
String? extension = filename?.split('.').last;
if (extension == null || extension.trim().isEmpty) {
extension = entity.mimeType?.split('.').last;
}
if (extension == null || extension.trim().isEmpty) {
extension = entity.title?.split('.').last;
}
if (extension == null || extension.trim().isEmpty) {
extension = (await entity.titleAsync).split('.').last;
}
if (extension.trim().isEmpty) {
extension = null;
}
ImageFileType? type;
if (extension != null) {
switch (extension.toLowerCase()) {
case 'jpg':
case 'jpeg':
type = ImageFileType.jpg;
break;
case 'png':
type = ImageFileType.png;
break;
case 'gif':
type = ImageFileType.gif;
break;
case 'tiff':
type = ImageFileType.tiff;
break;
case 'heic':
type = ImageFileType.heic;
break;
default:
type = ImageFileType.other;
break;
}
}
return type ?? ImageFileType.other;
}).then(lock.complete).catchError(lock.completeError);
return lock.future;
}

static void _evictCache(AssetEntityImageProvider key) {
Expand Down
2 changes: 1 addition & 1 deletion packages/photo_manager_image_provider/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: photo_manager_image_provider
description: A Flutter plugin, provide image_provider for photo_manager's AssetEntity.
version: 2.1.2
version: 2.2.0
homepage: https://github.com/fluttercandies/flutter_photo_manager_plugins/tree/main/packages/photo_manager_image_provider

environment:
Expand Down

0 comments on commit c6df0fd

Please sign in to comment.