Serversided afbeeldingen bewerken
Door Rutger Smit
22 april 2003
In deze tutorial gaan we bekijken hoe we met behulp van het .NET Framework in C# (in code behind)
afbeeldingen op een site dynamisch kunnen voorzien van een eigen logo en tekst.
Hadden we ‘vroeger’, in classic ASP nog third-party componenten nodig voor het
bewerken of maken van afbeeldingen (en als het een beetje tegen zat meerdere componenten),
tegenwoordig hebben we in ASP.NET genoeg aan de standaard voorzieningen. Afbeeldingen genereren,
bewerken en veranderen van formaat, het kan allemaal.
Voor deze tutorial ga ik er van uit dat de te bewerken afbeelding al op de server staat,
en deze niet overschreven wordt door de door ons gewijzigde afbeelding.
De afbeelding wordt als volgt aangeroepen:
<img src="/image.aspx?o=original.jpg&i=myIcon.ico&t=What are we going to do today?">
Om te beginnnen maken we eerst de aspx pagina. Deze is lekker kort.
We definiëren de Language: C#, het code-behind bestand: image.aspx.cs en de Inherits: createImage.
<%@ Page Language="C#" src="image.aspx.cs" Inherits="createImage"%>
Nu het code behind bestand, het bestand waar het allemaal in moet gebeuren.
We beginnen met het importeren van de benodigde namespaces.
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Configuration;
System: Algemene namespace, met algemene types en functies.
System.IO: Voor het openen van een bestand en het controleren of een bestand bestaat.
System.Drawing: Algemene namespace voor het kunnen werken met afbeeldingen.
System.Drawing.Drawing2D: Voor het verbeteren van de kwaliteit van de gegenereerde afbeelding.
System.Drawing.Imaging: Voor o.a. het Bitmap, Graphics en Image object.
System.Drawing.Text: Voor het schrijven van tekst op een afbeelding.
System.Configuration: Voor het uitlezen van waardes uit het web.config bestand.
In de pagina hebben we in het Page-directive bij Inherits createImage opgegeven,
we gaan nu dus een class aanmaken met de naam createImage en geven aan dat deze bij
het Page object hoort. Daaronder de Page_Init functie, deze wordt altijd gestart
als de pagina opgevraagd wordt. Dit gebeurt voor alle andere gebeurtenissen in de pagina,
maar de hebben we niet nodig, omdat daarin HTML gegenereerd wordt (en dat willen we juist niet).
public class pageLoad : System.Web.UI.Page
void Page_Init(object sender, EventArgs e)
De variabele basePath vullen we met het fysieke pad naar de root van de website.
Ter demonstratie haal ik deze uit de web.config, weet je ook meteen hoe dat werkt
(je kunt het pad natuurlijk ook hard in je code zetten). Ook de variabelen uit de
querystring zetten we in variabelen voor later gebruik.
--- web.config source ---
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appSettings>
<add key="wwwroot" value="C:\Inetpub\wwwroot\DynamicImages\" />
</appSettings>
</configuration>
--- einde web.config source ---
string basePath = ConfigurationSettings.AppSettings["wwwroot"];
string original = Request.QueryString["o"];
string icon = Request.QueryString["i"];
string txt = Request.QueryString["t"];
Een niet onbelangrijke check is om te kijken of de bestanden waar we mee aan de slag willen wel bestaan.
if(File.Exists(basePath+original) && File.Exists(basePath+icon))
{
//--- beide bestanden bestaan
}
else
{
//--- een van de bestanden bestaat niet
}
We maken een nieuw Image object aan en laten deze een bestand van de harde schijf openen.
In dit object zit nu onze originele afbeelding. Omdat we het Image niet kunnen bewerken
maken we een Bitmap object aan. Het Graphics object is het object dat nodig is om het
origineel te kunnen bewerken.
System.Drawing.Image img = System.Drawing.Image.FromFile(basePath+original);
Bitmap bmp = new Bitmap(img);
Graphics g = Graphics.FromImage(bmp);
De volgende stap is het aanmaken van wat Font en Brush objects. Deze gaan we gebruiken om het origineel mee te bewerken.
Font FontBottom = new Font("Arial Black", 8);
SolidBrush WhiteBrush = new SolidBrush(Color.White);
SolidBrush OrangeBrush = new SolidBrush(Color.Orange);
PointF FloatPoint = new PointF(bmp.Width-125, bmp.Height-17);
Icon ico = new Icon(basePath+icon);
Wat belangrijk is tijdens het bewerken van afbeeldingen is de volgorde van tekenen. Op het moment dat je een tekst weg schrijft
naar de afbeelding en vervolgens een zwart vlak gaat tekenen, dan komt deze over de tekst heen en is die dus niet meer te lezen.
We gaan nu onze Fonts en Brushes en andere zaken op de originele foto loslaten. Als eerste twee keer FillRectangle.
Dit zijn vierkante vlakken die je een startcoordinaat x,y en een eind coordinaat x,y moet geven (links boven en rechts onder).
De eerste is een wit vlak in de rechter bovenhoek die als omlijsting zal dienen voor de icon die we later zullen plaatsen.
De tweede is een oranje balk aan de onderkant van de foto. Vervolgens, als de basis vlakken getekend zijn, plaatsen we de
icon in de rechter bovenhoek. DrawIcon heeft in dit geval drie parameters, het Icon object,
het x coordinaat en het y
coordinaat van de linker bovenhoek. Ook hier bereken ik de x- en y-coordinaten omdat ik niet altijd zal weten hoe breed
het origineel is. We trekken er nog 8 van de x af en tellen er 8 bij de y bij zodat we een witte rand van het onderliggende
vlak te zien krijgen.
g.FillRectangle(WhiteBrush, (bmp.Width-ico.Width)-8, 0, bmp.Width, ico.Height+8);
g.FillRectangle(OrangeBrush, 0, bmp.Height-16, bmp.Width, 16);
g.DrawIcon(ico, (bmp.Width-ico.Width)-4, 4);
g.DrawString(txt, FontBottom, WhiteBrush, 0, bmp.Height-16);
De regel code die nu komt is niet noodzakelijk. Als je het niet gebruikt kun je
ook de namespace using System.Drawing.Drawing2D verwijderen.
De SmoothingMode kun
je gebruiken om de kwaliteit van de gegenereerde afbeelding wat te optimaliseren.
Let er wel op dat hoe hoger je de kwaliteit zet, de meer de CPU van je server belast
zal worden. Het is in dat soort gevallen raadzaam om de kwaliteit niet al te hoog
te zetten.
g.SmoothingMode = SmoothingMode.HighQuality;
Met Save slaan we de bewerkte afbeelding op in het Graphics object en is deze
klaar om naar de client gestuurd te worden.
g.Save();
Voordat we binaire data naar de client gaan sturen willen we eerst zeker weten
dat er geen vreemde zaken vooraf naar de client gestuurd zijn. Door Response.Clear
kunnen we met een schone lei beginnen. Response.ContentType gebruiken we om de browser
van de client te vertellen dat de data die nu gaat komen geen text/html
(zoals gebruikelijk met aspx paginas) maar van het type image/jpeg is.
Response.Clear();
Response.ContentType = "image/jpeg";
Nu daadwerkelijk de binaire data naar de client sturen. Met het bmp.Save wordt
de bewerkte foto in het Bitmap object opgeslagen en uiteindelijk verstuurd.
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
Nu zijn we er nog niet helemaal. Om alles netjes achter te laten gaan we even
alle aangemaakte objecten vernietigen. Dit gebeurt met Dispose. Doe je dit niet,
dan zal het origineel op de server gelocked zijn (kun je het niet deleten/overschrijven),
dit komt omdat de webserver nog met dit bestand bezig zou zijn. Verder is het verstandig
om het uitvoeren van de pagina te stoppen, zodat overige pagina gebeurtenissen niet
uitgevoerd worden.
g.Dispose();
bmp.Dispose();
img.Dispose();
Response.End();
|