To read the article online, visit http://www.4GuysFromRolla.com/articles/042909-1.aspx

Image Transforms with the ASP.NET Generated Image Control

By Scott Mitchell


Introduction


Last week's article - Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control - looked at how to programmatically create and cache images on the fly using the GeneratedImage control, one of a suite of "Futures" technologies found in the ASP.NET Team's CodePlex site. Programmatically creating and displaying images entails the use of an HTTP Handler for generating the binary content of the dynamically-generated image. While this HTTP Handler can be implemented as an ASP.NET page, the GeneratedImage control includes the ImageHandler class, which serves as a base class for HTTP Handlers designed specifically to generate and serve dynamic images and includes built-in functionality for caching images on the client and/or web server. Last week's article illustrated both how to create an HTTP Handler for generating dynamic images (one that extended ImageHandler) as well as how to use the GeneratedImage Web control to display such dynamically-generated images in a web page.

In addition to its image generation and caching features, the ImageHandler class includes functionality for performing image transforms. An image transformation is a process that takes an image as input, modifies it in some way, and outputs the modified image. Common image transformations include image resizing, changing the image's color information (such as transforming a color image into a black and white one), and adding a watermark to the image. What's more, transformations can be chained together. For example, you could take an original image and feed in into the resize transformation. The output of the resize transformation could then be used as the input of the watermark transformation, which would then output the resized image with a watermark.

The GeneratedImage control ships with an image transform class that resizes the original image, as well as an ImageTransform class that serves as a base class for creating your own image transform classes. This article illustrates how to use resize dynamically-created images on the fly as well as how to create your own image transform classes. Read on to learn more!

Applying Image Transforms


The ImageHandler class, which serves as a base class for the HTTP Handler classes used to dynamically generate images using the GeneratedImage control, includes a property named ImageTransforms that specifies what image transforms (and in what order) are applied to the generated image. Specifically, the ImageTransforms property holds a collection of objects that derive from the ImageTransform class. The ImageTransform class is another class in the GeneratedImage API that serves as a base class for classes that can perform image transforms. The API includes a class named ImageResizeTransform, which extends ImageTransform. (We'll look at using the ImageResizeTransform shortly, and later we'll see how you can create your own custom image transform classes).

To apply an image transform to a dynamically-generated image, simply add an instance of the image transform class to the ImageTransforms property in either the HTTP Handler's constructor or in the GenerateImage method. For instance, the following HTTP Handler snippet applies two image transform classes to the image generated by the HTTP Handler.

Public Class ApplyImageTransformsHandler
   Inherits ImageHandler
   
   Public Sub New()
      MyBase.ContentType = Imaging.ImageFormat.Jpeg
      
      ' Apply the image transform classes      
      MyBase.ImageTransforms.Add(New SomeImageTransformClass)
      
      Dim someOtherTransform As New SomeOtherImageTransformClass()
      someOtherTransform.SomeProperty = 5
      ...
      MyBase.ImageTransforms.Add(someOtherTransform)
   End Sub
   
   Public Overrides Function GenerateImage(ByVal parameters As System.Collections.Specialized.NameValueCollection) As Microsoft.Web.ImageInfo
      ...
   End Function
End Class

As you can see from the code above, you can apply any number of image transform classes to the ImageHandler class's ImageTransforms property. If the image transform class does not require that you set any property values, you can instantiate the image transform class directly in the call to the ImageTransforms property's Add method, as in MyBase.ImageTransforms.Add(New SomeImageTransformClass). More likely, you'll need to first set a variety of properties of the image transform class. In that case, start by creating an instance of the image transform class, set its properties, and then add the instance to the ImageTransforms collection.

Keep in mind that the specified image transforms are applied after the image is generated by the GenerateImage method. Furthermore, the transforms are applied in the order in which they appear in the ImageTransforms list. In other words, with the above example the GenerateImage method is responsible for creating the original image. Next, the SomeImageTransformClass image transform class is applied to the original image. It's output is then fed as input into the SomeOtherImageTransformClass image transform class and that output is what is returned to the client.

Resizing Images With The ImageResizeTransform Class


The GeneratedImage API ships with a built-in image transform class, ImageResizeTransform. As its name implies, ImageResizeTransform resizes the original image to an alternate size. This is particularly useful in websites where there are commonly very large, high-resolution pictures that need to sometimes be shown in a smaller, more compact view.

Consider a website like Flickr, which allows visitors to upload their digital pictures. Typically, digital pictures are very large, both in terms of file size and dimensions. For example, a digital picture might be more than 2,000 pixels wide by 1,000 pixels tall and consume upwards of 1 MB of disk space. Clearly, displaying such images on your website will not only stretch out your pages, but also greatly impact the total bandwidth used by the site. When you sign on to Flickr you see your picture library as a series of resized images that do not exceed 240 pixels in width, thereby permitting multiple columns of pictures to be listed on a single page. While the ASP.NET Image web control has Width and Height properties that let you specify the dimensions of the image on the browser, these properties do not actually resize the image. Instead, the web server still transmits the entire contents of the original image, which the browser then scales to the specified width and height. In order to reduce both the dimensions and the file size, you need to resize the image.

If you have a predetermined, known quantity of high-resolution images, you can proactively create smaller versions of these images using an image editing program like Photoshop. But if your site has user-added pictures or dynamically-generated images, and you need to resize these source images into smaller versions, then you'll need to resize the image dynamically. This is where the ImageResizeTransform class can help.

The following code snippet shows an HTTP Handler that extends the ImageHandler class and uses the ImageResizeTransform class to resize an image file to a width of 200 pixels. (The resized image's height is automatically scaled to the appropriate height.) Specifically, this code resizes an image file that resides on the web server's file system (note that the URL to the image to resize is specified via the parameters collection), although the resize transform could be applied to a dynamically-generated image.

Public Class ResizeImageHandler
   Inherits ImageHandler

   Public Sub New()
      MyBase.ContentType = Imaging.ImageFormat.Jpeg
      
      Dim resizeTrans As New ImageResizeTransform
      resizeTrans.Width = 200
      MyBase.ImageTransforms.Add(resizeTrans)

   End Sub

   Public Overrides Function GenerateImage(ByVal parameters As System.Collections.Specialized.NameValueCollection) As Microsoft.Web.ImageInfo
      'Get the parameters
      Dim imageUrl As String = parameters("ImageUrl")
      Dim imageFile As String = HttpContext.Current.Server.MapPath(imageUrl)

      If Not File.Exists(imageFile) Then
         Throw New ArgumentException(String.Format("The file {0} could not be found", imageFile))
      End If

      Return New ImageInfo(File.ReadAllBytes(imageFile))
   End Function
End Class

In the HTTP Handler's constructor an instance of the ImageResizeTransform class is created and its Width property is set to 200, which resizes the resulting image to 200 pixels with the height auto-scaled. The GenerateImage method reads in the ImageUrl parameter, which contains the path to the image to resize. This parameter contains the virtual path to the image, such as ~/Images/Picture.jpg. In order to load the image we need to know the physical path, such as C:\MyWebsite\Images\Picture.jpg. This translation from virtual to physical paths is performed by the Server.MapPath method and saved in the imageFile variable. Next, a check is performed to ensure that the file exists; if it does not, an ArgumentException is thrown. However, if the image exists then its bytes are read via the File.ReadAllBytes method and passed into the constructor for a new ImageInfo object, which is what the GenerateImage method must return.

After the GenerateImage method completes, the ImageHandler applies the ImageResizeTransform transform, which resizes the specified image to have a width of 200 pixels. This resized image is what is returned to the requesting client.

The download available at the end of this article includes a more refined resizing HTTP Handler, one where the width or height of the resized image can also be specified via the parameters collection. This HTTP Handler can be found in the demo at ~/ImageHandlers/ResizeImageHandler.ashx; there's also a web page named ~/Demos/TransformDemo.aspx that contains a GeneratedImage Web control with the following declarative markup:

<cc1:GeneratedImage ID="ResizedImageGenerator" runat="server" ImageHandlerUrl="~/ImageHandlers/ResizeImageHandler.ashx">
   <Parameters>
      <cc1:ImageParameter Name="ImageUrl" Value="~/Images/Hiking.jpg" />
      <cc1:ImageParameter Name="Width" Value="400" />
   </Parameters>
</cc1:GeneratedImage>

The original image, Hiking.jpg, an original resolution of 2589 x 1249 and over 940 KB in size and resizing it to 400 x 193 pixels with a total file size of 32 KB. To see this resizing in action, download the demo or check out the original picture and compare it with the resized one below.

Creating a Custom Image Transform Class


The ImageResizeTransform class derives from the ImageTransform class, which spells out the base functionality of a class that performs image transforms. Specifically, the ImageTransform class includes an abstract method (one that must be overridden) named ProcessImage, which is responsible for performing the transform. The ProcessImage method is passed an Image object as input and returns the transformed image as its output (also as an Image object).

It is possible to create your own image transform classes and to use them in your HTTP Handlers. The demo available for download at the end of this article includes a custom image transform class named ImageWatermarkTransform. As its name implies, ImageWatermarkTransform transforms the input image by adding a watermark. This image transform class contains properties like WatermarkText, FontColor, FontFamily, and other properties that affect the display and rendering of the watermark. In the ProcessImage method it takes the input image and adds the watermark, locating it in the center of the image.

The following code snippet shows the germane parts of the ImageWatermarkTransform class, namely the ProcessImage method. This method starts by creating a Font object based on the values of the class's FontFamily and FontSize properties. Next, a Graphics object is created to work on the input image (image). The size of the watermark is measured; next, the coordinates to place the watermark and the center of the image are computed. Finally, the watermark is added to the image via the DrawString method, and the modified image is returned.

Public Class ImageWatermarkTransform
   Inherits ImageTransform

   ... Properties removed for brevity ...

   Public Overrides Function ProcessImage(ByVal image As System.Drawing.Image) As System.Drawing.Image
      Dim WatermarkFont As New Font(Me.FontFamily, Me.FontSize)

      Dim myGraphics As Graphics = Graphics.FromImage(image)

      Dim sz As SizeF = myGraphics.MeasureString(Me.WatermarkText, WatermarkFont)

      Dim X As Single = image.Width / 2 - sz.Width / 2
      Dim Y As Single = image.Height / 2 - sz.Height / 2

      myGraphics.DrawString(Me.WatermarkText, WatermarkFont, New SolidBrush(FontColor), X, Y)

      Return image
   End Function
End Class

In addition to creating this image transform class, I also created an HTTP Handler named WatermarkAndResizeImageHandler.ashx that uses both the ImageWatermarkTransform and ImageResizeTransform transform classes to first (optionally) resize an image and then to (optionally) add a watermark. The code is quite similar to that of the ResizeImageHandler.ashx HTTP Handler, which we examined earlier in this article. The only difference is the addition of the ImageWatermarkTransform class, which is highlighted in the snippet below.

Public Class WatermarkAndResizeImageHandler
   Inherits ImageHandler
   
   ...
   
   Public Overrides Function GenerateImage(ByVal parameters As System.Collections.Specialized.NameValueCollection) As Microsoft.Web.ImageInfo
      ... The ImageResizeTransform class is added first (if needed) ...
      
      ' Add the watermark transform logic (if needed)
      If Not String.IsNullOrEmpty(parameters("WatermarkText")) Then
         Dim watermarkTrans As New ImageWatermarkTransform
         watermarkTrans.WatermarkText = parameters("WatermarkText")
         
         If Not String.IsNullOrEmpty(parameters("WatermarkFontFamily")) Then
            watermarkTrans.FontFamily = parameters("WatermarkFontFamily")
         End If
         If Not String.IsNullOrEmpty(parameters("WatermarkFontColor")) Then
            watermarkTrans.FontColor = ColorTranslator.FromHtml(parameters("WatermarkFontColor"))
         End If
         If Not String.IsNullOrEmpty(parameters("WatermarkFontSize")) Then
            watermarkTrans.FontSize = Convert.ToSingle(parameters("WatermarkFontSize"))
         End If
         
         MyBase.ImageTransforms.Add(watermarkTrans)
      End If


      ...      
   End Function
End Class

The WatermarkDemo.aspx page uses the GeneratedImage Web control to display the Hiking.jpg photo resized to 350 pixels wide (and auto-scaled to 168 pixels tall) with the watermark "Copyright Scott Mitchell" prominently displayed using a yellow, Comic Sans MS font of size 18pt.

Keep in mind that the image transforms are applied in the order they appear in the ImageTransforms list. In the above example, the image is first resized and then a watermark is added. However, if you reversed the order with which the image transform classes are added to the ImageTransforms property then the image would first have a watermark added and then be resized.

Conclusion


The ASP.NET GeneratedImage control makes it easy to work with dynamically-generated images. This article (and last week's article) showed how to use this control and the classes that make up its API to serve dynamic images, cache them, and apply image transforms. The GeneratedImage control ships with one built-in image transform class, ImageResizeTransform. You can also create your own custom image transform classes, as this article illustrated.

Happy Programming!

  • By Scott Mitchell


    Attachments:


  • Download the Demo Code Used in this Article

    Further Reading:


  • Download the GeneratedImage Control
  • Dynamically Generating and Caching Images in ASP.NET with the GeneratedImage Control
  • Article Information
    Article Title: ASP.NET.Image Transforms with the ASP.NET Generated Image Control
    Article Author: Scott Mitchell
    Published Date: April 29, 2009
    Article URL: http://www.4GuysFromRolla.com/articles/042909-1.aspx


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers