diff --git a/lib/src/utils/matrix_file.dart b/lib/src/utils/matrix_file.dart index 356225a0..7f360918 100644 --- a/lib/src/utils/matrix_file.dart +++ b/lib/src/utils/matrix_file.dart @@ -28,9 +28,9 @@ import 'package:mime/mime.dart'; import '../../matrix.dart'; class MatrixFile { - Uint8List bytes; - String name; - String mimeType; + final Uint8List bytes; + final String name; + final String mimeType; /// Encrypts this file and returns the /// encryption information as an [EncryptedFile]. @@ -66,14 +66,36 @@ class MatrixFile { } class MatrixImageFile extends MatrixFile { - Image? _image; - MatrixImageFile({ required Uint8List bytes, required String name, String? mimeType, + this.width, + this.height, + this.blurhash, }) : super(bytes: bytes, name: name, mimeType: mimeType); + /// Creates a new image file and calculates the width, height and blurhash. + static Future create( + {required Uint8List bytes, + required String name, + String? mimeType, + Future Function(FutureOr Function(U arg) function, U arg)? + compute}) async { + final metaData = compute != null + ? await compute(_calcMetadata, bytes) + : _calcMetadata(bytes); + + return MatrixImageFile( + bytes: metaData?.bytes ?? bytes, + name: name, + mimeType: mimeType, + width: metaData?.width, + height: metaData?.height, + blurhash: metaData?.blurhash, + ); + } + /// builds a [MatrixImageFile] and shrinks it in order to reduce traffic /// /// in case shrinking does not work (e.g. for unsupported MIME types), the @@ -85,11 +107,11 @@ class MatrixImageFile extends MatrixFile { String? mimeType, Future Function(FutureOr Function(U arg) function, U arg)? compute}) async { - Image? image; final arguments = _ResizeArguments( bytes: bytes, maxDimension: maxDimension, fileName: name, + calcBlurhash: true, ); final resizedData = compute != null ? await compute(_resize, arguments) @@ -98,48 +120,26 @@ class MatrixImageFile extends MatrixFile { if (resizedData == null) { return MatrixImageFile(bytes: bytes, name: name, mimeType: mimeType); } - image = decodeImage(resizedData); - - if (image == null) { - return MatrixImageFile(bytes: bytes, name: name, mimeType: mimeType); - } - - final encoded = encodeNamedImage(image, name); - if (encoded == null) { - return MatrixImageFile(bytes: bytes, name: name, mimeType: mimeType); - } final thumbnailFile = MatrixImageFile( - bytes: Uint8List.fromList(encoded), + bytes: resizedData.bytes, name: name, mimeType: mimeType, + width: resizedData.width, + height: resizedData.height, + blurhash: resizedData.blurhash, ); - // preserving the previously generated image - thumbnailFile._image = image; return thumbnailFile; } /// returns the width of the image - int? get width { - _image ??= decodeImage(bytes); - return _image?.width; - } + final int? width; /// returns the height of the image - int? get height { - _image ??= decodeImage(bytes); - return _image?.height; - } + final int? height; /// generates the blur hash for the image - String? get blurhash { - _image ??= decodeImage(bytes)!; - if (_image != null) { - final blur = BlurHash.encode(_image!, numCompX: 4, numCompY: 3); - return blur.hash; - } - return null; - } + final String? blurhash; @override String get msgType => 'm.image'; @@ -171,7 +171,23 @@ class MatrixImageFile extends MatrixFile { return thumbnailFile; } - static Uint8List? _resize(_ResizeArguments arguments) { + static _ResizedResponse? _calcMetadata(Uint8List bytes) { + final image = decodeImage(bytes); + if (image == null) return null; + + return _ResizedResponse( + bytes: bytes, + width: image.width, + height: image.height, + blurhash: BlurHash.encode( + image, + numCompX: 4, + numCompY: 3, + ).hash, + ); + } + + static _ResizedResponse? _resize(_ResizeArguments arguments) { final image = decodeImage(arguments.bytes); final resized = copyResize(image!, @@ -180,26 +196,54 @@ class MatrixImageFile extends MatrixFile { final encoded = encodeNamedImage(resized, arguments.fileName); if (encoded == null) return null; - return Uint8List.fromList(encoded); + final bytes = Uint8List.fromList(encoded); + return _ResizedResponse( + bytes: bytes, + width: resized.width, + height: resized.height, + blurhash: arguments.calcBlurhash + ? BlurHash.encode( + resized, + numCompX: 4, + numCompY: 3, + ).hash + : null, + ); } } +class _ResizedResponse { + final Uint8List bytes; + final int width; + final int height; + final String? blurhash; + + const _ResizedResponse({ + required this.bytes, + required this.width, + required this.height, + this.blurhash, + }); +} + class _ResizeArguments { final Uint8List bytes; final int maxDimension; final String fileName; + final bool calcBlurhash; const _ResizeArguments({ required this.bytes, required this.maxDimension, required this.fileName, + required this.calcBlurhash, }); } class MatrixVideoFile extends MatrixFile { - int? width; - int? height; - int? duration; + final int? width; + final int? height; + final int? duration; MatrixVideoFile( {required Uint8List bytes, @@ -221,7 +265,7 @@ class MatrixVideoFile extends MatrixFile { } class MatrixAudioFile extends MatrixFile { - int? duration; + final int? duration; MatrixAudioFile( {required Uint8List bytes, diff --git a/test/encryption/utils_test.dart b/test/encryption/utils_test.dart index 79a7f8d2..b21d0990 100644 --- a/test/encryption/utils_test.dart +++ b/test/encryption/utils_test.dart @@ -48,7 +48,7 @@ void main() { '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'; final data = base64Decode(base64Image); - final image = MatrixImageFile( + final image = await MatrixImageFile.create( bytes: data, name: 'bomb.png', mimeType: 'image/png',