Nelle gallerie fotografiche e ovunque sia necessario utilizzare delle thumbnails per ottenere in un colpo d'occhio, molte piccole anteprime delle immagini, possiamo incontrare problemi di aspetto.
I casi sono diversi: possiamo trovarci a gestire collezioni di immagini che non hanno tutte le stesse proporzioni, o più tipicamente, insiemi di foto orientate sia in orizzontale, sia in verticale.
Se ci limitiamo a produrre thumbnails tutte uguali, possiamo ottenere un effetto sgradevole dovuto alla perdita dell'aspect ratio. Possiamo pensare allora di rimpicciolire le immagini mantenendo le proporzioni iniziali ma l'effetto d'insieme potrebbe risultare troppo disomogeneo.
Le soluzioni sono diverse e spesso si ricorre a cornici omogenee, spesso quadrate, che rendono tutto più gradevole.
Con questo articolo utilizziamo una soluzione alternativa, che ben si applica alle foto, e che prevede di creare thumbnails quadrate ritagliate dalle figure originali. L'idea è quella di cercare il lato minore dell'immagine e di tagliarla utilizzando il quadrato costruito su quel lato.
Alle immagini orizzontali saranno tolte le parti laterali, e alle verticali saranno scorciate le parti superiore e inferiore.
Cerchiamo di risolvere questo problema e lo usiamo come spunto per approfondire la conoscenza con le librerie grafiche di ASP.NET.
Anzitutto pensiamo la soluzione concettualmente. La classe Graphics
del namespace System.Drawing
ci permette di generare una sorta di "tela" sulla quale posizionare elementi grafici tra cui forme (linee, rettangoli o ellissi), testi e immagini raster (o bitmap). Possiamo definire quindi una superficie grafica sulla quale applicare gli elementi fornendone le coordinate.
Un'istanza della classe Graphics
si può ottenere a partire da una finestra Windows Form, ma è possibile generarla anche a partire da un oggetto Bitmap
utilizzando il metodo statico FromImage()
.
In questo modo la tela assume le stesse dimensioni della bitmap e tutte le modifiche applicate alla tela saranno riportate sull'immagine.
Possiamo pensare quindi di iniziare creando la nostra thumbnail vuota come oggetto Bitmap
con dimensioni quadrate. Fatto ciò generiamo l'oggetto Graphics (la "tela") e vi posizioniamo l'immagine.
Prima di piazzare l'immagine sulla tela dobbiamo averla già talgiata e ridimensionata. Vediamo come compiere tutte queste operazioni in un sol colpo.
Uno dei molti metodi DrawImage()
della classe Graphics
ci consente proprio di tagliare l'immagine e di ridimensionarla nello stesso passaggio. È importante però capire bene quali parametri sono necessari.
- L'immagine iniziale. È semplicemente l'immagine di cui vogliamo ottenere la thumbnail.
- Un rettangolo che serve per definire le dimensioni finali dell'immagine
- Le coordinate in cui posizionare un rettangolo di ritaglio sull'immagine per ottenere che sia centrata. Si intendono le coordinate dell'angolo superiore-sinistro del rettangolo.
- Le dimensioni del rettangolo di ritaglio (in questo caso un quadrato)
- L'unità di misura delle dimensioni inserite (in questo caso il pixel)
Il motore grafico GDI+ adatta alle nuove dimensioni solo ciò che è contenuto nel rettangolo di ritaglio. Nel nostro caso utilizziamo un quadrato con lato pari al lato minore dell'immagine in input.
Per migliorare la qualità dell'adattamento possiamo indicare all'oggetto Graphics
un algoritmo di interpolazione, impostando la proprietà InterpolationMode
.
Come abbiamo visto in un articolo precedente possiamo aggiungere alla thumbnail un'immagine o una scritta come watermark, continuando a scrivere sulla tela con i metodi della classe Graphics.
Una volta elaborata l'immagine la inviamo sullo stream di output e il gioco è fatto.
Possiamo finalmente esaminare un esempio di codifica che realizza quanto abbiamo descritto. Il codice che mostriamo può essere ottimizzato, ma per l'esempio abbiamo preferito lasciarlo più leggibile.
<%@ Page Language="C#" %>
<%@ import Namespace="System.Drawing" %>
<script runat="server">
// contenitore dell'immagine in ingresso
System.Drawing.Image immagineIN;
void Page_Load(Object sender, EventArgs e) {
// prende i dati dalla querystring
int dimLato = Convert.ToInt32(Request.QueryString["lato"]);
string strNomeFile = Request.QueryString["nomefile"];
string strPercorsoImmagine = Server.MapPath(".") + "/https://static.html.it/app/uploads/documenti/articoli/2308/" + strNomeFile;
try {
// carica l'immagine di partenza
immagineIN = System.Drawing.Image.FromFile(strPercorsoImmagine);
// determina l'orientamento dell'immagine
bool orizzontale = (immagineIN.Width > immagineIN.Height);
// misura lato minore e maggiore
int latoMinore = (orizzontale) ? immagineIN.Height : immagineIN.Width;
int latoMaggiore = (orizzontale) ? immagineIN.Width : immagineIN.Height;
// calcola le coordinate per centrare l'immagine nel rettangolo di taglio
int posizione_centrale = latoMaggiore / 2 - latoMinore / 2;
int posX = (orizzontale) ? posizione_centrale: 0;
int posY = (orizzontale) ? 0: posizione_centrale;
// crea un nuovo oggetto bitmap delle dimensioni della thumbnail
// che useremo per ottenere la superficie grafica
using (Bitmap bmThumb = new Bitmap(dimLato, dimLato, System.Drawing.Imaging.PixelFormat.Format24bppRgb))
{
using (Graphics grTela = Graphics.FromImage(bmThumb))
{
// rettangolo per il ridimensionamento
Rectangle rectDimensioniFinali = new Rectangle(0, 0, dimLato, dimLato);
// Imposta un'interpolazione molto accurata (più lenta)
grTela.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
// disegna l'immagine nella thumbnail
grTela.DrawImage(immagineIN, rectDimensioniFinali, posX, posY, latoMinore, latoMinore, GraphicsUnit.Pixel);
// indica al client che sta per ricevere un'immagine Jpeg
Response.ContentType = "image/jpeg";
bmThumb.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
}
immagineIN.Dispose();
immagineIN = null;
}
catch (Exception ex) {
Response.Write(ex.Message);
}
}
</script>
Le modalità di utilizzo possono essere diverse: possiamo richiamare il file così com'è, o realizzare un HttpHandler
che gestisca tutte le chiamate ad un certo insieme di indirizzi.