Manipolare immagini con GDI in ASP.NET



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)
{
string imagePath = @"C:inetpubwwwrootLaMiaApplicazioneLaMiaImmagine.jpg";
//.bmp, .tif, …

// 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.

Annunci sponsorizzati:
Condividi su Facebook Condividi su Twitter!
  • machiavelli

    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

  • Anonimo

    GRAZIE DA VERo UTILISSIMO! mi serviva per modificare la dimensione di un immagine in anteprima, temporaneamente, cosí non ci mette tanto per caricarsi. In che modo consigli di fare “il meccanismo di caching x non affaticare il server”? Non ho la piu palida idea come potrebbe farsi… magari fare una richiesta ad un servizio appositamente creato nel server, ma mi sembra poco utile, in quanto presenterebbe altri ritardi.

  • La cosa più facile è, prima di restituire la foto al client attraverso this.Response.OutputStream, salvare una copia su disco ad esempio, supponendo un parametro

    string ID=thie.Request[“ID”];

    Salvare su disco il sul seguente file:

    string cachedFile=LaMiaCachePath+”\\”+ID+”_”+q+”_”+h;

    StreamWriter sw= new StreamWriter(cachedFile);
    BitMapTools.ConvertBitmapToJpeg(b, q).Save(sw, System.Drawing.Imaging.ImageFormat.Jpeg);
    sw.Close();

    E aggiungere, come prima riga di Page_Load(..)
    (in pratica invece di ridimensionare la jpg, se esiste già una versione del file ridimensionato alle stesse dimeniosni e qualità, leggo il file da disco.

    if (File.Exists(cachedFile))
    {
    Stream cachedStream = System.IO.File.Open(name, System.IO.FileMode.Open);
    byte[] buf = new byte[this.BufferSize];
    int avail = 0;
    while ((avail = cachedStream.Read(buf, 0, this.BufferSize)) > 0)
    {
    this.Response.OutputStream.Write(buf, 0, avail);
    }

    cachedStream.Close();

    }

    Eventualmente invece di un semplice

    if (File.Exists(cachedFile))

    verificare anche che il cachedFile non sia più vecchio di tot giorni.

Pinterest