Merge branch 'krille/matrix-sdk-image-thumbnailing' into 'main'

fix: Set image width and height

See merge request famedly/fluffychat!708
This commit is contained in:
Krille Fear 2022-02-03 06:55:34 +00:00
commit 0a909dcfb9
7 changed files with 47 additions and 124 deletions

View File

@ -13,7 +13,6 @@ import 'package:fluffychat/pages/add_story/add_story_view.dart';
import 'package:fluffychat/pages/add_story/invite_story_page.dart'; import 'package:fluffychat/pages/add_story/invite_story_page.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_file_extension.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
import 'package:fluffychat/utils/resize_image.dart'; import 'package:fluffychat/utils/resize_image.dart';
import 'package:fluffychat/utils/room_send_file_extension.dart';
import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/utils/string_color.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart'; import '../../utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
@ -29,8 +28,8 @@ class AddStoryController extends State<AddStoryPage> {
final TextEditingController controller = TextEditingController(); final TextEditingController controller = TextEditingController();
late Color backgroundColor; late Color backgroundColor;
late Color backgroundColorDark; late Color backgroundColorDark;
MatrixFile? image; MatrixImageFile? image;
MatrixFile? video; MatrixVideoFile? video;
VideoPlayerController? videoPlayerController; VideoPlayerController? videoPlayerController;
@ -49,8 +48,13 @@ class AddStoryController extends State<AddStoryPage> {
); );
final fileName = picked.fileName; final fileName = picked.fileName;
if (fileName == null) return; if (fileName == null) return;
final shrinked = await MatrixImageFile.shrink(
bytes: picked.toUint8List(),
name: fileName,
compute: Matrix.of(context).client.runInBackground,
);
setState(() { setState(() {
image = MatrixFile(bytes: picked.toUint8List(), name: fileName); image = shrinked;
}); });
} }
@ -60,8 +64,13 @@ class AddStoryController extends State<AddStoryPage> {
); );
if (picked == null) return; if (picked == null) return;
final bytes = await picked.readAsBytes(); final bytes = await picked.readAsBytes();
final shrinked = await MatrixImageFile.shrink(
bytes: bytes,
name: picked.name,
compute: Matrix.of(context).client.runInBackground,
);
setState(() { setState(() {
image = MatrixFile(bytes: bytes, name: picked.name); image = shrinked;
}); });
} }
@ -73,7 +82,7 @@ class AddStoryController extends State<AddStoryPage> {
final bytes = await picked.readAsBytes(); final bytes = await picked.readAsBytes();
setState(() { setState(() {
video = MatrixFile(bytes: bytes, name: picked.name); video = MatrixVideoFile(bytes: bytes, name: picked.name);
videoPlayerController = VideoPlayerController.file(File(picked.path)) videoPlayerController = VideoPlayerController.file(File(picked.path))
..setLooping(true); ..setLooping(true);
}); });
@ -109,16 +118,17 @@ class AddStoryController extends State<AddStoryPage> {
var video = this.video?.detectFileType; var video = this.video?.detectFileType;
if (video != null) { if (video != null) {
video = await video.resizeVideo(); video = await video.resizeVideo();
await storiesRoom.sendFileEventWithThumbnail( final thumbnail = await video.getVideoThumbnail();
await storiesRoom.sendFileEvent(
video, video,
extraContent: {'body': controller.text}, extraContent: {'body': controller.text},
thumbnail: thumbnail,
); );
return; return;
} }
var image = this.image?.detectFileType; final image = this.image;
if (image != null) { if (image != null) {
image = await image.resizeImage(); await storiesRoom.sendFileEvent(
await storiesRoom.sendFileEventWithThumbnail(
image, image,
extraContent: {'body': controller.text}, extraContent: {'body': controller.text},
); );
@ -142,9 +152,10 @@ class AddStoryController extends State<AddStoryPage> {
final shareContent = Matrix.of(context).shareContent; final shareContent = Matrix.of(context).shareContent;
// ignore: unnecessary_null_comparison // ignore: unnecessary_null_comparison
if (shareContent != null) { if (shareContent != null) {
image = shareContent.tryGet<MatrixFile>('file'); final shareFile = shareContent.tryGet<MatrixFile>('file')?.detectFileType;
controller.text = shareContent.tryGet<String>('body') ?? ''; controller.text = shareContent.tryGet<String>('body') ?? '';
if (shareContent.tryGet<String>('msgtype') == MessageTypes.Image) { if (shareFile is MatrixImageFile) {
Event( Event(
content: shareContent, content: shareContent,
type: EventTypes.Message, type: EventTypes.Message,
@ -154,10 +165,10 @@ class AddStoryController extends State<AddStoryPage> {
originServerTs: DateTime.now(), originServerTs: DateTime.now(),
).downloadAndDecryptAttachment().then((file) { ).downloadAndDecryptAttachment().then((file) {
setState(() { setState(() {
image = file; image = shareFile;
}); });
}); });
} else if (shareContent.tryGet<String>('msgtype') == MessageTypes.Video) { } else if (shareFile is MatrixVideoFile) {
Event( Event(
content: shareContent, content: shareContent,
type: EventTypes.Message, type: EventTypes.Message,
@ -167,7 +178,7 @@ class AddStoryController extends State<AddStoryPage> {
originServerTs: DateTime.now(), originServerTs: DateTime.now(),
).downloadAndDecryptAttachment().then((file) { ).downloadAndDecryptAttachment().then((file) {
setState(() { setState(() {
video = file; video = shareFile;
}); });
}); });
} }

View File

@ -6,7 +6,6 @@ import 'package:matrix/matrix.dart';
import '../../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart'; import '../../utils/matrix_sdk_extensions.dart/matrix_file_extension.dart';
import '../../utils/resize_image.dart'; import '../../utils/resize_image.dart';
import '../../utils/room_send_file_extension.dart';
class SendFileDialog extends StatefulWidget { class SendFileDialog extends StatefulWidget {
final Room room; final Room room;
@ -31,15 +30,21 @@ class _SendFileDialogState extends State<SendFileDialog> {
Future<void> _send() async { Future<void> _send() async {
var file = widget.file; var file = widget.file;
MatrixImageFile? thumbnail;
if (file is MatrixImageFile && if (file is MatrixImageFile &&
!origImage && !origImage &&
file.bytes.length > minSizeToCompress) { file.bytes.length > minSizeToCompress) {
file = await file.resizeImage(); file = await MatrixImageFile.shrink(
bytes: file.bytes,
name: file.name,
compute: widget.room.client.runInBackground,
);
} }
if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) { if (file is MatrixVideoFile && file.bytes.length > minSizeToCompress) {
file = await file.resizeVideo(); file = await file.resizeVideo();
thumbnail = await file.getVideoThumbnail();
} }
await widget.room.sendFileEventWithThumbnail(file); await widget.room.sendFileEvent(file, thumbnail: thumbnail);
} }
@override @override

View File

@ -7,7 +7,6 @@ import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart'; import 'package:vrouter/vrouter.dart';
import '../../utils/resize_image.dart';
import '../../widgets/matrix.dart'; import '../../widgets/matrix.dart';
import 'settings_emotes_view.dart'; import 'settings_emotes_view.dart';
@ -206,14 +205,19 @@ class EmotesSettingsController extends State<EmotesSettings> {
name: result.fileName!, name: result.fileName!,
); );
try { try {
file = await file.resizeImage(calcBlurhash: false); file = (await file.generateThumbnail(
compute: Matrix.of(context).client.runInBackground,
))!;
} catch (_) { } catch (_) {
// do nothing // do nothing
} }
final uploadResp = await showFutureLoadingDialog( final uploadResp = await showFutureLoadingDialog(
context: context, context: context,
future: () => Matrix.of(context).client.uploadContent(file.bytes, future: () => Matrix.of(context).client.uploadContent(
filename: file.name, contentType: file.mimeType), file.bytes,
filename: file.name,
contentType: file.mimeType,
),
); );
if (uploadResp.error == null) { if (uploadResp.error == null) {
setState(() { setState(() {

View File

@ -1,9 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:blurhash_dart/blurhash_dart.dart'; import 'package:blurhash_dart/blurhash_dart.dart';
import 'package:image/image.dart'; import 'package:image/image.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
@ -50,74 +47,15 @@ extension ResizeImage on MatrixFile {
return MatrixImageFile( return MatrixImageFile(
bytes: bytes, bytes: bytes,
name: name, name: name,
).resizeImage(); );
} catch (e, s) { } catch (e, s) {
SentryController.captureException(e, s); SentryController.captureException(e, s);
} }
return null; return null;
} }
Future<MatrixImageFile> resizeImage({
bool calcBlurhash = true,
int max = ResizeImage.max,
int quality = ResizeImage.quality,
}) async {
final bytes = mimeType == 'image/gif'
? this.bytes
: await compute<_ResizeBytesConfig, Uint8List>(
resizeBytes,
_ResizeBytesConfig(
bytes: this.bytes,
mimeType: mimeType,
max: max,
quality: quality,
));
final blurhash = calcBlurhash
? await compute<Uint8List, BlurHash>(createBlurHash, bytes)
: null;
return MatrixImageFile(
bytes: bytes,
name: '${name.split('.').first}_thumbnail_$max.jpg',
blurhash: blurhash?.hash,
);
}
} }
Future<BlurHash> createBlurHash(Uint8List file) async { Future<BlurHash> createBlurHash(Uint8List file) async {
final image = decodeImage(file)!; final image = decodeImage(file)!;
return BlurHash.encode(image, numCompX: 4, numCompY: 3); return BlurHash.encode(image, numCompX: 4, numCompY: 3);
} }
Future<Uint8List> resizeBytes(_ResizeBytesConfig config) async {
var image = decodeImage(config.bytes)!;
// Is file already smaller than max? Then just return.
if (math.max(image.width, image.height) > config.max) {
// Use the larger side to resize.
final useWidth = image.width >= image.height;
image = useWidth
? copyResize(image, width: config.max)
: copyResize(image, height: config.max);
}
const pngMimeType = 'image/png';
final encoded = config.mimeType.toLowerCase() == pngMimeType
? encodePng(image)
: encodeJpg(image, quality: config.quality);
return Uint8List.fromList(encoded);
}
class _ResizeBytesConfig {
final Uint8List bytes;
final int max;
final int quality;
final String mimeType;
const _ResizeBytesConfig({
required this.bytes,
this.max = ResizeImage.max,
this.quality = ResizeImage.quality,
required this.mimeType,
});
}

View File

@ -1,35 +0,0 @@
import 'package:matrix/matrix.dart';
import 'resize_image.dart';
extension RoomSendFileExtension on Room {
Future<Uri> sendFileEventWithThumbnail(
MatrixFile file, {
String? txid,
Event? inReplyTo,
String? editEventId,
bool? waitUntilSent,
Map<String, dynamic>? extraContent,
}) async {
MatrixImageFile? thumbnail;
if (file is MatrixImageFile) {
thumbnail = await file.resizeImage();
if (thumbnail.size > file.size ~/ 2) {
thumbnail = null;
}
} else if (file is MatrixVideoFile) {
thumbnail = await file.getVideoThumbnail();
}
return sendFileEvent(
file,
txid: txid,
inReplyTo: inReplyTo,
editEventId: editEventId,
waitUntilSent: waitUntilSent ?? false,
thumbnail: thumbnail,
extraContent: extraContent,
);
}
}

View File

@ -486,7 +486,7 @@ packages:
name: flutter_native_splash name: flutter_native_splash
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.3" version: "2.0.1+1"
flutter_olm: flutter_olm:
dependency: "direct main" dependency: "direct main"
description: description:
@ -818,7 +818,7 @@ packages:
name: matrix name: matrix
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.7.3" version: "0.8.0"
matrix_api_lite: matrix_api_lite:
dependency: transitive dependency: transitive
description: description:

View File

@ -50,7 +50,7 @@ dependencies:
intl: any intl: any
localstorage: ^4.0.0+1 localstorage: ^4.0.0+1
lottie: ^1.2.1 lottie: ^1.2.1
matrix: ^0.7.3 matrix: ^0.8.0
matrix_link_text: ^1.0.2 matrix_link_text: ^1.0.2
open_noti_settings: ^0.4.0 open_noti_settings: ^0.4.0
package_info_plus: ^1.2.1 package_info_plus: ^1.2.1
@ -82,7 +82,7 @@ dependencies:
dev_dependencies: dev_dependencies:
dart_code_metrics: ^4.2.0-dev.3 dart_code_metrics: ^4.2.0-dev.3
flutter_lints: ^1.0.4 flutter_lints: ^1.0.4
flutter_native_splash: ^1.2.4 flutter_native_splash: ^2.0.1+1
flutter_test: flutter_test:
sdk: flutter sdk: flutter
import_sorter: ^4.6.0 import_sorter: ^4.6.0