.NET and GDI+: Transforming Images on the Server - Introduction and Examples, Part IV...
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 available.
In this article, part IV in the series, we shall continue our exploration of GDI+ utilizing a series of examples to illustrate key concepts. We continue with examples 8 and 9.
This example looks at how to handle transparent images in GDI+. The technique to achieve this called alpha blending. Alpha blending is the process of mixing colours to generate a transparent affect.
As discussed earlier, in GDI+ a colour can be represented by four components: Alpha, Red, Green and Blue (ARGB). The Alpha component 'of a colour' represents transparency. 'Of a colour' is placed in quotes as transparency is perhaps better considered to be a property of the entity being created from the colour, rather than the colour itself. The value of each of these components varies from 0 to 255. For the Alpha component a value of 0 means full transparency and 255 means opaque. To achieve transparency you therefore pass a colour with the transparency value set to the object. You then create the shapes on a bitmap via a graphics object, as per the example that produces the following output:

Here we've overlaid several objects: a sequence of 10 pixel wide lines of increasing transparency/ increasing opacity from top to bottom, a rectangle and an ellipse of differing transparencies as well as some semi-transparent text as well. The following code achieved this:
gdi8.aspx code snippet:
|
dim myBitmap2 as New Bitmap(400,200) dim myGraphic = Graphics.FromImage(myBitmap2) 'load an image dim myBitmap1 as New Bitmap(server.mappath("images/alima.jpg")) myGraphic.DrawImage(myBitmap1, 0, 0, myBitmap1.Width, myBitmap1.Height) 'create a rectangle dim rect as Rectangle = new Rectangle(200, 30, 180, 50) dim opacity as integer=255 dim loopy as integer 'draw the 10 pixel wide lines down the image, via a loop, increasing the 'transparency as we go for loopy=10 to myBitmap1.height-30 step 30 dim transPen as Pen = new Pen(Color.FromArgb(opacity, 0, 255, 255), 10) opacity=opacity-32 if opacity<0 then opacity=0 end if myGraphic.DrawLine(transPen, 10, loopy, 200, loopy) next 'produce the rectangle and ellipse myGraphic.FillRectangle(new SolidBrush(Color.FromArgb(100, 0, 128, 255)), rect) rect.Y += 50 myGraphic.FillEllipse(new SolidBrush(Color.FromArgb(50, 255, 255, 255)), rect) dim semiTransBrush as SolidBrush = new SolidBrush(Color.FromArgb(128, 255, 255, 50)) myGraphic.DrawString("A photo!", new Font("Verdana", 14), semiTransBrush, new RectangleF(270, 170, 340, 170)) |
Examining the above, and not going into too much detail, as you will have come across much of the above in the earlier examples, we use the graphics object as a working canvas. To produce the series of increasingly transparent (/decreasingly opaque) lines from top to bottom of the image we loop through the y dimension of the image with a step size of 30 decreasing the alpha value of the colour object by 32 each step. We then use S olidBrush objects (more of which in section 10) to provide the fill colour and transparency for the rectangle, ellipse and text.
Transformations, you may remember from your mathematics schooling, are achieved via different forms of matrices that allow you to perform transformations include rotating, scaling, reflecting, shearing, and translation. In the example we shall use matrices to rotate and translate a simple shape encapsulated in a GDI+ GraphicsPath object.
The Matrix class allows representation of transformations that may then be applied to the coordinates of your object via their specification as a compatible matrix. I'm not going to enter into significant detail regarding the mathematical foundations of matrices, so if you’re rusty and interested I suggest you locate an appropriate resource (or see article references). The Matrix class thankfully shields the programmer from most of the intricacies.
The GraphicsPath class allows you to draw complex shapes using curves, lines, rectangles, ellipses, text, and so on. The resultant GraphicsPath object can be treated as a unit that can then be translated or rotated it using matrix transformations.
The 'complex' shape we shall store using a GraphicsPath object is a circle with a curve attached to it represented in GDI+ by an ellipse object and a bezier curve object, as follows:

This image shall be duplicated and transformed by rotation and translation to produce:

This is achieved by the following code:
Gdi9.aspx code snippet:
|
dim myBitmap as New Bitmap(820,300) dim myGraphic = Graphics.FromImage(myBitmap) dim offsetX as integer = 100 dim myGraphicsPath as GraphicsPath = new GraphicsPath() 'add a Bexier curve myGraphicsPath.AddBezier(110, 70, 130, 100, 90, 130, 110, 160) 'add an ellipse myGraphicsPath.AddEllipse(100,50, 20,20) 'save a copy of the graphics path so we can reset it after each set of transforms dim myGraphicsPathOld as GraphicsPath = new GraphicsPath() myGraphicsPathOld = CType(myGraphicsPath.Clone(), GraphicsPath) 'setup the matrices and related info 'rotation matrix dim RotationTransform as Matrix = new Matrix(1, 0, 0, 1, 0, 0) 'translation matrix dim TranslationTransform as Matrix = new Matrix(1, 0, 0, 1, 0, 0) 'rotation point dim rotationPoint as PointF = new PointF(110.0f, 160.0f) 'cycle through 5 translated patterns dim i as integer dim angle as single for i = 0 to 6 'cycle through 360 degrees and draw a balloon at each 45 degree steps for angle = 0 to 360 step 45 'use the translation matrix to translate the graphics path 'to the right myGraphicsPath.Transform(TranslationTransform) 'rotate f degrees around the rotationPoint RotationTransform.RotateAt(angle, rotationPoint) 'call the Transform method of the Graphics Path in order to 'multiply it by the rotation matrix and rotate the object the specified amount myGraphicsPath.Transform(RotationTransform) 'fill the GraphicsPath object with a red coloured brush myGraphic.FillPath(Brushes.Red, myGraphicsPath) 'outline the object with a white pen myGraphic.DrawPath(Pens.White, myGraphicsPath) 'reset the transformation matrix RotationTransform.Dispose() RotationTransform = new Matrix(1, 0, 0, 1, 0, 0) 'get the original graphicspath for use as our rotation and translation reference object myGraphicsPath = CType(myGraphicsPathOld.Clone(), GraphicsPath) next 'move the translation matrix by an offset. When the matrix is then 'used in the transform method(above), it causes the object to 'be drawn to the right (translation in the positive X direction) TranslationTransform.Translate(offsetX, 0) 'We also need to translate the point that we rotate around rotationPoint.X = rotationPoint.X + offsetX next |
As before we instantiate a graphics object, for use as a workspace, from a bitmap.
Next we construct our GraphicsPath object via the AddEllipse and AddBezier methods:
myGraphicsPath.AddBezier(110, 70, 130, 100, 90, 130, 110, 160)
myGraphicsPath.AddEllipse(100,50, 20,20)
The overloaded AddEllipse method of the GraphicsPath object we shall use has the following signature:
Overloads Public Sub AddEllipse( _
ByVal x As Integer, _
ByVal y As Integer, _
ByVal width As Integer, _
ByVal height As Integer _
)
where
x is the x-coordinate of the upper-left corner of the bounding rectangle that defines the ellipse.
y is the y-coordinate of the upper-left corner of the bounding rectangle that defines the ellipse.
width is the width of the bounding rectangle that defines the ellipse.
height is the height of the bounding rectangle that defines the ellipse.
The AddBezier method of the GraphicsPath object we shall use has the following signature:
Overloads Public Sub AddBezier( _
ByVal pt1 As Point, _
ByVal pt2 As Point, _
ByVal pt3 As Point, _
ByVal pt4 As Point _
)
where
pt1 is a Point structure that represents the starting point of the curve.
pt2 is a Point structure that represents the first control point for the curve.
pt3 is a Point structure that represents the second control point for the curve.
pt4 is a Point structure that represents the endpoint of the curve.
We then proceed with two subsets of transformations to produce the resultant effect after we have taken a copy of the GraphicPath object for later re-use. First we setup the transformations as well as the rotation point for the rotational transformation:
dim RotationTransform as Matrix = new Matrix(1, 0, 0, 1, 0, 0)
dim TranslationTransform as Matrix = new Matrix(1, 0, 0, 1, 0, 0)
dim rotationPoint as PointF = new PointF(110.0f, 160.0f)
With the matrices initially set up as identity matrices, i.e. they do nothing: matrix multiplication against a coordinate appropriately represented as a matrix will result in the same coordinate matrix.
Within the for ... next loops, which simply repeat the transformations to produce the pattern based on a copy of the original GraphicsPath object, the code configures and applies the transformations.
As far as the translation transformation is concerned for the first loop the translation matrix is the identity matrix so
myGraphicsPath.Transform(TranslationTransform)
does nothing. However, on subsequent iterations the TranslationTransform has been updated with the value of offsetX to the x axis:
TranslationTransform.Translate(offsetX, 0)
And the appropriate transformation is performed.
Turning to the rotation, the number of degrees to rotate the original object is specified by angle which is augmented by 45 degrees each iteration until a full circle has been navigated. The angle and point about which to rotate are specified to the RotationTransform matrix via the RotateAt method of the matrix class:
RotationTransform.RotateAt(angle, rotationPoint)
And apply the transformation to the GraphicsPath object:
myGraphicsPath.Transform(RotationTransform)
We finish off by filling and outlining the object before resetting the objects as well as incrementing properties ready for the next iteration.
Note that we could have combined both rotation and translation into one matrix but the implementation presented is via two matrices for clarity.
That concludes article IV. In the fifth 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.