From 9643242cc80d906534a4658ab3a7207c5907274d Mon Sep 17 00:00:00 2001 From: krille-chan Date: Sat, 12 Jul 2025 15:38:04 +0200 Subject: [PATCH] refactor: Make notification avatars rounded --- .../client_download_content_extension.dart | 51 +++++++++++++++++-- .../local_notifications_extension.dart | 4 +- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/lib/utils/client_download_content_extension.dart b/lib/utils/client_download_content_extension.dart index 65e7e023c..3da8a1c51 100644 --- a/lib/utils/client_download_content_extension.dart +++ b/lib/utils/client_download_content_extension.dart @@ -1,6 +1,7 @@ +import 'dart:math' show min; import 'dart:typed_data'; +import 'dart:ui'; -import 'package:image/image.dart'; import 'package:matrix/matrix.dart'; extension ClientDownloadContentExtension on Client { @@ -49,10 +50,10 @@ extension ClientDownloadContentExtension on Client { var imageData = response.bodyBytes; if (rounded) { - final image = decodeImage(imageData); - if (image != null) { - imageData = encodePng(copyCropCircle(image)); - } + imageData = await _convertToCircularImage( + imageData, + min(width ?? 64, height ?? 64).round(), + ); } await database.storeFile(cacheKey, imageData, 0); @@ -60,3 +61,43 @@ extension ClientDownloadContentExtension on Client { return imageData; } } + +Future _convertToCircularImage( + Uint8List imageBytes, + int size, +) async { + final codec = await instantiateImageCodec(imageBytes); + final frame = await codec.getNextFrame(); + final originalImage = frame.image; + + final recorder = PictureRecorder(); + final canvas = Canvas(recorder); + + final paint = Paint(); + final rect = Rect.fromLTWH(0, 0, size.toDouble(), size.toDouble()); + + final clipPath = Path() + ..addOval( + Rect.fromCircle(center: Offset(size / 2, size / 2), radius: size / 2), + ); + + canvas.clipPath(clipPath); + + canvas.drawImageRect( + originalImage, + Rect.fromLTWH( + 0, + 0, + originalImage.width.toDouble(), + originalImage.height.toDouble(), + ), + rect, + paint, + ); + + final picture = recorder.endRecording(); + final circularImage = await picture.toImage(size, size); + + final byteData = await circularImage.toByteData(format: ImageByteFormat.png); + return byteData!.buffer.asUint8List(); +} diff --git a/lib/widgets/local_notifications_extension.dart b/lib/widgets/local_notifications_extension.dart index ceee364f5..0cdc291ef 100644 --- a/lib/widgets/local_notifications_extension.dart +++ b/lib/widgets/local_notifications_extension.dart @@ -50,7 +50,7 @@ extension LocalNotificationsExtension on MatrixState { Uri? thumbnailUri; if (avatarUrl != null) { - const size = 64; + const size = 128; const thumbnailMethod = ThumbnailMethod.crop; // Pre-cache so that we can later just set the thumbnail uri as icon: await client.downloadMxcCached( @@ -59,6 +59,7 @@ extension LocalNotificationsExtension on MatrixState { height: size, thumbnailMethod: thumbnailMethod, isThumbnail: true, + rounded: true, ); thumbnailUri = @@ -92,6 +93,7 @@ extension LocalNotificationsExtension on MatrixState { height: size, thumbnailMethod: thumbnailMethod, isThumbnail: true, + rounded: true, ); final image = decodeImage(data);