: Home :: Competenze :: Applicazioni :: Articoli :: Recensioni :: Wallpapers :: Links :: AJAX Chat :: LabGL (NEW) :: admin@damix.it :

Articolo: Immagini dinamiche con ASP.NET

TitoloImmagini dinamiche con ASP.NET
PrerequisitiConcetti di base di ASP.NET
CategoriaServer-side/ASP.NET
Descrizione In questo articolo useremo .NET e GDI+ per restituire un'immagine PNG dinamica al client; infatti, così come possibile utilizzare ASP.NET per restituire HTML personalizzato, la stessa cosa possibile per le immagini.

Introduzione

Il 99,99% degli script server-side presenti su Internet è rivolto alla generazione di codice HTML cosiddetto "dinamico", perchè è funzione dell'istante di esecuzione, del tipo di utente e di altri input più o meno evidenti. In effetti qualsiasi tipo di file può essere generato dinamicamente lato server e restituito all'utente; evidentemente con i formati di testo questo è particolarmente facile (e probabilmente è uno dei motivi per cui SVG avrà sempre più successo); con i formati binari invece ci sono maggiori difficoltà perchè non sappiamo esattamente quali byte restituire per ottenere un certo effetto, ad esempio visualizzare un coniglio rosa con un nome proprio variabile scritto sulla pancia. Ma per fortuna .NET può darci una mano. Invece di un coniglio rosa cercheremo di visualizzare qualcosa di simile alla barra del voto delle recensioni di damix.it, quella graziosa barra arancione che certamente avrete visto nel mio sito (perchè voi l'avete vista, veeero??? grr...). In gergo tecnico un componente rettangolare che rappresenta una percentuale è detto "gauge", cioè "misura".

Top Down

Pensiamo dapprima a "cosa" vorremmo poter scrivere in una pagina ASP.NET per generare un'immagine dinamica; vorremmo poter chiamare una funzione che prende in ingresso una percentuale, due colori, qualche altro parametro, così tanto per gradire; poi potremmo volere che questa funzione ci restituisse un oggetto Bitmap; e poi magari vorremmo poter salvare questa Bitmap come file PNG "direttamente" nello stream di risposta HTTP dell'utente, con una funzione simile a Response.Write(); quest'ultima funzione c'è, si chiama Response.BinaryWrite() e prende come argomento un byte []; resta il problema di come ottenere dei byte che effettivamente rappresentano l'immagine PNG che noi desideriamo. Per farla breve, vogliamo scrivere una DLL che poi potrà consentirci di fare questo:
gauge.aspx
<%@ Page Language="C#" Debug="true" %> <%@ Import Namespace="System" %> <%@ Import Namespace="System.Drawing" %> <%@ Import Namespace="System.Globalization" %> <%@ Import Namespace="ImgGauge" %> <% Response.ContentType = "image/png"; int level = Int32.Parse(Request.QueryString["level"]); if (level >= 0 && level <= 100) { Bitmap img = GaugeRenderer.GetImage(level, Server.MapPath("gauge-base.png"), new Point(1, 1), new Size(112, 12), Color.Blue, Color.Yellow, Color.Black); byte [] imgData = GaugeRenderer.GetImageBytes(img); img.Dispose(); Response.BinaryWrite(imgData); } %>
L'idea in effetti è quella di prendere un elemento grafico di base, ovvero nel nostro caso gauge-base.png, e disegnarci sopra un retangolo sfumato di dimensione variabile con GDI+.
Da questa: otteniamo questa:
La trasformazione è volutamente semplice; in dipendenza della vostra conoscenza di GDI+ e del tempo che avete a disposizione, potete ottenere cose molto più articolate. Una volta realizzata questa pagina ASP.NET, potremmo creare pagine che la utilizzano, in questo modo:
try-gauge.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <title>Prova Gauge</title> </head> <body> <img src="gauge.aspx?level=85" alt="Pompa: 85%"/> </body> </html>
Comodo eh? Starete ovviamente già pensando che anche questa pagina potrebbe essere dinamica e quindi il livello della gauge potrebbe essere letto da un database o da un file XML, come nel caso di damix.it; oppure starete pensando a un grafico "a torta" da visualizzare con questo metodo. Vediamo il codice della DLL che consente di renderizzare una gauge piena fino a un certo livello:
GaugeRenderer.cs
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.IO; using System.Drawing.Imaging; namespace ImgGauge { public class GaugeRenderer { public static Bitmap GetImage(int level, string imgBasePath, Point p, Size sz, Color start, Color end, Color bck) { Bitmap imgBase = new Bitmap(imgBasePath); Bitmap img = new Bitmap(imgBase.Size.Width, imgBase.Size.Height); Graphics gfx = Graphics.FromImage(img); gfx.DrawImage(imgBase, 0, 0); imgBase.Dispose(); SolidBrush b = new SolidBrush(bck); gfx.FillRectangle(b, new Rectangle(p, sz)); LinearGradientBrush lg = new LinearGradientBrush(p, p + sz, start, end); gfx.FillRectangle(lg, new Rectangle(p, new Size((int)((level / 100.0) * sz.Width), sz.Height))); gfx.Dispose(); return img; } public static byte[] GetImageBytes(Bitmap img) { byte[] imgData = new byte[8 * img.Width * img.Height]; MemoryStream ms = new MemoryStream(imgData); img.Save(ms, ImageFormat.Png); long pos = ms.Position; ms.Close(); Array.Resize<byte>(ref imgData, (int)pos); return imgData; } } }
Per semplicità ho strutturato il codice in due sole funzioni statiche. La prima, GetImage(), prende come argomenti:
La seconda, GetImageBytes() invece prende un'immagine e ne restituisce i byte. Cominciamo dal commentare la seconda, che è effettivamente la più noiosa.
Si comincia allocando un vettore di byte sufficientemente grande; nel mio caso ho voluto abbondare riservando 8 byte per ogni pixel dell'immagine. Poi si crea un oggetto MemoryStream; questo oggetto permetterà di utilizzare il vettore di byte come un file. A questo punto salviamo l'immagine in PNG sul MemoryStream creato; i pixel dell'immagine finiranno codificati nei byte del vettore di byte. La proprietà Position di MemoryStream ci da la posizione del cursore; in pratica utilizziamo questa informazione per capire quanti byte sono stati effettivamente scritti. Poi si chiude il MemoryStream e si ridimensiona il vettore di byte in modo da tralasciare in eccesso che avevamo riservato. Infine si restituisce i byte contenenti i pixel dell'immagine.
La prima funzione è molto più semplice. Praticamente crea un immagine Bitmap caricando il file di base, contenente la gauge vuota, poi crea una seconda immagine vuota della stessa dimensione; poi disegna l'immagine di base sull'immagine vuota e chiude il riferimento all'immagine di base. A questo punto si crea un SolidBrush con il colore di sfondo desiderato e si usa per disegnare un rettangolo pieno della dimensione specificata. Il LinearGradientBrush ci consentirà di ottenere l'effetto "sfumato" sulla gauge; il costruttore prende quattro parametri; il punto dell'immagine in cui è idealmente applicato il colore iniziale, il punto dell'immagine in cui e idealmente applicato quello finale, e i due colori. Il rettangolo sfumato viene disegnato con altezza fissa e con larghezza proporzionale al livello. Infine si fanno le necessarie operazioni di pulizia e si restituisce la Bitmap personalizzata appena creata.

Conclusione

Spero che questo articolo vi sia stato utile; potete scaricare il codice sorgente allegato. Con queste tecniche potete realizzare un qualsiasi tipo di immagine personalizzata! Potete addirittura offrire un servizio di e-card super-graficose! In cui il testo appare nell'immagine, e non all'esterno! Wow!
Valid XHTML 1.0 Strict Valid CSS!