thumbhash improvements

This commit is contained in:
mertalev
2025-07-08 16:17:03 +03:00
parent df4a27e8a7
commit 0dadfc52dd
8 changed files with 80 additions and 165 deletions
@@ -1,50 +0,0 @@
import 'dart:convert' hide Codec;
import 'dart:ui';
import 'package:flutter/foundation.dart';
import 'package:flutter/rendering.dart';
import 'package:thumbhash/thumbhash.dart';
class ThumbHashProvider extends ImageProvider<ThumbHashProvider> {
final String thumbHash;
const ThumbHashProvider({
required this.thumbHash,
});
@override
Future<ThumbHashProvider> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture(this);
}
@override
ImageStreamCompleter loadImage(
ThumbHashProvider key,
ImageDecoderCallback decode,
) {
return MultiFrameImageStreamCompleter(
codec: _loadCodec(key, decode),
scale: 1.0,
);
}
Future<Codec> _loadCodec(
ThumbHashProvider key,
ImageDecoderCallback decode,
) async {
final image = thumbHashToRGBA(base64Decode(key.thumbHash));
return decode(await ImmutableBuffer.fromUint8List(rgbaToBmp(image)));
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other is ThumbHashProvider) {
return thumbHash == other.thumbHash;
}
return false;
}
@override
int get hashCode => thumbHash.hashCode;
}
@@ -1,9 +1,8 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/presentation/widgets/images/thumb_hash_provider.dart';
import 'package:immich_mobile/widgets/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/widgets/common/fade_in_placeholder_image.dart';
import 'package:immich_mobile/widgets/common/thumbhash.dart';
import 'package:logging/logging.dart';
import 'package:octo_image/octo_image.dart';
@@ -58,11 +57,7 @@ OctoPlaceholderBuilder _blurHashPlaceholderBuilder(
}) {
return (context) => thumbHash == null
? const ThumbnailPlaceholder()
: FadeInPlaceholderImage(
placeholder: const ThumbnailPlaceholder(),
image: ThumbHashProvider(thumbHash: thumbHash),
fit: fit ?? BoxFit.cover,
);
: Thumbhash(blurhash: thumbHash, fit: fit ?? BoxFit.cover);
}
OctoErrorBuilder _blurHashErrorBuilder(
@@ -6,7 +6,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/presentation/widgets/images/full_image.widget.dart';
import 'package:immich_mobile/presentation/widgets/images/image_provider.dart';
import 'package:immich_mobile/utils/hooks/blurhash_hook.dart';
import 'package:immich_mobile/widgets/common/thumbhash.dart';
class DriftMemoryCard extends StatelessWidget {
final RemoteAsset asset;
@@ -117,43 +117,34 @@ class _BlurredBackdrop extends HookWidget {
@override
Widget build(BuildContext context) {
final blurhash = useDriftBlurHashRef(asset).value;
final blurhash = asset.thumbHash;
if (blurhash != null) {
// Use a nice cheap blur hash image decoration
return Container(
return Stack(
children: [
const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
Thumbhash(blurhash: blurhash, fit: BoxFit.cover),
],
);
}
// Fall back to using a more expensive image filtered
// Since the ImmichImage is already precached, we can
// safely use that as the image provider
return ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: DecoratedBox(
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(
blurhash,
image: getFullImageProvider(
asset,
size: Size(context.width, context.height),
),
fit: BoxFit.cover,
),
),
child: Container(
color: Colors.black.withValues(alpha: 0.2),
),
);
} else {
// Fall back to using a more expensive image filtered
// Since the ImmichImage is already precached, we can
// safely use that as the image provider
return ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
image: getFullImageProvider(
asset,
size: Size(context.width, context.height),
),
fit: BoxFit.cover,
),
),
child: Container(
color: Colors.black.withValues(alpha: 0.2),
),
),
);
}
child: const ColoredBox(color: Color.fromRGBO(0, 0, 0, 0.2)),
),
);
}
}