Control Freak
LANGUAGES: VB.NET
ASP.NET VERSIONS: 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. Scattered throughout company hard drives everywhere are marketing materials, scanned documentation, artwork, charts containing sensitive data, and other valuable images that can do wonders in the right hands or horrors in the wrong hands. Consolidating these materials into one central system is a common optimization of corporate dollars these days, and these systems usually must provide some way to get at files from across the Internet. Security is rightly a top concern in most document management systems.
In some basic cases you can configure IIS to manage the files and their permissions for you, but often a more customized system is necessary. As you re probably aware, a standard Image control is defined with the following ASPX code:
ImageUrl="SomeImage.jpg" /> When the page is output to the browser, the resulting HTML
will consist of a standard tag similar to this: A key point here is that the image is not really part of
the page from the server s point of view. Therefore, you can t really do any
custom image processing (such as cropping, resizing, or adding annotations)
within the page itself. Rather, the image file name is all that s written to
the page (inside the image tag). As the browser interprets the HTML, it
downloads the image from the Web server as a completely separate request. Now consider the following code: ImageUrl="GenImage1.aspx" /> This Image control declaration illustrates that, instead
of pointing directly to an image file, you can point an Image control toward a
separate ASP.NET page where you can do any fancy dynamic image processing that
is needed. In this example, GenImage1.aspx doesn t contain any HTML
because its sole purpose is to output an image for inclusion in another page. The
only code in the Page_Load event calls the procedure listed in Figure 1. DisplayImage(New
Bitmap("C:\PrivateDir\TopSecret.jpg"))) Private Sub DisplayImage(ByVal bmp As Bitmap) With
HttpContext.Current 'Clear any existing
page content .Response.Clear() 'Set the content type .Response.ContentType
= "image/jpeg" 'Output the image to
the OutputStream object bmp.Save(.Response.OutputStream, _ Imaging.ImageFormat.Jpeg) 'Ensure the image is
the only thing that is output .Response.End() End With End Sub Figure 1: ASPX
pages don t have to output HTML. This example outputs an image, so that image
controls on other pages can reference this page instead of pointing directly to
a static image file. You might choose to add authentication code to a page such
as GenImage1 to ensure only proper individuals see the image. You re also
likely to sprinkle in some code to make this simple example more versatile by
accepting an image as a url parameter or some other mechanism to serve out a
variety of image files instead of a single hard-coded one. For an ASP.NET application to effectively manage files, it
must have permission to access these files. By default, ASP.NET runs under a
user account (intuitively) named ASPNET. This user account has very limited
permissions. It will not be able to interact with most of the server s file
system 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, or
have ASP.NET use a different user account that does have the necessary
permissions. You can adjust the user account from within IIS, or you
can configure Impersonation in the web.config file or the machine.config file. For
initial experimentation and debugging I d suggest having ASP.NET run under your
user account because you know what files you have permission to access: "Redmond\BillG"
password="Melinda"/> If the images aren t stored in a file system, but instead
are stored in a SQL Server database, then the code behind for GenImage1.aspx
might 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 can
grab the image data from a database and write the raw file data directly into
the Output Stream just before it s sent to the browser. This technique shows how you can dump a file directly from
a database into the Response.OutputStream. ADO.NET is used to extract the
binary data from a SQL Server image field, the data is then converted into a
byte array, and, finally, it s written to the output stream along with a
descriptive header to help the browser better interpret the resulting file. For
more details on this technique, see Easy
Uploads. By using the functionality included in the System.Drawing
namespace, your image manipulation capabilities are limitless. As if that weren t
enough power for a single developer to wield, there are also dozens of third-party
components available under such categories as charting, reporting, and image
processing libraries. Additionally, you can build your own image processing
object models either from scratch or by building on existing technologies. Hopefully
by now you re beginning to realize the full power that can really lie behind
the seemingly humble image control. The previous techniques are great for distributing
pre-existing images, but if you need to dynamically create an image from
scratch (or modify an existing image on the fly,) then the System.Drawing
namespace will become quite familiar to you. Using the classes within this
namespace you could create dynamic charts, graphs, or other useful output. However,
that s soooo boring! The next example will focus on less tangible corporate
enhancements, such as improved morale. Smiles can be infectious, and the next example will
generate as many as you d like. Call the subroutine shown in Figure 3 to create
a 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 As
Integer = rand.Next(Width / 2) Dim SmileyHeight As
Integer = rand.Next(Height / 2) 'Draw the head (a big
circle) 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 New
Pen(RandomColor, PenWidth) g.DrawEllipse(Pen, x, y,
SmileyWidth, SmileyHeight) 'Draw the Nose (in the
center 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 As
System.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(New
Pen(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) As
System.Drawing.PointF points(0) = New
System.Drawing.PointF(CInt(x + _ (SmileyWidth / 2) -
(EyeRect.Width / 2) - _ (SmileyWidth / 4)), y +
(SmileyHeight / 2)) points(1) = New
System.Drawing.PointF(CInt(x + _ (SmileyWidth / 2)), y +
(SmileyHeight / 2) + _ (SmileyHeight / 4)) points(2) = New
System.Drawing.PointF(CInt(x + _ (SmileyWidth / 2) -
(EyeRect.Width / 2) + _ (SmileyWidth / 4)), y +
(SmileyHeight / 2)) g.DrawCurve(Pen, points,
1) End Sub Figure 3: By using
the classes within the System.Drawing namespace, nearly any illustration
imaginable can be generated at run time, including a bunch of smiley faces. The first parameter is a Graphics object, which is the
canvas on which this masterpiece will be painted. The height and width of the
canvas are also passed along, to help ensure no smileys get abruptly cut off at
the edges of the canvas. Finally, a Random object is passed along, which will
be used to mix things up a bit. Using the Random object, a random height and width are
generated for the current smiley face and the head is drawn within this
bounding rectangle. A pen is created of random thickness and color. This pen
will be used to draw most features of the face. The DrawEllipse method creates
a circle, which is used in concert with the FillEllipse method to fill it with
color. Three smaller circles are then drawn within the head to represent the
nose and two eyes. Finally, the smile is drawn by passing an array of points to
the DrawCurve method of the Graphics object. All of the mathematical formulas
throughout the example are there simply to calculate the position and size of
each facial feature. The final piece of this image generation puzzle is the
code that will fill the Page_Load event of GenImage1.aspx and call the
DrawSmiley 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: This
code goes in the Page_Load event of GenImage1.aspx, which can be referenced by
the ImageURL property of a standard image control placed on any other page. First, a few variables are declared with some default values
specifying the size of the image and the number of smiley faces that will be
drawn. Then the querystring is examined for optional parameters, which will
replace the defaults. A blank bitmap is then created with a white background. Antialiasing
is turned on to create smoother looking curves for rounded shapes, such as
circles and smiles. The main loop is then entered, iterating once for each
smiley face to be drawn by calling the DrawSmiley subroutine mentioned earlier.
Finally, the completed image is output by the DisplayImage subroutine in Figure
1. To see the code in action, create a new WebForm and drop
an Image control onto it. Then simply set the ImageURL property of that Image
control to point to the GenImage1.aspx page. The result will look a lot like
Figure 5. You should now have enough knowledge to manage and
manipulate images in all kinds of complex ways. The graphical possibilities are
endless with these tools at your disposal. You can expand on these ideas in all
kinds of ways. For example, you could create image buttons and other graphical
page 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, such
as: resizing, optimizing, cropping, rotating, adding borders, altering colors
and brightness, etc. The techniques outlined in this article are the foundation
for virtually every modern third-party graphing component available on the
market today. You could also create your own, if so inclined. Let your
imagination wander and let me know what kinds of image creation tools you
produce as a result. The sample code in
this article is available for download to asp.netPRO subscribers. Steve C. Orr is an
MCSD and a Microsoft MVP in ASP.NET. He s been developing software solutions
for leading companies in the Seattle
area for more than a decade. When he s not busy designing software systems or
writing about such activities, he can often be found loitering at local user
groups and habitually lurking in the ASP.NET newsgroup. Find out more about him
at http://Steve.Orr.net
or e-mail him at mailto:[email protected]. Custom Image Generation
Figure 5: The humble Image control
can turn into a powerful tool once you ve mastered the art of creating dynamic,
configurable images at run time.Conclusion