From 94eec93e1395ad3dfaf07a57af5dd7aa34a89710 Mon Sep 17 00:00:00 2001 From: gary Date: Sat, 21 Feb 2026 22:51:44 +0100 Subject: [PATCH] Fix cap photo flow to always open review dialog with fallback --- lib/main.dart | 113 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 25 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index ccc1900..15ea070 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -154,10 +154,48 @@ class _MosaicHomePageState extends State { imageQuality: 95, maxWidth: 1800, ); - if (captured == null) return; + if (captured == null) { + debugPrint('[cap-photo] Capture cancelled by user.'); + return; + } + + Uint8List bytes; + try { + bytes = await captured.readAsBytes(); + } catch (e, st) { + debugPrint('[cap-photo] Failed to read captured bytes: $e\n$st'); + final fallbackImage = img.Image(width: 1, height: 1) + ..setPixelRgb(0, 0, 255, 152, 0); + bytes = Uint8List.fromList( + img.encodePng(fallbackImage, level: 1), + ); + } + + Map detected; + String? detectionWarning; + try { + detected = await compute(_extractCapFromPhotoIsolate, bytes); + } catch (e, st) { + debugPrint('[cap-photo] Detection isolate failed: $e\n$st'); + detectionWarning = + '⚠️ Farberkennung ist fehlgeschlagen. Fallback-Farbe wird verwendet; bitte prüfen/korrigieren.'; + final decoded = img.decodeImage(bytes); + final imageW = (decoded?.width ?? 1).toDouble(); + final imageH = (decoded?.height ?? 1).toDouble(); + final fallbackColor = Colors.orange.toARGB32(); + detected = { + 'dominantColor': fallbackColor, + 'averageColor': fallbackColor, + 'usedFallback': true, + 'circleX': imageW / 2, + 'circleY': imageH / 2, + 'circleR': math.min(imageW, imageH) * 0.28, + 'imageW': imageW, + 'imageH': imageH, + 'previewPng': bytes, + }; + } - final bytes = await captured.readAsBytes(); - final detected = await compute(_extractCapFromPhotoIsolate, bytes); if (!mounted) return; Color dominantColor = Color(detected['dominantColor'] as int); @@ -201,23 +239,35 @@ class _MosaicHomePageState extends State { if (updateDialog) { setDialogState(() {}); } - final adjusted = await compute( - _extractCapFromAdjustedCircleIsolate, - { - 'sourceBytes': bytes, - 'circleX': circleX, - 'circleY': circleY, - 'circleR': circleR, - }, - ); - if (localToken != recalcToken) return; - dominantColor = Color(adjusted['dominantColor'] as int); - averageColor = Color(adjusted['averageColor'] as int); - previewBytes = adjusted['previewPng'] as Uint8List; - selected = mode == ColorExtractionMode.dominant - ? dominantColor - : averageColor; - _photoCapHexCtrl.text = _colorToHex(selected); + try { + final adjusted = await compute( + _extractCapFromAdjustedCircleIsolate, + { + 'sourceBytes': bytes, + 'circleX': circleX, + 'circleY': circleY, + 'circleR': circleR, + }, + ); + if (localToken != recalcToken) return; + dominantColor = Color(adjusted['dominantColor'] as int); + averageColor = Color(adjusted['averageColor'] as int); + previewBytes = adjusted['previewPng'] as Uint8List; + selected = mode == ColorExtractionMode.dominant + ? dominantColor + : averageColor; + _photoCapHexCtrl.text = _colorToHex(selected); + } catch (e, st) { + debugPrint('[cap-photo] Recalculate failed: $e\n$st'); + if (localToken != recalcToken) return; + detectionWarning = + '⚠️ Aktualisierung fehlgeschlagen. Bitte Farbe manuell prüfen.'; + const fallback = Colors.orange; + dominantColor = fallback; + averageColor = fallback; + selected = fallback; + _photoCapHexCtrl.text = _colorToHex(selected); + } if (ctx.mounted) { setDialogState(() {}); } @@ -274,6 +324,16 @@ class _MosaicHomePageState extends State { : 'Kreis erkannt. Direkt im Foto ziehen oder mit Pinch/Slider anpassen.', style: Theme.of(context).textTheme.bodySmall, ), + if (detectionWarning != null) ...[ + const SizedBox(height: 6), + Text( + detectionWarning!, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.w600, + ), + ), + ], const SizedBox(height: 8), SegmentedButton( segments: const [ @@ -1094,8 +1154,8 @@ class _CircleAdjustOverlayState extends State<_CircleAdjustOverlay> { final x = (local.dx / maxW).clamp(0.0, 1.0); final y = (local.dy / shownH).clamp(0.0, 1.0); final scale = details.scale; - final r = ((_baseRadius ?? widget.circleR) * scale) - .clamp(0.06, 0.49); + final r = + ((_baseRadius ?? widget.circleR) * scale).clamp(0.06, 0.49); widget.onCircleChanged(x, y, r); }, onScaleEnd: (_) => _baseRadius = null, @@ -1125,7 +1185,8 @@ class _CircleOverlayPainter extends CustomPainter { final double y; final double r; - const _CircleOverlayPainter({required this.x, required this.y, required this.r}); + const _CircleOverlayPainter( + {required this.x, required this.y, required this.r}); @override void paint(Canvas canvas, Size size) { @@ -1154,8 +1215,10 @@ class _CircleOverlayPainter extends CustomPainter { ..style = PaintingStyle.stroke ..strokeWidth = 1 ..color = Colors.white70; - canvas.drawLine(Offset(center.dx - 10, center.dy), Offset(center.dx + 10, center.dy), cross); - canvas.drawLine(Offset(center.dx, center.dy - 10), Offset(center.dx, center.dy + 10), cross); + canvas.drawLine(Offset(center.dx - 10, center.dy), + Offset(center.dx + 10, center.dy), cross); + canvas.drawLine(Offset(center.dx, center.dy - 10), + Offset(center.dx, center.dy + 10), cross); } @override