Add image cropping functionality with isolate support and optimize crop border processing

This commit is contained in:
Moustapha Kodjo Amadou 2025-11-06 16:47:32 +01:00
parent f483dfab2b
commit 6e94632417
3 changed files with 116 additions and 6 deletions

View file

@ -22,6 +22,7 @@ import 'package:mangayomi/models/track.dart' as track;
import 'package:mangayomi/models/track_preference.dart';
import 'package:mangayomi/models/track_search.dart';
import 'package:mangayomi/modules/manga/detail/providers/track_state_providers.dart';
import 'package:mangayomi/modules/manga/reader/providers/crop_borders_provider.dart';
import 'package:mangayomi/modules/more/data_and_storage/providers/storage_usage.dart';
import 'package:mangayomi/modules/more/settings/browse/providers/browse_state_provider.dart';
import 'package:mangayomi/modules/more/settings/general/providers/general_state_provider.dart';
@ -52,6 +53,7 @@ void main(List<String> args) async {
if (Platform.isLinux && runWebViewTitleBarWidget(args)) return;
MediaKit.ensureInitialized();
await RustLib.init();
await imgCropIsolate.start();
if (!(Platform.isAndroid || Platform.isIOS)) {
await windowManager.ensureInitialized();
}

View file

@ -23,12 +23,119 @@ Future<Uint8List?> cropBorders(
return null;
}
return await Isolate.run(() async {
await RustLib.init();
final imageRes = processCropImage(image: imageBytes!);
RustLib.dispose();
return imageRes;
});
return imgCropIsolate.process(imageBytes);
}
return null;
}
class ImageCropIsolate {
bool _isRunning = false;
Isolate? _rustIsolate;
ReceivePort? _receivePort;
SendPort? _sendPort;
Future<void> start() async {
if (!_isRunning) {
try {
await _initRustIsolate();
} catch (_) {
await stop();
}
}
}
Future<void> _initRustIsolate() async {
_receivePort = ReceivePort();
_rustIsolate = await Isolate.spawn(
_rustIsolateEntryPoint,
_receivePort!.sendPort,
);
final completer = Completer<SendPort>();
_receivePort!.listen((message) {
if (message is SendPort) {
completer.complete(message);
}
});
_sendPort = await completer.future;
_isRunning = true;
}
static Future<void> _rustIsolateEntryPoint(SendPort mainSendPort) async {
await RustLib.init();
final receivePort = ReceivePort();
mainSendPort.send(receivePort.sendPort);
await for (var message in receivePort) {
if (message is Map<String, dynamic>) {
try {
final imageBytes = message['imageBytes'] as Uint8List;
final responsePort = message['responsePort'] as SendPort;
final croppedImage = processCropImage(image: imageBytes);
responsePort.send({'success': true, 'data': croppedImage});
} catch (e) {
final responsePort = message['responsePort'] as SendPort;
responsePort.send({'success': false, 'error': e.toString()});
}
} else if (message == 'dispose') {
RustLib.dispose();
break;
}
}
}
Future<Uint8List?> process(Uint8List imageBytes) async {
await start();
if (_sendPort == null) {
if (kDebugMode) {
print('Image crop isolate is not running');
}
return null;
}
final responsePort = ReceivePort();
final completer = Completer<Uint8List?>();
responsePort.listen((response) {
responsePort.close();
if (response is Map<String, dynamic>) {
if (response['success'] == true) {
completer.complete(response['data'] as Uint8List);
} else {
if (kDebugMode) {
print('Image cropping failed: ${response['error']}');
}
completer.complete(Future.value(null));
}
}
});
_sendPort!.send({
'imageBytes': imageBytes,
'responsePort': responsePort.sendPort,
});
return completer.future;
}
Future<void> stop() async {
if (!_isRunning) {
return;
}
_sendPort?.send('dispose');
_rustIsolate?.kill(priority: Isolate.immediate);
_receivePort?.close();
_sendPort = null;
_rustIsolate = null;
_receivePort = null;
_isRunning = false;
}
}
final imgCropIsolate = ImageCropIsolate();

View file

@ -1397,6 +1397,7 @@ class _MangaChapterPageGalleryState
}
void _processCropBorders() async {
if (_cropBorderCheckList.length == _uChapDataPreload.length) return;
for (var i = 0; i < _uChapDataPreload.length; i++) {
if (!_cropBorderCheckList.contains(i)) {
_cropBorderCheckList.add(i);