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