From c79b17f32572c38c4139a8e158f1ea22492309de Mon Sep 17 00:00:00 2001 From: Khusham Bansal <142911972+KhushamBansal@users.noreply.github.com> Date: Fri, 10 Jan 2025 22:50:50 +0530 Subject: [PATCH 1/2] fix: Pictures in portrait orientation are being sent flipped (in horizontal) Imports: Added package:exif/exif.dart and package:image/image.dart to handle image metadata and manipulation. _fixExifOrientation Method: This method reads the EXIF orientation from the image data and adjusts the image accordingly using the image package. Modifying _load Method: After downloading the image data (remoteData or data.bytes), we apply _fixExifOrientation before setting _imageData. Error Handling: If there's an error while adjusting the orientation, the original image data is used, ensuring the app doesn't crash. --- lib/widgets/mxc_image.dart | 92 +++++++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 10 deletions(-) diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index 230f3eb091..7354b21e41 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -10,6 +10,9 @@ import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_file_extension.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:exif/exif.dart'; +import 'package:image/image.dart' as img; + class MxcImage extends StatefulWidget { final Uri? uri; final Event? event; @@ -59,9 +62,71 @@ class _MxcImageState extends State { set _imageData(Uint8List? data) { if (data == null) return; final cacheKey = widget.cacheKey; - cacheKey == null - ? _imageDataNoCache = data - : _imageDataCache[cacheKey] = data; + if (cacheKey == null) { + _imageDataNoCache = data; + } else { + _imageDataCache[cacheKey] = data; + } + } + + Future _fixExifOrientation(Uint8List data) async { + try { + + final image = img.decodeImage(data); + if (image == null) return data; + + // Read EXIF data + final Map exifData = await readExifFromBytes(data); + final orientationValue = + exifData['Image Orientation']?.values?.first?.toInt() ?? 1; + + img.Image fixedImage; + + switch (orientationValue) { + case 1: + // No rotation + fixedImage = image; + break; + case 2: + // Horizontal flip + fixedImage = img.flipHorizontal(image); + break; + case 3: + // Rotate 180 + fixedImage = img.copyRotate(image, 180); + break; + case 4: + // Vertical flip + fixedImage = img.flipVertical(image); + break; + case 5: + // Vertical flip + 90 rotate right + fixedImage = img.copyRotate(img.flipVertical(image), 90); + break; + case 6: + // Rotate 90 + fixedImage = img.copyRotate(image, 90); + break; + case 7: + // Horizontal flip + 90 rotate right + fixedImage = img.copyRotate(img.flipHorizontal(image), 90); + break; + case 8: + // Rotate 270 + fixedImage = img.copyRotate(image, -90); + break; + default: + // Unknown orientation + fixedImage = image; + break; + } + + // Encode the fixed image back to Uint8List + return Uint8List.fromList(img.encodeJpg(fixedImage)); + } catch (e) { + // If there's an error, return the original data + return data; + } } Future _load() async { @@ -85,9 +150,12 @@ class _MxcImageState extends State { isThumbnail: widget.isThumbnail, animated: widget.animated, ); + + final adjustedData = await _fixExifOrientation(remoteData); + if (!mounted) return; setState(() { - _imageData = remoteData; + _imageData = adjustedData; }); } @@ -96,16 +164,21 @@ class _MxcImageState extends State { getThumbnail: widget.isThumbnail, ); if (data.detectFileType is MatrixImageFile) { + + final adjustedData = await _fixExifOrientation(data.bytes); + if (!mounted) return; setState(() { - _imageData = data.bytes; + _imageData = adjustedData; }); return; } } } - void _tryLoad(_) async { + void _tryLoad(_) => tryLoad(); + + void tryLoad() async { if (_imageData != null) { return; } @@ -114,7 +187,7 @@ class _MxcImageState extends State { } on IOException catch (_) { if (!mounted) return; await Future.delayed(widget.retryDuration); - _tryLoad(_); + tryLoad(); } } @@ -124,8 +197,7 @@ class _MxcImageState extends State { WidgetsBinding.instance.addPostFrameCallback(_tryLoad); } - Widget placeholder(BuildContext context) => - widget.placeholder?.call(context) ?? + Widget placeholder(BuildContext context) => widget.placeholder?.call(context) ?? Container( width: widget.width, height: widget.height, @@ -145,7 +217,7 @@ class _MxcImageState extends State { firstChild: placeholder(context), secondChild: hasData ? Image.memory( - data, + data!, width: widget.width, height: widget.height, fit: widget.fit, From 2a75fa0224be8c643ff4b95f06f6f944cf907821 Mon Sep 17 00:00:00 2001 From: Khusham Bansal <142911972+KhushamBansal@users.noreply.github.com> Date: Fri, 10 Jan 2025 22:54:55 +0530 Subject: [PATCH 2/2] fix: Pictures in portrait orientation are being sent flipped (in horizontal) --- pubspec.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 71f4d6d26b..2c671a7f96 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,8 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: + exif: ^3.1.4 + image: ^4.0.17 animations: ^2.0.11 archive: ^3.4.10 async: ^2.11.0 @@ -152,4 +154,4 @@ msix_config: install_certificate: false dependency_overrides: - win32: 5.5.3 \ No newline at end of file + win32: 5.5.3