Manipolare immagini con GDI in ASP.NET
Ridimensionare, comprimere e aggiungere testo ad immagini all’interno di una pagina web: può essere utile farlo?
Per manipolare un’immagine, intendo leggerla da hard disk, fare delle modifiche (aggiungere testo, cambiare le dimensioni, cambiare il formato da bmp a jpg, ad esempio, cambiare la compressione) e presentare la copia modificata al visitatore che l’ha richiesta.
Questo è molto utile per i seguenti motivi:
- Permette di avere una sola copia dell’immagine sul server (ad esempio una copia di buona qualità e grandi dimensioni)
- Permette di aggiungere testo (ad esempio informazioni sul copyright, o una scritta che copra l’immagine, o addirittura un’altra immagine nel caso l’immagine venga “rubata” da un altro sito)
- Permette di mascherare il vero nome dell’immagine, in modo che l’immagine originale non possa essere trovata e scaricata.
Per compiere queste operazioni, mi sono appoggiato a delle semplici classi C# trovate in rete, modificandole per venire in contro alle mie esigenze. Quindi il codice che segue è scritto in C#.
Il cuore del sistema sarà una pagina .aspx che avrà il compito di leggere l’immagine e di trasmetterla a chi ne ha fatto richiesta. Supponiamo di chiamare questa pagina Display.aspx.
Quindi sarà necessario:
1) Copiare i due files BitMapTools.cs e BitMapManipulator.cs all’interno della vostra WebApplication (ad es. nella cartella App_Code, se usate VisualStudio2005, ma qualunque cartella va bene) e includerli nel progetto corrente (aggiungi elemento esistente).
2) Creare una nuova pagina aspx, chiamata ad esempio Display.aspx
3) Cancellare TUTTO il codice HTML/C# dalla pagina Display.aspx , ECCETTO la prima riga. Quindi il contenuto di Display.aspx dovrebbe assomigliare a
<%@ Page language="c#" Inherits="Display" CodeFile="Display.aspx.cs" %>
e BASTA, senza tag <form>, senza tag. <html>, senza tag <body>, ecc.
4) In Display.aspx.cs (il file “Code Behind” per Display.aspx, che dovrebbe essere nella stessa cartella di Display.aspx), aggiungere sotto ai vari “using System; using System.Data;..” le righe “using GMsGraphLib.BitMapTools; using System.Drawing.Drawing2D; using System.Drawing;”, per avere accesso ai metodi delle classi senza usare ogni volta il namespace come prefisso.
5) Aggiungere il necessario (che verrà descritto di seguito) codice all’interno del metodo PageLoad(..) in Display.aspx.cs
Il codice per modificare l’immagine.
In questo esempio, supporrò di voler modificare un’immagine cambiandone le dimensioni e la qualità in base a un parametro, e aggiungendo una scritta con il copyright.
L’url dell’immagine sarà qualcosa del tipo
http://IlMioServer/LaMiaApplicazione/Pagine/Display.aspx?h=200&q=80
Dove h=<pixel> rappresenta l’altezza desiderata dell’immagine, e q=<1-100> la qualità della compressione jpeg (1=minimo, 100=massimo)
Il codice per realizzare quanto descritto, è il seguente (da piazzare dentro a Display.aspx.cs)
protected void Page_Load(object sender, EventArgs e) //.bmp, .tif, …
{
string imagePath = @"C:\inetpub\wwwroot\LaMiaApplicazione\LaMiaImmagine.jpg";
// Questo è molto importante: dato che l’url dell’immagine sarà una pagina .aspx,
// è necessario far sapere al browser che questa è una jpeg, non un file html
this.Response.ContentType = "image/jpeg";
//Apro uno stream per leggere da disco la mia immagine (imagePath), e la carico in memoria, dentro alla variabile “b”
System.IO.Stream pic = System.IO.File.Open(imagePath, System.IO.FileMode.Open);
System.Drawing.Bitmap b = new Bitmap(pic);
pic.Close();
//Suppongo l’esistenza di un parametro h che mi dica l’altezza dell’immagine che voglio ottenere
//http://IlMioServer/LaMiaApplicazione/Pagine/Display.aspx?h=200&q=80
int h = 100, q=100;
//un’altezza a caso, quella predefinita in caso non sia specificata un altezza tramite h=<pixel>
if (this.Request["h"] != null)
{
try
{
//se il parametro h era un numero intero (200 nell’esempio), lo converto in numero intero
h = int.Parse(this.Request["h"]);
}
catch
{
//gestire l’errore
}
}
if (this.Request["q"] != null)
{
try
{
//se il parametro q era un numero intero (80 nell’esempio), lo converto in numero intero
q = int.Parse(this.Request["q"]);
}
catch
{
//gestire l’errore
}
}
//di quanto devo rimpicicolire l’immagine? 1= dimesione originale; 0,1 = un decimo della dim originale, ecc.
double scale = ((double)h / (double)b.Height);
//in molti casi non ha senso ingrandire l’immagine, quindi se richiede l’immagine con una
//dimensione maggiore dell’originale, restituisco l’originale
if (scale > 1)
scale = 1;
//ridimensiona la bitmap
if (scale != 1)
b = BitMapTools.ScaleBitmap(b, scale);
//Per miei scopi, ho creato un metodo che genera un’ombra in basso e a destra… come
//si può leggere nella descrizione di AddShadow(..), sono richiesti 5 files .bmp
//contenenti l’ombreggiatura: l’ombra dell’angolo in alto a destra, del lato destro, dell’angolo in basso a destra, ecc…
//b = (Bitmap)BitMapTools.AddShadow(b, “path contenente i files bmp con l’ombra);
//aggiungo una scritta sull’immagine, in basso a destra
string text = "Hello world!";
//dimensione del carattere
float size = 12;
//stile del carattere
System.Drawing.FontStyle style= System.Drawing.FontStyle.Bold;
//colori delle varie componenti usate
System.Drawing.Brush brush = System.Drawing.Brushes.YellowGreen, brushBack = System.Drawing.Brushes.Black, brushFront = System.Drawing.Brushes.Red;
//creo un oggetto GDI su cui apportare le modifiche, partendo dall’immagine BMP
System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(b);
//carattere
System.Drawing.Font font = new Font("Verdana", size, style);
//alcuni parametri non necessari, ma utili
g.CompositingQuality = CompositingQuality.HighQuality;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.AntiAlias;
//scrivo due volte il testo, sfasato di qualche pixel, e con colori diversi, per farlo risaltare in caso di sfondo
//con lo stesso colore del testo
g.DrawString(text, font, brushBack, new System.Drawing.PointF(b.Width - ((size - 2) * text.Length) + 1, b.Height - 5 - 2 * size + 1));
g.DrawString(text, font, brush, new System.Drawing.PointF(b.Width - ((size - 2) * text.Length), b.Height - 5 - 2 * size));
//riscrivo le modifiche sull’oggetto Bitmap
g.DrawImage(b, 0, 0);
//scrivo la jpeg ottenuta, invece che su disco, sullo stream associato alla connessione che ha richiesto questa immagine
//cioè, al client che ha chiesto l’immagine
BitMapTools.ConvertBitmapToJpeg(b, q).Save(this.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
A questo punto, per visualizzare la nostra immagine, alta 200 pixel e compressa con qualità 80, dovremo scrivere nella barra degli indirizzi del browser
h*ttp://IlMioServer/LaMiaApplicazione/Pagine/Display.aspx?h=200&q=80
Oppure, per visualizzarla dentro una pagina html, scrivere <img xsrc=”h*ttp://IlMioServer/LaMiaApplicazione/Pagine/Display.aspx?h=200&q=80″ >
Come nell’esempio seguente, dove la foto “rubata” da un altro sito (questo, ad esempio) compare con una scritta sul copyright,
mentre la stessa foto vista vista all’ interno del sito di appartenenza, appare senza la scritta sul copyright
Ovviemente questo è un esempio, che restituisce sempre la stessa foto, anche se modificata. Andrebbero ovviamente aggiunti un parametro per visualizzare foto diverse, e qualche meccanismo di caching, per evitare di affaticare il server.
Uno svantaggio di questo metodo, è che mascherando i nomi delle foto, i motori di ricerca hanno problemi ad associarle a parole chiave,e per questo è utlie usare tecniche di UrlRewriting.
Per vedere il risultato, potete visitare il mio sito www.fotovallescrivia.it.



A causa di un restyling del sito i due files “BitMapTools.cs” e “BitMapManipulator.cs” a cui faccio riferimento nell’articolo sono andati persi. Possono essere scaricati qui:
http://www.fotovallescrivia.it/rumente/GMsGraphLib.zip
Grazie macchiavelli per l’aggiornamento
Se vuoi possiamo modificare l’articolo e aggiungerli.
Ciao