Skip navigation

Improve Your Image(s)

Master Image Processing and Management

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.

 

Custom Image Generation

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.

 


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

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

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish