using SkiaSharp; namespace SteUp.Maui.Core.Services; /// /// Raddrizza le immagini applicando l'orientamento EXIF ai pixel. /// La fotocamera di sistema non ruota lo scatto: salva i pixel come li ha letti il /// sensore e annota la rotazione in EXIF. SkiaSharp () /// ignora quel tag, quindi senza questo passaggio le foto scattate ruotate finiscono /// storte nelle miniature e sul server. /// public static class ImageOrientation { /// /// Restituisce i byte dell'immagine con i pixel già nel verso giusto e senza tag /// di orientamento residui. In caso di formato non gestito o errore restituisce /// l'originale: meglio una foto storta che perdere lo scatto del rilevatore. /// public static byte[] Normalize(byte[] bytes, int quality = 92) { try { using var codec = SKCodec.Create(new SKMemoryStream(bytes)); if (codec is null) return bytes; var origin = codec.EncodedOrigin; // Già dritta: non ricodifichiamo, così non si perde qualità inutilmente. if (origin == SKEncodedOrigin.TopLeft) return bytes; using var decoded = SKBitmap.Decode(codec); if (decoded is null) return bytes; using var upright = ApplyOrigin(decoded, origin); using var image = SKImage.FromBitmap(upright); // Manteniamo il formato di partenza: il nome file conserva la sua estensione. var format = codec.EncodedFormat == SKEncodedImageFormat.Png ? SKEncodedImageFormat.Png : SKEncodedImageFormat.Jpeg; using var encoded = image.Encode(format, quality); return encoded?.ToArray() ?? bytes; } catch { return bytes; } } /// /// Versione dritta di una bitmap già decodificata. Restituisce sempre una nuova /// bitmap, anche quando non serve ruotare, così chi chiama ne possiede una sola. /// public static SKBitmap Upright(SKBitmap src, SKEncodedOrigin origin) => origin == SKEncodedOrigin.TopLeft ? src.Copy() : ApplyOrigin(src, origin); private static SKBitmap ApplyOrigin(SKBitmap src, SKEncodedOrigin origin) { // Gli orientamenti "di lato" scambiano larghezza e altezza. var sideways = origin is SKEncodedOrigin.LeftTop or SKEncodedOrigin.RightTop or SKEncodedOrigin.RightBottom or SKEncodedOrigin.LeftBottom; var w = sideways ? src.Height : src.Width; var h = sideways ? src.Width : src.Height; var dst = new SKBitmap(w, h, src.ColorType, src.AlphaType); using var canvas = new SKCanvas(dst); switch (origin) { case SKEncodedOrigin.TopRight: // specchiata in orizzontale canvas.Translate(w, 0); canvas.Scale(-1, 1); break; case SKEncodedOrigin.BottomRight: // ruotata di 180° canvas.Translate(w, h); canvas.RotateDegrees(180); break; case SKEncodedOrigin.BottomLeft: // specchiata in verticale canvas.Translate(0, h); canvas.Scale(1, -1); break; case SKEncodedOrigin.LeftTop: // trasposta canvas.RotateDegrees(90); canvas.Scale(1, -1); break; case SKEncodedOrigin.RightTop: // ruotata di 90° in senso orario canvas.Translate(w, 0); canvas.RotateDegrees(90); break; case SKEncodedOrigin.RightBottom: // trasversa canvas.Translate(w, h); canvas.RotateDegrees(270); canvas.Scale(1, -1); break; case SKEncodedOrigin.LeftBottom: // ruotata di 90° in senso antiorario canvas.Translate(0, h); canvas.RotateDegrees(-90); break; } canvas.DrawBitmap(src, 0, 0); return dst; } }