.NET and GDI+: Transforming Images on the Server - Introduction and Examples, Part III...
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.
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.
In this article, part III in the series, we shall continue our exploration of GDI+ utilizing a series of examples to illustrate key concepts. We continue with examples 6 and 7.
Killing two birds with one stone, this example is going to show how to manipulate the Bitmap image by adding both text and a simple vector graphic element to the image. As per the cropping example we utilize the Graphics class to do this. For this example we also need the system.drawing.drawing2D namespace. Before we move onto the code this is the image it outputs:
gdi6.aspx code snippet:
|
dim myBitmap as New bitmap(server.mappath("images/alima.jpg")) dim myGraphic As Graphics = Graphics.FromImage(myBitmap) 'set the smoothing mode myGraphic.SmoothingMode = SmoothingMode.AntiAlias 'draw the oval myGraphic.DrawArc(New Pen(Color.White, 2), 280, 0, 120, 50, 0, 360) 'draw the text myGraphic.DrawString("Alima!", New Font("Helvetica", 30, FontStyle.Bold), SystemBrushes.WindowText, New PointF(290, 10)) 'note: enable these 3 lines and disable the previous if you want to see vertical text in action 'Dim stringFormat As New StringFormat() 'stringFormat.FormatFlags = StringFormatFlags.DirectionVertical 'myGraphic.DrawString("Alima!", New Font("Helvetica", 30, FontStyle.Bold), SystemBrushes.WindowText, New PointF(290, 10), stringFormat) 'set the content type response.contenttype="image/jpeg" 'save to the outputstream myBitmap.Save(response.outputstream, ImageFormat.jpeg) myGraphic.Dispose() myBitmap.Dispose() |
Examining the code:
|
dim myBitmap as New system.drawing.bitmap(server.mappath("images/alima.jpg")) dim myGraphic As Graphics = Graphics.FromImage(myBitmap) |
is as per the previous example.
| myGraphic.SmoothingMode = SmoothingMode.AntiAlias |
sets how any vector graphics will be rendered onto the image. Here we set the mode to anti-aliased via the appropriate member of the Graphics.SmoothingMode property enumeration. This does not affect how text is rendered. Text rendering is specified via the TextRenderingHint enumeration of the System.Drawing.Text namespace (not done in this example).
| myGraphic.DrawArc(New Pen(Color.White, 2), 280, 0, 120, 50, 0, 360) |
draws the oval using a pen object of colour white with a line width of 2 pixels.
DrawArc has four overloaded constructors. Here we are using:
|
Overloads Public Sub DrawArc( _ ByVal pen As Pen, _ ByVal x As Integer, _ ByVal y As Integer, _ ByVal width As Integer, _ ByVal height As Integer, _ ByVal startAngle As Integer, _ ByVal sweepAngle As Integer _ ) |
which draws an arc representing a portion of an ellipse specified by a pair of coordinates, a width, and a height the start angle and the 'sweep' of the curve. So, in the example, we specifying a starting position of the curve of (280, 0) which is the top center point of the ellipse, with the width and height of 120 and 50 effectively defining a bounding rectangle for the curve, or ellipse in this case. The start angle is 0 degrees, which is by convention 3 o'clock, and the sweep 360 degrees - so all the way round to the same position again. I suggest you experiment with the parameters to gain a feel of how to control the output.
Next we add the text:
| myGraphic.DrawString("Alima!", New Font("Helvetica", 30, FontStyle.Bold), Color.Black, New PointF(290, 10)) |
Here we add the text "Alima!" to the myGraphic object, specifying the following to do so
As an aside, to draw vertical text you use the StringFormat class. Amongst other functionality this class encapsulates text layout information (such as alignment and line spacing). To accomplish this you set the FormatFlags property to the appropriate member of the StringFormatFlags enumeration after instantiating the object:
|
Dim stringFormat As New StringFormat() stringFormat.FormatFlags = StringFormatFlags.DirectionVertical |
and use an alternate overloaded method of the drawstring method:
| myGraphic.DrawString("Alima!", New Font("Helvetica", 30, FontStyle.Bold), Color.Black, New PointF(290, 10), stringFormat) |
This is included in the example, albeit commented out, for your experimentation.
As you are probably well aware the most popular format for photos on the web is JPEG. Actually, strictly, JPEG is a compression algorithm, not a file format. JPEG File Interchange Format (JFIF) is the file format commonly used for storing and transferring images that have been compressed according to the JPEG scheme. JPEG employs a lossy algorithm for image compression, i.e. some information is lost in the compression. One parameter to the compression is a quality metric varying from 100% down to 0. Deciding on the value of the metric to use is dependent on several factors, not least of which are the quality of the original image and the destination display mechanism. For web sites, where the original is of good quality, a suitable value is between 60-80%. Much below 60% the differences become more evident. In the following example you can see the 75% image is far from perfect but this is partly due to the quality of the original image. More important is the quality differences between the different jpgs.
ImageA: 75% (13K)
ImageB: 50% (8K)
ImageC: 25% (6K)
To produce the above images the following code was used:
gdi7.aspx code snippet:
|
Const intQuality=25 dim bmpBitmap1 as New Bitmap(server.mappath("images/alima.jpg")) 'Get the list of available encoders Dim codecs() As ImageCodecInfo = ImageCodecInfo.GetImageEncoders() 'find the encoder with the image/jpeg mime-type dim iciInfo as ImageCodecInfo dim item as ImageCodecInfo for each item in codecs if(item.MimeType="image/jpeg")then iciInfo=item next 'Create a collection of encoder parameters (we only need one in the collection) Dim ep as EncoderParameters = new EncoderParameters() ep.Param(0)= new system.drawing.imaging.EncoderParameter(system.drawing.imaging.Encoder.Quality,intQuality) 'Set the content type response.contenttype="image/jpeg" 'bmpBitmap1.save(server.mappath("." & "/article_images/alima25.jpg"), iciInfo, ep) 'send the bitmap to the outputstream bmpBitmap1.save(response.outputstream, iciInfo, ep) 'tidy up bmpBitmap1.dispose() |
Note that the quality metric is set via the constant intQuality. This was run three times with values of 25, 50 and 75 to produce the output presented. After loading the original image from the file system we locate the information we need within the ImageCodecInfo class and store it in iciInfo for later use:
|
Dim codecs() As ImageCodecInfo = ImageCodecInfo.GetImageEncoders() 'find the encoder with the image/jpeg mime-type dim iciInfo as ImageCodecInfo dim item as ImageCodecInfo for each item in codecs if(item.MimeType="image/jpeg")then iciInfo=item next |
We then create a new EncoderParameters collection so that we may instantiate and later reference the Encoder.Quality parameter. This is done via an overloaded save method of the Bitmap class:
| bmpBitmap1.save(response.outputstream, iciInfo, ep) |
If you consider the above you will realize that there is the possibility of extending the ImageCodec related classes via your own classes so as to add further image format support to the Framework.
That concludes article III. In the fourth article of this series we'll continue to look at features of GDI+ via further examples.
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.