import 'dart:io'; import 'dart:ui' show Rect; import 'package:image/image.dart' as img; import 'package:path/path.dart' as p; class CropSaveError implements Exception { final String code; const CropSaveError(this.code); @override String toString() => 'CropSaveError($code)'; } class CropSaveService { /// Tam çözünürlükte decode + EXIF yönünü gömme (engine görüntüsüyle tutarlı). static Future load(String srcPath) async { final bytes = await File(srcPath).readAsBytes(); final src = img.decodeImage(bytes); if (src == null) throw const CropSaveError('decode'); return img.bakeOrientation(src); } /// Hedef yol: `resim.jpg` -> `resim_143025.jpg` (çakışmada null). static String? buildOutputPath(String srcPath) { final ext = p.extension(srcPath); final base = p.basenameWithoutExtension(srcPath); final dir = p.dirname(srcPath); final now = DateTime.now(); final hh = now.hour.toString().padLeft(2, '0'); final mm = now.minute.toString().padLeft(2, '0'); final ss = now.second.toString().padLeft(2, '0'); final candidate = p.join(dir, '${base}_$hh$mm$ss$ext'); return File(candidate).existsSync() ? null : candidate; } /// Crop + aktif renk matrisi + JPEG q90. /// [matrix] 4x5 row-major renk matrisi (image_canvas ile aynı form, 0-255 alanı); /// null = gömme yok. static Future saveCrop({ required String srcPath, required img.Image source, required Rect rect, List? matrix, }) async { final outPath = buildOutputPath(srcPath); if (outPath == null) throw const CropSaveError('file_exists'); final iw = source.width, ih = source.height; final x = rect.left.round().clamp(0, iw - 1); final y = rect.top.round().clamp(0, ih - 1); final w = rect.right.round().clamp(x + 1, iw) - x; final h = rect.bottom.round().clamp(y + 1, ih) - y; if (w <= 0 || h <= 0) throw const CropSaveError('empty_rect'); final crop = img.copyCrop(source, x: x, y: y, width: w, height: h); // Çıktıda yön fiziksel — EXIF tag'ini temizle (çift dönüşü önler). crop.exif.imageIfd.orientation = null; if (matrix != null) _applyMatrix(crop, matrix); final jpeg = img.JpegEncoder(quality: 90).encode(crop); await File(outPath).writeAsBytes(jpeg); return outPath; } /// Flutter'la aynı 0-255 alan/semantiğindeki 4x5 matrisi uygular. static void _applyMatrix(img.Image image, List m) { for (final pixel in image) { final r = pixel.r.toDouble(), g = pixel.g.toDouble(), b = pixel.b.toDouble(); pixel.r = (m[0] * r + m[1] * g + m[2] * b + m[4]).clamp(0, 255).round(); pixel.g = (m[5] * r + m[6] * g + m[7] * b + m[9]).clamp(0, 255).round(); pixel.b = (m[10] * r + m[11] * g + m[12] * b + m[13]).clamp(0, 255).round(); } } }