Skip to content

Commit 0be28f4

Browse files
authored
fix(image): preserve transparency when compressing sent images (#1940)
1 parent f5c4f5f commit 0be28f4

File tree

5 files changed

+54
-8
lines changed

5 files changed

+54
-8
lines changed

lib/account/send_message_helper.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1393,7 +1393,7 @@ class SendMessageHelper {
13931393
conversationId,
13941394
messageId,
13951395
null,
1396-
mimeType: defaultMimeType,
1396+
mimeType: type.mimeType,
13971397
);
13981398
attachment = await attachment.create(recursive: true);
13991399
await attachment.writeAsBytes(data);

lib/utils/image.dart

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,20 @@ import 'load_balancer_utils.dart';
77
const _kMaxDimension = 1920;
88
const _kQuality = 85;
99

10-
enum ImageType { gif, jpeg }
10+
enum ImageType { gif, jpeg, png }
1111

1212
extension ImageTypeExtension on ImageType {
13-
String get mimeType => this == ImageType.gif ? 'image/gif' : 'image/jpeg';
13+
String get mimeType => switch (this) {
14+
ImageType.gif => 'image/gif',
15+
ImageType.jpeg => 'image/jpeg',
16+
ImageType.png => 'image/png',
17+
};
1418

15-
String get extension => this == ImageType.gif ? 'gif' : 'jpg';
19+
String get extension => switch (this) {
20+
ImageType.gif => 'gif',
21+
ImageType.jpeg => 'jpg',
22+
ImageType.png => 'png',
23+
};
1624
}
1725

1826
(Uint8List, ImageType, int, int)? compressImage(Uint8List data) {
@@ -47,6 +55,18 @@ extension ImageTypeExtension on ImageType {
4755
image = copyResize(image, width: targetWidth, height: targetHeight);
4856
}
4957

58+
final maxChannelValue = image.maxChannelValue;
59+
final hasTransparency = image.any((pixel) => pixel.a < maxChannelValue);
60+
61+
if (hasTransparency) {
62+
return (
63+
Uint8List.fromList(encodePng(image)),
64+
ImageType.png,
65+
image.width,
66+
image.height,
67+
);
68+
}
69+
5070
return (
5171
JpegEncoder(quality: _kQuality).encode(image),
5272
ImageType.jpeg,

macos/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ SPEC CHECKSUMS:
196196
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
197197
file_selector_macos: 9e9e068e90ebee155097d00e89ae91edb2374db7
198198
flutter_app_icon_badge: e05568a57ac0d98a0e7bc35c5b9ae9b118109b1e
199-
flutter_local_notifications: 4bf37a31afde695b56091b4ae3e4d9c7a7e6cda0
199+
flutter_local_notifications: 1fc7ffb10a83d6a2eeeeddb152d43f1944b0aad0
200200
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
201201
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
202202
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb

pubspec.lock

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,10 @@ packages:
174174
dependency: "direct dev"
175175
description:
176176
name: build_runner
177-
sha256: "7981eb922842c77033026eb4341d5af651562008cdb116bdfa31fc46516b6462"
177+
sha256: "521daf8d189deb79ba474e43a696b41c49fb3987818dbacf3308f1e03673a75e"
178178
url: "https://pub.dev"
179179
source: hosted
180-
version: "2.12.2"
180+
version: "2.13.1"
181181
built_collection:
182182
dependency: transitive
183183
description:
@@ -2358,4 +2358,4 @@ packages:
23582358
version: "3.1.3"
23592359
sdks:
23602360
dart: ">=3.11.0 <4.0.0"
2361-
flutter: ">=3.38.0"
2361+
flutter: ">=3.38.1 <4.0.0"

test/utils/image_test.dart

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'dart:typed_data';
2+
3+
import 'package:flutter_app/utils/image.dart';
4+
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:image/image.dart' as img;
6+
7+
void main() {
8+
test('compressImage preserves transparency for alpha images', () {
9+
final source = img.Image(width: 2, height: 1, numChannels: 4)
10+
..setPixelRgba(0, 0, 0, 0, 0, 0)
11+
..setPixelRgba(1, 0, 255, 255, 255, 255);
12+
13+
final encoded = Uint8List.fromList(img.encodePng(source));
14+
final result = compressImage(encoded);
15+
16+
expect(result, isNotNull);
17+
18+
final (bytes, type, _, _) = result!;
19+
final decoded = img.decodeImage(bytes);
20+
21+
expect(type.mimeType, 'image/png');
22+
expect(decoded, isNotNull);
23+
expect(decoded!.getPixel(0, 0).a.toInt(), 0);
24+
expect(decoded.getPixel(1, 0).a.toInt(), 255);
25+
});
26+
}

0 commit comments

Comments
 (0)