Display Dynamic Images
Crop, scale, zoom, and pan — all with ASP.NET.
October 30, 2009
asp:feature
LANGUAGE: C#
TECHNOLOGIES: ImageManipulation | System.Drawing Classes
DisplayDynamic Images
Crop,scale, zoom, and pan - all with ASP.NET.
By BillWagner
Chances are, none of your Web applications are made ofsimple text anymore. Instead, your Web visitors want to see picturesof everything they're working with. Sometimes you simply can load these imagesfrom a database and deliver them to users. Other times, however, you need tocreate the images on the fly. Luckily, this is easy to do with ASP.NET becauseit lets you use all the new graphics features in the .NET Framework and theGraphics Device Interface (GDI+). In this article, I'll show you how to useASP.NET to generate graphics images on the fly. You'll build a sample based onan archiving system I helped develop for a library, which loads full-pageimages from old texts. These images are cropped, scaled, and highlighted.Finally, the modified images are delivered to the client.
A Simple Demonstration
This article's demo application shows you how wemanipulated the images and delivered them to the client. Figure 1 shows thepage I created to demonstrate dynamic image manipulation in ASP.NET.
Figure 1. The buttons on this page let you create and load one of thesample dynamic images.
The page contains three buttons that will show you part ofthe image; regardless of which you click on, the resulting image is createdfrom the source image. Below the buttons, you can see an image tag that willdisplay the dynamic images. The full system stores the images, the coordinatesfor articles of interest, and keywords in a database. The sample for thisarticle, however, works only with one image and shows you different regions ofthe image with some parts of it highlighted. Figure 2 shows you the Web pagewith the thumbnail image displayed.
Figure 2. This thumbnail image is created from the full-sized sourceimage. Users look at the thumbnail to determine if they have the right imagebefore loading the full article it represents.
Each request follows the samesteps:
Load the source image.
Apply whatever transformations are necessary to the sourceimage.
Save the modified image on the server.
Modify the page to load the dynamic image.
All these features make use of the .NET Framework's System.Drawingclasses, especially System.Drawing.Image. The Image class is anabstract class, so you create instances of System.Drawing.Bitmap, aconcrete class derived from the Image class. The Bitmap class hasa constructor that takes a file and loads the image. Every request starts withloading this sample image:
string Path =Request.PhysicalApplicationPath;
System.Drawing.Bitmapim = new System.Drawing.Bitmap
(Path+"\test.png");
The Request.PhysicalApplicationPath propertyreturns the physical directory that maps to the application's virtual directoryon the Web server. I often use this instead of the Server.MapPathmethod, but they both accomplish the same basic task. I chose to put my sourceimage in the virtual directory of my Web application. I'll get into this moreat the end of the article, along with some shortcuts for a small, downloadablesample.
Create the Thumbnails
The first dynamic image to create is a thumbnail of thefull page. The Image class contains a method to do just that: GetThumbnailImage.You pass this method the size of the thumbnail, a delegate, and a pointer to abitmap surface. In this version of the .NET Framework, neither the delegate northe IntPtr parameter is used, but both must be present. The IntPtrmust be IntPtr.Zero, which is the same as NULL, for the functionto work correctly:
private boolthumbCallback ()
{
return false;
}
// Make a newone, only much smaller:
Size sz = newSize (im.Width / 20, im.Height / 20);
System.Drawing.Image thumb = im.GetThumbnailImage
(sz.Width, sz.Height,
newSystem.Drawing.Image.GetThumbnailImageAbort
(thumbCallback), IntPtr.Zero);
Once you have the thumbnail, you need to save it somewhereon the Web server so you can deliver it to the client. The Bitmap classcontains a Save method that writes a variety of formats. Your Web servermight be processing multiple requests from different users concurrently, so youneed to save your dynamic images with names guaranteed to be unique acrossusers and unique for the same user across time. I did this by creating theimage file name from the session ID and the current ticks in milliseconds. Byconcatenating the session ID and the number of ticks since 12 a.m. on Jan 1,0001, you can be sure to have a unique filename:
private stringGetImageFileName ()
{
System.Text.StringBuilder name = newSystem.Text.StringBuilder ();
name.Append (Session.SessionID);
name.Append(System.DateTime.Now.Ticks.ToString());
name.Append (".png");
return name.ToString ();
}
To keep it simple, I saved these dynamic images in theroot directory of the application:
string name =GetImageFileName ();
string fullPath= Path + "\" + name;
thumb.Save(fullPath);
// Load theimage tag:
DynamicImage.ImageUrl= name;
Figure 2 shows the results. In the original application,this let users see small versions of complex graphics images before spendingtheir time generating and downloading larger, higher-resolution versions. Bygenerating the thumbnails dynamically, the original application needed only tostore the full high-resolution images in the database. All the smaller imageswere generated as needed.
Download a Section of an Image
The small thumbnails help users see if they have thecorrect page, but I doubt anyone can read these. So, it's time to create a moreuseful image containing a single article from a printed page (see Figure 3). Icreated large, high-resolution images for the sources, and I shrank them to areasonable size for ease of use. I also drew borders around the selectedarticle. These scaling and drawing operations give you an inkling of thedifferent features you can add when you need to deliver images to your Webclients.
Figure 3. This image was created by cropping the full image and scalingit to a size that is easy to read.
After loading thesource image, create a new bitmap the size of the crop rectangle in the sourceimage, divided by two. This way, it will hold the section of the original imagescaled to 50 percent. The source image is 300 dpi, and the code sets all thefinal images to the same resolution:
System.Drawing.Bitmap crop = new System.Drawing.Bitmap
(cropRect.Width/2, cropRect.Height/2,im.PixelFormat);
crop.SetResolution(300F, 300F);
Now that you have created the destination image, it's timeto put the right bits in it. The System.Drawing.Graphics class has astatic method that creates a new Graphics context that lets you draw onthe image using GDI+ graphics methods. First, draw the selected region of thesource image on the destination image, scaling down by 50 percent. The simplestway to do this is to use the Graphics.DrawImage method:
System.Drawing.Graphicsg1 = Graphics.FromImage (crop);
RectangledestRect = new Rectangle (0,0, cropRect.Width/2,
cropRect.Height/2);
g1.DrawImage(im, destRect, cropRect.X, cropRect.Y,
cropRect.Width,cropRect.Height,GraphicsUnit.Pixel );
After the source image has been copied to the new image,use the Graphics.DrawRectangle method to draw a border around theselected article:
System.Drawing.PenhighlightPen = new Pen (Brushes.Green, 5F);
g1.DrawRectangle(highlightPen, 0,0, crop.Width-3, crop.Height-3);
You can see the final results in Figure 4. The pagecontains a cropped version of one section of the original image, with thehighlight drawn around it. By using similar techniques, you can annotate yourown images for your Web users.
Highlight Images
The third button shows a mixture of raster and vectorgraphics capabilities. After creating a destination image that contains theselected article, you need to add the highlight rectangle. The highlightrectangle is an interesting feature. If a pixel in the highlight area is white,you draw it; if it's black, you skip it.
None of my source images had an alpha channel (fortransparency), so I had to do the highlighting myself. When you scan an image,not every white pixel ends up exactly white. Any number of imperfections causepixels to become a light shade of gray. In the same sense, not every blackpixel is black; some are dark shades of gray. You need to generate thehighlight by examining the brightness of each pixel in the highlight rectangle.If a pixel is close to white, change the pixel to the highlight color. The .NETFramework makes this simple, as well. You can get the color of a pixel by usingthe GetPixel method. The pixel values are Color objects. The Colorclass provides a method to get the brightness of the pixel. Simply examine thatvalue and modify the pixel using SetPixel if the value is close towhite:
for (int y =highlightRect.Y; y < highlightRect.Bottom; y++)
{
for (int x = highlightRect.X; x { Color c = crop.GetPixel (x, y); if (c.GetBrightness () > 0.5F) crop.SetPixel (x,y, Color.Pink); } } You can see the final resultsin Figure 4.
Figure 4. When a user has been searching for particular words andphrases, articles return with that text highlighted. You can see how to mix raster and vector operations tocreate your final images. The borders are drawn using vector operations on the Graphicsobject. The highlights are drawn by modifying pixels if they're closer to whitethan black. Clean Up the Server All these actions create a variety of temporary images onthe Web server. At some point, you'll need to delete all these images. Youcan't get rid of them too early, or your users might not receive them; but ifyou wait too long, you'll have many useless images on your Web server. I delete all the temporary images when a user sessionends. I store the list of image filenames in the session state collection in a System.Collections.ArrayListobject. When the page first loads, I see if the session state contains this ArrayList.If it exists, I get the reference to it, but if not, I create it. Anytime Icreate a new dynamic image, I store the full local path name in the sessioncollection. Later, when the session ends, I delete all these temporary images. The Global.asax.cs file contains a Session_Endevent handler already. All you need to do is add the code to that method toretrieve the list of files from the session object and delete each file. Thedefault timeout value for an ASP.NET application is 20 minutes. To test thiscleanup operation, you can shorten the timeout: Open the web.config file foryour application, find the SessionState element, and modify the timeoutvalue. The value is specified in minutes; I used two minutes for most of mytesting. Here is one important caveat to this strategy: When yourun your application in the VS .NET debugger, the SessionEnd method doesnot get called. The VS .NET process ends the Web server process, then you exitthe browser window. So if you are running the sample in the debugger, thecleanup code might not run. To circumvent this problem, leave the browserwindow open until the session times out. As I said earlier, I took some shortcuts for this sample,involving where to find the source images and where to put the dynamic imagescreated for the user. You should place the source images in a directory notshared as part of the Web site to prevent users from hacking around and findingthem. You should place the dynamic images in a shared directory on the Webserver, but in a subdirectory rather than the root application directory. Whenyou need to remove image files by hand because a session did not time outproperly, it is much easier if those images are in their own directory.Finally, the full production version uses high-resolution images to produce thecrispest possible images at the client, after they are reduced. I did not wantyou to have to download a 7 mb image to see the sample. So for this sample, thetest image is a much lower resolution than the production version of theapplication. The files referenced in thisarticle are available for download. Bill Wagner is SRT Solutions' Windows technology expert (http://www.SRTSolutions.com). He is theauthor of C# CoreLanguage Little Black Book (The Coriolis Group), an advanced referencefor C# developers. Bill has an extensive background in 2-D and 3-D graphics andmultimedia software, including developing the video-playback engine used forDisney's "The Lion King Animated Storybook." E-mail Bill at mailto:[email protected].
About the Author
You May Also Like