Control Freak
LANGUAGES:VB.NET
ASP.NETVERSIONS: 1.x | 2.0
Improve Your Image(s)
Master Image Processing and Management
By Steve C. Orr
A picture is worth a thousand words ? and in some cases,they?re worth quite a few dollars too. Content is king on the Internet. Scatteredthroughout company hard drives everywhere are marketing materials, scanneddocumentation, artwork, charts containing sensitive data, and other valuableimages that can do wonders in the right hands ? or horrors in the wrong hands. Consolidatingthese materials into one central system is a common optimization of corporatedollars these days, and these systems usually must provide some way to get atfiles from across the Internet. Security is rightly a top concern in mostdocument management systems.
In some basic cases you can configure IIS to manage thefiles and their permissions for you, but often a more customized system isnecessary. As you?re probably aware, a standard Image control is defined withthe following ASPX code:
<asp:Image ID="Image1" Runat="server"
ImageUrl="SomeImage.jpg" />
When the page is output to the browser, the resulting HTMLwill consist of a standard <img> tag similar to this:
<img ID="Image1" src="SomeImage.jpg" />
A key point here is that the image is not really part ofthe page from the server?s point of view. Therefore, you can?t really do anycustom image processing (such as cropping, resizing, or adding annotations)within the page itself. Rather, the image file name is all that?s written tothe page (inside the image tag). As the browser interprets the HTML, itdownloads the image from the Web server as a completely separate request.
Now consider the following code:
<asp:Image ID="Image1" Runat="server"
ImageUrl="GenImage1.aspx" />
This Image control declaration illustrates that, insteadof pointing directly to an image file, you can point an Image control toward aseparate ASP.NET page where you can do any fancy dynamic image processing thatis needed.
In this example, GenImage1.aspx doesn?t contain any HTMLbecause its sole purpose is to output an image for inclusion in another page. Theonly code in the Page_Load event calls the procedure listed in Figure 1.
DisplayImage(NewBitmap("C:\PrivateDir\TopSecret.jpg")))
Private Sub DisplayImage(ByVal bmp As Bitmap)
WithHttpContext.Current
'Clear any existingpage content
.Response.Clear()
'Set the content type
.Response.ContentType= "image/jpeg"
'Output the image tothe OutputStream object
bmp.Save(.Response.OutputStream, _
Imaging.ImageFormat.Jpeg)
'Ensure the image isthe only thing that is output
.Response.End()
End With
End Sub
Figure 1: ASPXpages don?t have to output HTML. This example outputs an image, so that imagecontrols on other pages can reference this page instead of pointing directly toa static image file.
You might choose to add authentication code to a page suchas GenImage1 to ensure only proper individuals see the image. You?re alsolikely to sprinkle in some code to make this simple example more versatile byaccepting an image as a url parameter or some other mechanism to serve out avariety of image files instead of a single hard-coded one.
For an ASP.NET application to effectively manage files, itmust have permission to access these files. By default, ASP.NET runs under auser account (intuitively) named ASPNET. This user account has very limitedpermissions. It will not be able to interact with most of the server?s filesystem by default, and it won?t have access to any network shares, either. Therefore,you?ll want to give the ASPNET user account the folder permissions it needs, orhave ASP.NET use a different user account that does have the necessarypermissions.
You can adjust the user account from within IIS, or youcan configure Impersonation in the web.config file or the machine.config file. Forinitial experimentation and debugging I?d suggest having ASP.NET run under youruser account because you know what files you have permission to access:
<!-- Web.config file. -->
<identity impersonate="true"/>
<identity impersonate="true" userName=
"Redmond\BillG"password="Melinda"/>
If the images aren?t stored in a file system, but insteadare stored in a SQL Server database, then the code behind for GenImage1.aspxmight look more like that shown in Figure 2.
Dim dr As System.Data.SqlClient.SqlDataReader
cmdGetFile.Parameters("@File_ID").Value = _
Request("AttachmentID").ToString
dbConn.Open()
dr = cmdGetFile.ExecuteReader
If dr.Read Then
Response.Clear()
Response.ContentType =dr("ContentType").ToString
Response.OutputStream.Write(CType(dr("FileData"), _
Byte()), 0,CInt(dr("FileSize")))
Response.AddHeader("Content-Disposition", _
"inline;filename=" + dr("FileName").ToString())
End If
Figure 2: You cangrab the image data from a database and write the raw file data directly intothe Output Stream just before it?s sent to the browser.
This technique shows how you can dump a file directly froma database into the Response.OutputStream. ADO.NET is used to extract thebinary data from a SQL Server image field, the data is then converted into abyte array, and, finally, it?s written to the output stream along with adescriptive header to help the browser better interpret the resulting file. Formore details on this technique, see EasyUploads.
Custom Image Generation
By using the functionality included in the System.Drawingnamespace, your image manipulation capabilities are limitless. As if that weren?tenough power for a single developer to wield, there are also dozens of third-partycomponents available under such categories as charting, reporting, and imageprocessing libraries. Additionally, you can build your own image processingobject models either from scratch or by building on existing technologies. Hopefullyby now you?re beginning to realize the full power that can really lie behindthe seemingly humble image control.
The previous techniques are great for distributingpre-existing images, but if you need to dynamically create an image fromscratch (or modify an existing image on the fly,) then the System.Drawingnamespace will become quite familiar to you. Using the classes within thisnamespace you could create dynamic charts, graphs, or other useful output. However,that?s soooo boring! The next example will focus on less tangible corporateenhancements, such as improved morale.
Smiles can be infectious, and the next example willgenerate as many as you?d like. Call the subroutine shown in Figure 3 to createa randomly generated smiley face.
Private Sub DrawSmiley(ByVal g As Graphics, _
ByVal Width As Integer,ByVal Height As Integer, _
ByVal rand As Random)
Dim SmileyWidth AsInteger = rand.Next(Width / 2)
Dim SmileyHeight AsInteger = rand.Next(Height / 2)
'Draw the head (a bigcircle)
Dim x As Integer =rand.Next(Width - SmileyWidth)
Dim y As Integer =rand.Next(Height - SmileyHeight)
Dim PenWidth As Integer =rand.Next(5)
Dim RandomColor As Color= _
Color.FromArgb(rand.Next(255), _
rand.Next(255),rand.Next(255))
Dim Pen As NewPen(RandomColor, PenWidth)
g.DrawEllipse(Pen, x, y,SmileyWidth, SmileyHeight)
'Draw the Nose (in thecenter of the head)
Dim NoseRect As System.Drawing.RectangleF
NoseRect.Width =CInt(SmileyWidth / 50)
NoseRect.Height =CInt(SmileyHeight / 50)
NoseRect.X = CInt(x +(SmileyWidth / 2) - _
(NoseRect.Width / 2))
NoseRect.Y = CInt(y +(SmileyHeight / 2) - _
(NoseRect.Height / 2))
g.DrawEllipse(Pen,NoseRect)
g.FillEllipse(Brushes.Green, NoseRect)
'Draw the Left Eye
Dim EyeRect AsSystem.Drawing.RectangleF
EyeRect.Width =CInt(SmileyWidth / 30)
EyeRect.Height =CInt(SmileyHeight / 30)
EyeRect.X = CInt(x +(SmileyWidth / 2) - _
(EyeRect.Width / 2) -(SmileyWidth / 4))
EyeRect.Y = CInt(y +(SmileyHeight / 3) - _
(EyeRect.Height / 2))
g.DrawEllipse(NewPen(Color.Blue, PenWidth), EyeRect)
g.FillEllipse(Brushes.Blue, EyeRect)
'Draw the Right Eye
EyeRect.Width =CInt(SmileyWidth / 30)
EyeRect.Height =CInt(SmileyHeight / 30)
EyeRect.X = CInt(x +(SmileyWidth / 2) - _
(EyeRect.Width / 2) +(SmileyWidth / 4))
EyeRect.Y = CInt(y +(SmileyHeight / 3) - _
(EyeRect.Height / 2))
g.DrawEllipse(New Pen(Color.Blue,PenWidth), EyeRect)
g.FillEllipse(Brushes.Blue, EyeRect)
'Draw the smile
Dim points(2) AsSystem.Drawing.PointF
points(0) = NewSystem.Drawing.PointF(CInt(x + _
(SmileyWidth / 2) -(EyeRect.Width / 2) - _
(SmileyWidth / 4)), y +(SmileyHeight / 2))
points(1) = NewSystem.Drawing.PointF(CInt(x + _
(SmileyWidth / 2)), y +(SmileyHeight / 2) + _
(SmileyHeight / 4))
points(2) = NewSystem.Drawing.PointF(CInt(x + _
(SmileyWidth / 2) -(EyeRect.Width / 2) + _
(SmileyWidth / 4)), y +(SmileyHeight / 2))
g.DrawCurve(Pen, points,1)
End Sub
Figure 3: By usingthe classes within the System.Drawing namespace, nearly any illustrationimaginable can be generated at run time, including a bunch of smiley faces.
The first parameter is a Graphics object, which is thecanvas on which this masterpiece will be painted. The height and width of thecanvas are also passed along, to help ensure no smileys get abruptly cut off atthe edges of the canvas. Finally, a Random object is passed along, which willbe used to mix things up a bit.
Using the Random object, a random height and width aregenerated for the current smiley face and the head is drawn within thisbounding rectangle. A pen is created of random thickness and color. This penwill be used to draw most features of the face. The DrawEllipse method createsa circle, which is used in concert with the FillEllipse method to fill it withcolor. Three smaller circles are then drawn within the head to represent thenose and two eyes. Finally, the smile is drawn by passing an array of points tothe DrawCurve method of the Graphics object. All of the mathematical formulasthroughout the example are there simply to calculate the position and size ofeach facial feature.
The final piece of this image generation puzzle is thecode that will fill the Page_Load event of GenImage1.aspx and call theDrawSmiley routine. This Page_Load code is listed in Figure 4.
Dim g As Graphics
Dim rand As New Random 'random number generator
Dim bmp As Bitmap 'to hold the picture
Dim Width As Integer = 200 'image height
Dim Height As Integer = 200 'image width
Dim NumberOfSmileys As Integer = 3
'Grab parameters from the querystring (if any)
If Not IsNothing(Request.QueryString("NumSmileys"))Then
NumberOfSmileys = _
Int32.Parse(Request.QueryString("NumSmileys"))
End If
If Not IsNothing(Request.QueryString("Width")) Then
Width = _
CType(Request.QueryString("Width"), Integer)
End If
If Not IsNothing(Request.QueryString("Height")) Then
Height = _
CType(Request.QueryString("Height"), Integer)
End If
'Create a new bitmap of the specified size
bmp = New Bitmap(Width, Height, _
Drawing.Imaging.PixelFormat.Format16bppRgb565)
'Get the underlying Graphics object
g = Graphics.FromImage(bmp)
'Specify a white background
g.FillRectangle(Brushes.White, g.ClipBounds)
'Smooth out curves
g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
'Generate random smileys
For i As Integer = 1 To NumberOfSmileys
DrawSmiley(g, Width,Height, rand)
Next
DisplayImage(bmp)
Figure 4: Thiscode goes in the Page_Load event of GenImage1.aspx, which can be referenced bythe ImageURL property of a standard image control placed on any other page.
First, a few variables are declared with some default valuesspecifying the size of the image and the number of smiley faces that will bedrawn. Then the querystring is examined for optional parameters, which willreplace the defaults. A blank bitmap is then created with a white background. Antialiasingis turned on to create smoother looking curves for rounded shapes, such ascircles and smiles.
The main loop is then entered, iterating once for eachsmiley face to be drawn by calling the DrawSmiley subroutine mentioned earlier.Finally, the completed image is output by the DisplayImage subroutine in Figure1.
To see the code in action, create a new WebForm and dropan Image control onto it. Then simply set the ImageURL property of that Imagecontrol to point to the GenImage1.aspx page. The result will look a lot likeFigure 5.
Figure 5: The humble Image controlcan turn into a powerful tool once you?ve mastered the art of creating dynamic,configurable images at run time.
Conclusion
You should now have enough knowledge to manage andmanipulate images in all kinds of complex ways. The graphical possibilities areendless with these tools at your disposal. You can expand on these ideas in allkinds of ways. For example, you could create image buttons and other graphicalpage elements on demand to keep your Web site feeling constantly fresh and new.Look for a future article about manipulating existing images at run time, suchas: resizing, optimizing, cropping, rotating, adding borders, altering colorsand brightness, etc.
The techniques outlined in this article are the foundationfor virtually every modern third-party graphing component available on themarket today. You could also create your own, if so inclined. Let yourimagination wander and let me know what kinds of image creation tools youproduce as a result.
The sample code inthis article is available for download to asp.netPRO subscribers.
Steve C. Orr is anMCSD and a Microsoft MVP in ASP.NET. He?s been developing software solutionsfor leading companies in the Seattlearea for more than a decade. When he?s not busy designing software systems orwriting about such activities, he can often be found loitering at local usergroups and habitually lurking in the ASP.NET newsgroup. Find out more about himat http://Steve.Orr.netor e-mail him at mailto:Steve@Orr.net.