.NET and GDI+: Transforming Images on the Server - Introduction and Examples, Part II...

This article series focuses on the imaging elements of the GDI+ class available within the system.drawing namespace but also dips into the related graphics and typography facilities also available.


By: Chris Sully Date: March 5, 2004 Download the code.

Abstract

New to developers in the .NET Framework, and hence ASP.NET, is the ability to manipulate images through the GDI+ managed class interface (a set of wrappers) which is distributed as part of the Microsoft .NET Framework, allowing access to underlying operating system functionality that provides two-dimensional vector graphics, imaging, and typography. This article series focuses on the imaging elements of the GDI+ class available within the system.drawing namespace but also dips into the related graphics and typography facilities also available.

Introduction

In this article, part II in the series, we shall continue our exploration of GDI+ utilizing a series of examples to illustrate key concepts. Example 1: Displaying an Image was our first simple example following a consideration of the related theory in Article I. There follow examples 2 through 5.

2. Displaying a Thumbnail Image

GetThumbnailImage

A 'thumbnail' is a reduced sized image used to represent the full image without the hit of downloading the full sized image from the web server before it is actually required. This section introduces two methods of producing such as an image. Both will produce a 50 pixels by 50 pixels version of the original image as follows:

You can generate a thumbnail image via the exposed height and width properties of the Bitmap object, as follows:

gdi2_1.aspx code snippet:

'get original bitmap
dim myBitmap as New Bitmap(server.mappath("images/alima.jpg"))

'create thumbnail from the orginal bitmap
dim myThumbNail as New Bitmap(myBitmap, 50,50)

'Set the content type
response.contenttype="image/jpeg"

'send the thumbnail bitmap to the outputstream
myThumbNail.save(response.outputstream, imageformat.jpeg)

The above loads alima.jpg from the file system into a Bitmap (myBitmap) and proceeds to construct a new Bitmap from myBitmap utilising the constructor that allows you to specify the new image dimensions, which we specify as 50 pixels by 50 pixels.

As well as exposing the height and width properties of the Bitmap you can generate your own thumbnail image you can use GetThumbnailImage:

gdi2_2.aspx code snippet:

dim myBitmap1 as New Bitmap(server.mappath("images/alima.jpg"))
dim myThumbnail as Bitmap = myBitmap1.GetThumbnailImage(50, 50, nothing, IntPtr.Zero)

'Set the content type
response.contenttype="image/jpeg"
myThumbnail.save(response.outputstream, imageformat.jpeg)

As you can see from the above the GetThumbnailImage method takes 4 parameters:

Public Function GetThumbnailImage( _
  ByVal thumbWidth As Integer, _
  ByVal thumbHeight As Integer, _
  ByVal callback As Image.GetThumbnailImageAbort, _
  ByVal callbackData As IntPtr _
) As Image

This would appear to be one of the facilities in development in GDI+ v1.0 as the callback parameter is not currently utilized. It can be set to nothing as per the example. The value of callbackData must be set to IntPtr.Zero.

3. Rotating and/ or flipping an image

The following code rotates the image 90 degrees anti-clockwise before displaying it.

We start with alima.jpg as previously introduced and the result is:

gdi3.aspx code snippet:

dim bmpBitmap1 as New Bitmap(server.mappath("images/alima.jpg"))

'do the rotate/ flip
bmpBitmap1.RotateFlip(RotateFlipType.Rotate90FlipX)

'Set the content type
response.contenttype="image/jpeg"

'send the bitmap to the outputstream
bmpBitmap1.save(response.outputstream, imageformat.jpeg)

'tidy up
bmpBitmap1.dispose()

The key line here is:

bmpBitmap1.RotateFlip(RotateFlipType.Rotate90FlipX)

The RotateFlip method of the Bitmap class is inherited from the Image class and takes as its parameter one of the 16 members of the enumeration RotateFlipType. Rotate90FlipX specifies a 90-degree rotation followed by a horizontal flip equating to a 90-degree anti-clockwise rotation. You could also use Rotate270FlipNone to achieve the same effect. See the .NET SDK documentation for details of all the available members of the enumeration.

If you want a more flexible facility you have the RotateTransform method of the graphics object at your disposal. This allows you to rotate the coordinates of the object by the number of degrees you specify. We'll meet the graphics object and some of its methods in example 5 onwards, but won't be using RotateTransform.

4. Converting an image to a different image format

Converting an image from one format to another can be simple, though there are pitfalls to be aware of. In the first example we convert cwlogo.gif to a jpeg.

to the jpeg equivalent, which will look almost exactly the same apart from being a little fuzzier due to the nature of jpegs for this type of image.

gdi4_1.aspx code snippet:

dim bmpBitmap1 as New Bitmap(server.mappath("images/cwlogo.gif"))

'Set the content type
response.contenttype="image/jpeg"

'send the bitmap to the outputstream
bmpBitmap1.save(response.outputstream, imageformat.jpeg)

'tidy up
bmpBitmap1.dispose()

Here we are simply loading up an image of one imageformat and saving to the outputstream in another format. Of course you will need to consider the properties of the bitmap you are dealing with and the limitation and properties of the different image formats you are utilizing, e.g. ability to store differing numbers of colours.

If you try something similar with the Portable Network Graphics (PNG) format, however, an error will be generated: 'A generic error occurred in GDI+'. The solution follows:

gdi4_2.aspx code snippet:

dim bmpBitmap1 as New Bitmap(server.mappath("images/cwlogo.gif"))

'Set the content type
response.contenttype="image/png"

'send the bitmap to the outputstream
bmpBitmap1.save(response.outputstream, imageformat.png)

'tidy up
bmpBitmap1.dispose()

An error is returned as some image formats, including .png, can only be saved to 'seekable' streams and this excludes Response.OutputStream. 'Seeking' is the ability to examine any of the items in the stream. The solution is to use an intermediary stream that does support this ability, in this case a MemoryStream, as below:

gdi4_3.aspx code snippet:

dim bmpBitmap1 as New Bitmap(server.mappath("images/cwlogo.gif"))
dim memStream As New MemoryStream()

'Set the content type
response.contenttype="image/png"

'send the bitmap to the memory stream
bmpBitmap1.save(memStream, imageformat.png)

'and the memory stream to the output
memStream.WriteTo(response.outputStream)

'tidy up
bmpBitmap1.dispose()

5. Cropping Images

To crop an image we can utilize the Graphics and Rectangle classes with the system.drawing namespace.

The code below crops alima.jpg, who you will have already seen in '1. Displaying an image' to

gdi5.aspx code snippet:

'get original bitmap
dim myBitmap as New Bitmap(server.mappath("images/alima.jpg"))

'create thumbnail from the original bitmap
dim myBitmapCropped as New Bitmap(200,100)
dim myGraphic = Graphics.FromImage(myBitmapCropped)

'crop to the graphic object from the original bitmap
myGraphic.DrawImage(myBitmap, new Rectangle(0,0,myBitmapCropped.Width,myBitmapCropped.Height),100,100,myBitmapCropped.Width,myBitmapCropped.Height,GraphicsUnit.Pixel)
myGraphic.dispose()

'Set the content type
response.contenttype="image/jpeg"

'send the cropped image to the outputstream
myBitmapCropped.save(response.outputstream, imageformat.jpeg)

'tidy up
myBitmap.dispose()
myBitmapCropped.dispose()

Above we define a new Bitmap object of size 200 by 100 pixels and use this to create a new graphics object using its FromImage method (remembering that Bitmap is a child class of Image). Next we use the DrawImage method of the graphics object to perform the cropping from the original bitmap with the aid of a rectangle object that specifies the size of the rectangle from the original Bitmap to be cropped:

new Rectangle(0,0,myBitmap_cropped.Width,myBitmap_cropped.Height

i.e. 200x100

with the additional parameters of this overloaded DrawImage method as follows:

myGraphic.DrawImage(myBitmap, new Rectangle(0,0,myBitmap_cropped.Width,myBitmap_cropped.Height),100,50, myBitmap_cropped.Width,myBitmap_cropped.Height,GraphicsUnit.Pixel)

Graphics.DrawImage Method (Image, Rectangle, Int32, Int32, Int32, Int32, GraphicsUnit):

Overloads Public Sub DrawImage( _
  ByVal image As Image, _
  ByVal destRect As Rectangle, _
  ByVal srcX As Integer, _
  ByVal srcY As Integer, _
  ByVal srcWidth As Integer, _
  ByVal srcHeight As Integer, _
  ByVal srcUnit As GraphicsUnit _
)

where GraphicsUnit specifies the unit of measure for the given data, here pixels. This is the only sensible enumeration value for the given output device. Alternative values include inch and millimeter.

This is one of 30 overloaded method signatures of the DrawImage method for you to experiment with!

That concludes article II. In the third article of this series we'll continue to look at features of GDI+ via further examples.

References

ASP.NET: Tips, tutorials and code
Mitchell et al.
Sams Publishing
ISBN 0-672-32143-2

.NET SDK Documentation

Various online articles, notably:

The Code Project - An ASP.NET thumbnail solution - ASP.NET
http://www.codeproject.com/aspnet/ThumbTools.asp

Chris Garretts ASPAlliance Column:
http://www.aspalliance.com/chrisg/

Bob Powell’s GDI+ FAQ
http://www.bobpowell.net/

http://www.c-sharpcorner.com/Code/2002/April/DrawTransparentImageUsingAB.asp
C# Corner: C# and .NET Developer's Network: transparent images
Mahesh Chand

http://www.c-sharpcorner.com/Code/2002/Dec/WorkWithPNG.asp
Working with the Portable Network Graphics (PNG) Format: Part 1
Tom Curry

http://www.c-sharpcorner.com/graphics/transforms_101.asp
Using Transforms with GDI+ in C#
Mike Gold

You may download the code here.