3a9174f6fc
La fotocamera di sistema non ruota lo scatto: salva i pixel come li legge il sensore e annota la rotazione in EXIF. SkiaSharp (SKBitmap.Decode) ignora quel tag, quindi le foto scattate ruotate finivano storte nelle miniature e venivano inviate cosi' al server. - ImageOrientation: applica l'orientamento ai pixel e ricodifica senza tag residui, mantenendo il formato di partenza. Immagine gia' dritta: nessuna ricodifica, per non perdere qualita' inutilmente. In caso di errore restituisce i byte originali, meglio una foto storta che nessuna foto. - UtilityFile.ConvertToDto: normalizza li', unico punto di passaggio di fotocamera e galleria, cosi' miniature, salvataggio locale e upload lavorano su pixel gia' dritti. Corretto anche DimensionBytes. - ImageThumb: applica l'orientamento anche in fase di miniatura, per raddrizzare le foto gia' presenti sul dispositivo. Nessun ritratto forzato: costringere una foto orizzontale in verticale vorrebbe dire ruotare il contenuto o ritagliarlo, e queste foto sono prove di ispezione. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
56 lines
2.1 KiB
C#
56 lines
2.1 KiB
C#
using SkiaSharp;
|
|
|
|
namespace SteUp.Maui.Core.Services;
|
|
|
|
public static class ImageThumb
|
|
{
|
|
public static async Task CreateThumbnailAsync(
|
|
string inputPath,
|
|
string outputPath,
|
|
int maxSide = 320,
|
|
int quality = 70,
|
|
CancellationToken ct = default)
|
|
{
|
|
// Leggi bytes (meglio in async)
|
|
var data = await File.ReadAllBytesAsync(inputPath, ct);
|
|
|
|
using var codec = SKCodec.Create(new SKMemoryStream(data));
|
|
if (codec is null)
|
|
throw new InvalidOperationException("Formato immagine non supportato o file corrotto.");
|
|
|
|
// Decodifica
|
|
using var decoded = SKBitmap.Decode(codec);
|
|
if (decoded is null)
|
|
throw new InvalidOperationException("Impossibile decodificare l'immagine.");
|
|
|
|
// SKBitmap.Decode non applica l'orientamento EXIF: senza questo passaggio le
|
|
// miniature delle foto scattate ruotate escono storte (vale anche per gli
|
|
// scatti già sul dispositivo, che non passano da UtilityFile.ConvertToDto).
|
|
using var bitmap = ImageOrientation.Upright(decoded, codec.EncodedOrigin);
|
|
|
|
// Calcola resize mantenendo aspect ratio
|
|
var w = bitmap.Width;
|
|
var h = bitmap.Height;
|
|
|
|
if (w <= 0 || h <= 0) throw new InvalidOperationException("Dimensioni immagine non valide.");
|
|
|
|
var scale = (float)maxSide / Math.Max(w, h);
|
|
if (scale > 1f) scale = 1f; // non ingrandire
|
|
|
|
var newW = Math.Max(1, (int)Math.Round(w * scale));
|
|
var newH = Math.Max(1, (int)Math.Round(h * scale));
|
|
|
|
using var resized = bitmap.Resize(new SKImageInfo(newW, newH), SKFilterQuality.Medium);
|
|
if (resized is null)
|
|
throw new InvalidOperationException("Resize fallito.");
|
|
|
|
using var image = SKImage.FromBitmap(resized);
|
|
using var encoded = image.Encode(SKEncodedImageFormat.Jpeg, quality);
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(outputPath)!);
|
|
|
|
await using var fs = File.Open(outputPath, FileMode.Create, FileAccess.Write, FileShare.None);
|
|
encoded.SaveTo(fs);
|
|
await fs.FlushAsync(ct);
|
|
}
|
|
} |