diff --git a/lib/services/image_crop_service.dart b/lib/services/image_crop_service.dart index fe7aa76f..bb96820b 100644 --- a/lib/services/image_crop_service.dart +++ b/lib/services/image_crop_service.dart @@ -155,10 +155,31 @@ void _viewportCropIsolateEntry(_ViewportCropParams p) { final int srcX = (cropCenterX - side / 2).round(); final int srcY = (cropCenterY - side / 2).round(); - // Toile carrée noire opaque ; compositeImage rogne automatiquement les bords - // hors-cadre, donc les débordements restent noirs (padding). + // Toile carrée NOIRE opaque (numChannels 3 → pixels initialisés à 0 = noir). final out = img.Image(width: side, height: side, numChannels: 3); - img.compositeImage(out, rotated, dstX: -srcX, dstY: -srcY); + + // Intersection de la fenêtre de visée avec l'image réelle. Tout ce qui + // déborde reste noir (padding) → aucun recentrage forcé. + final int vx0 = math.max(0, srcX); + final int vy0 = math.max(0, srcY); + final int vx1 = math.min(rotated.width, srcX + side); + final int vy1 = math.min(rotated.height, srcY + side); + final int vw = vx1 - vx0; + final int vh = vy1 - vy0; + + if (vw > 0 && vh > 0) { + final region = img.copyCrop(rotated, x: vx0, y: vy0, width: vw, height: vh); + // dstW/dstH EXPLICITES = taille de la région → AUCUN redimensionnement + // (sinon compositeImage étire la source pour remplir la destination). + img.compositeImage( + out, + region, + dstX: vx0 - srcX, + dstY: vy0 - srcY, + dstW: vw, + dstH: vh, + ); + } img.Image result = out; if (side != p.outputSize) {